静态程序分析chapter2 - IR(Jimple) 和 CFG
文章目錄
- 一、
- IR(Intermediate Representation)
- 編譯器(Compiler)
- 3AC(3-Address Code)
- Soot和它的IR:Jimple
- JVM中的方法調用與方法簽名簡介
- invokespecial
- invokevirtual
- invokeinterface
- invokestatic
- invokedynamic
- method signature
- CFG(Control Flow Graph)的建立
- 建立節點Node
- 建立邊Edge
- 總結
一、
IR(Intermediate Representation)
編譯器(Compiler)
??????編譯器編譯一般要經過5個階段:詞法分析;語法分析;語義分析;優化;目標代碼生成。下面通過自然語言處理來說明這些步驟(處理代碼也是類似的)。
??????詞法分析(Lexical Analysis)通過正則表達式來判斷每一個單詞本身是否存在錯誤。如果每一個單詞都是正確的,就會給它們做上標記(Token),交給下一個步驟。
??????語法分析(Syntax Analysis)通過上下文無關語法來判斷每一個語句是否符合英文語法(主謂賓…)。如果語句的語法沒有問題,就會將其轉化為抽象語法樹(AST)。
??????語義分析(Semantic Analysis)通過屬性語法來判斷每一個語句的語義上是否存在問題,比如語句 Apples eat you 從語義上來說是行不通的。當然編譯器在這個步驟的工作是比較簡單的,它只對語句做類型檢查,比如不能將一個 float 類型變量賦值給 char 類型變量。如果語句的語義沒有問題,就會生成修飾過后的抽象語法樹。
??????優化(optimization)步驟是通過轉換器轉換為一種中間表示形式(IR),優化的結果就是生成了IR。通常所說的IR就是三地址碼(3AC)。
??????最終的目標代碼(.obj)通過IR來生成。
??????而靜態分析通常是在IR上進行的,雖然最早也可以在AST上進行,但是現在已經被淘汰了。IR的優點在于:它是語言無關的并且包含有控制流信息,通常認為IR是靜態分析的基石。見下面圖示:
3AC(3-Address Code)
??????三地址碼的格式:在一個語句的右側只有一個操作符。比如語句 a = b + c + 2;它轉換為三地址碼的格式;t1 = b + c;a = t1 + 2;其中的 t1 是臨時變量。
??????其實三地址碼就是包含有三個地址的語句。這里的地址指的是:常量(2)、變量名(b or c)以及臨時變量(t1)。
??????常見的三地址碼格式:(x,y 和 z 都是地址)
????????????x = y bop z
????????????x = uop z
????????????x=y
????????????goto L
????????????if x goto L
????????????if x rop y goto L
??????bop 指的是二元算術運算符(+ , - , * , / , …);uop 指的是一元算術運算符(- ,casting …);rop指的是二元關系判斷運算符(> , < , == …)。
Soot和它的IR:Jimple
??????Jimple是一種典型的三地址碼。下面給出一些簡單的Java代碼和對應的Jimple代碼作為示例:(關于Soot,后面會專門講解)
一、
??????Java Src(do-while 循環):
??????其中main函數所對應的Jimple代碼為:
public static void main(java.lang.String[]) {java.lang.String[] r0;int[] r1;int $i0,i1;r0 := @parameter0: java.lang.String[];r1 = newarray (int)[10];i1 = 0;label1:i1 = i1 + 1;$i0 = r1[i1];if $i0 < 10 goto label1;return; }二、
??????Java Src(方法調用):
??????其中 foo 函數和 main 函數對應的Jimple代碼為:
java.lang.String foo(java.lang.String,java.lang.String) {MethodCall3AC r0;java.lang.String r1, r2, $r7;java.lang.StringBuilder $r3,$r4,$r5,$r6;r0 := @this: MethodCall3AC;r1 := @parameterr0: java.lang.String;r2 := @parameterr1: java.lang.String;$r3 = new java.lang.StringBulider;specialinvoke $r3.<java.lang.StringBulider: void<init>()>();$r4 = virtualinvoke $r3.<java.lang.StringBulider: java.lang.StringBulider append(java.lang.String)>(r1);$r5 = virtualinvoke $r4.<java.lang.StringBulider: java.lang.StringBulider append(java.lang.String)>(" ");$r6 = virtualinvoke $r5.<java.lang.StringBulider: java.lang.StringBulider append(java.lang.String)>(r2);$r7 = virtualinvoke $r6.<java.lang.StringBulider: java.lang.String toString()>();return $r7; }public static void main(java.lang.String[]) {java.lang.String[] r0;MethodCall3AC $r1;r0:= @parameter0: java.lang.String[];$r1= new MethodCall3AC;specialinvoke $r1.<MethodCall3AC: void<init>()>();virtualinvoke $r1.<MethodCall3AC: java.lang.String foo(java.lang.Stirng,java.lang.String)>("hello","world");return; }三、
??????Java Src(類):
??????這個類對應的Jimple代碼為:
public class a.b.c.Class3AC extends java.lang.Object {public static final double pi;public void <init>(){a.b.c.Class3AC r0;r0 := @this: a.b.c.Class3AC;specialinvoke r0.<java.lang.Object: void <init>()>();return;}public static void main(java.lang.String[]){java.lang.String[] r0;r0:= @parameter0: java.lang.String[];return;}public static void <clinit>(){<a.b.c.Class3AC: double pi> = 3.14;return;} }JVM中的方法調用與方法簽名簡介
invokespecial
??????用來調用構造函數,父類方法和私有方法(constructor、superclass method、private method),構造函數的的方法名是 。
invokevirtual
??????用來調用實例方法、基于類進行分派(instance method、virtual dispatch)
invokeinterface
??????用來調用接口中的方法
invokestatic
??????用來調用靜態方法
invokedynamic
??????Java7 添加,調用方法讓其動態執行
method signature
??????方法簽名的格式為:<類名: 返回值 方法名(參數1的類型,參數2的類型…)>(傳入的實參1,傳入的實參2…)
CFG(Control Flow Graph)的建立
??????下面講述采用3AC-IR建立控制流程圖的過程。控制流程圖是靜態分析的最基本的結構,控制流程圖是由節點和邊組成的,節點通常是一個獨立的3AC語句,或者是一個基本塊BB(Basic Block);邊用來表示控制流的方向。
建立節點Node
??????BB是滿足下面特點的最大的連續語句系列。第一,BB只能擁有一個入口。第二,BB只能擁有一個出口。根據這兩個特點,對于連續的3AC語句,將它們轉換為一系列BB的方法為:
??????每一個入口語句只能為:在所有語句中的第一條語句;任何跳轉語句的目的地址所在語句;任何跳轉語句的下一條語句。
??????當我們找到所有入口語句之后,就可以輕易地構建BB了。
??????下圖中左側為輸入語句,右側是根據上面方法構建的BB:
??????每一個BB中的第一條語句稱為 leaders,先找到所有的 leaders:(1)是第一條語句;(3)(7)(12)是跳轉語句的目標語句;(5)(11)(12)是跳轉語句的下一條語句。
??????最后,得到的BB如下圖所示:
建立邊Edge
??????1,如果一個基本塊 A 的最后一條語句為跳轉語句,并且該跳轉語句指向另一個基本塊 B 的第一條語句,那么,存在一條邊從基本塊 A 指向基本塊 B 。
??????2,如果一個基本塊 A 的最后一條語句不是無條件跳轉語句,而 B 是緊接著 A 的下一個基本塊,那么,存在一條邊從基本塊 A 指向基本塊 B 。
??????稱 A 為 B 的前驅,B 為 A 的后繼。
??????通常,我們需要再添加兩個節點,入口節點和出口節點。類似于數據結構鏈表的鏈表頭,它們并不包含任何語句,僅用來標記CFG的入口和出口。入口節點指向的BB中包含有整個代碼執行中的第一條IR語句;指向出口節點的BB中包含可能是整個代碼執行的最后一條IR語句。
??????在上面的BB中按照既有規則加上邊之后,得到的 CFG 如下圖:
總結
??????靜態分析的原材料是 3AC-IR ,文中列出了常用的六種 3AC-IR 的格式,為了更好的理解,我們介紹了 Soot 是如何將 Java 源代碼轉換為 IR Jimple 的,并且補充了JVM中的方法調用。最后一部分介紹了在 IR 的基礎上建立 CFG ,包括邊和節點的建造,后面會繼續介紹如何在 CFG 上進行數據流分析和三個典型案例講解。未完待續~
總結
以上是生活随笔為你收集整理的静态程序分析chapter2 - IR(Jimple) 和 CFG的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 静态程序分析chapter1 - 概述和
- 下一篇: 静态程序分析chapter3 - 数据流