Fundamental Soot objects
Soot的类继承结构非常庞大和复杂,本节为读者介绍一些在Soot开发中最重要的类。
我们会详细介绍如下几个类的概念Body
, Unit
, Local
, Value
, UnitBox
和 ValueBox
Body
在教程Creating a class from scratch 中已经提到过Body
这个概念。下面我们会更加详细的阐述Body
特性。
Soot使用Body
存储方法的代码。在Soot中有四种类型的Body
,每种Body
都是一种中间表示(IR)
- BafBody
- JimpleBody
- ShimpleBody
- GrimpBody
在Body
中三个最主要的链就是
Unit
链Local
链Trap
链。
下面的例子将演示每个链的具体规则:
看一下下面的Java方法
1 | public static void main(String[] argv) throws Exception |
经过Jimplification
之后,我们得到了经过简化的jimple
代码
1 | public static void main(java.lang.String[]) throws java.lang.Exception |
Local variables
该方法体的本地变量如下
1 | java.lang.String[] r0; |
本地变量存储在localChain
里,可以通过body.getLocals()
方法访问这些本地变量。每一种中间表示都可以用自己的方式实现本地变量。但是,不管如何实现,对于每一个变量Local r0
都要能正确调用r0.getName()
, r0.getType()
, r0.setName()
和 r0.setType()
。注意本地变量是必须包含类型的。
Traps
为了支持Java的异常处理,Soot定义了traps概念。在Java的字节码中处理异常是通过一个元组(tuple)表示的(exception
, start
, stop
, handler
). 在start和stop之间([start, stop)
)的字节码中,如果有异常抛出来,后面的handler将处理该异常
下面的trap就对应一个异常处理
1 | catch java.lang.Exception from label0 to label1 with label2; |
Units
Body
中最有趣的部分就是它的Unit
链了。这是Body
中真正的代码部分。Jimple
提供了Unit
的Stmt
实现,而Gimp
提供了Unit
的Inst
实现。这说明,每一个IR都有它自己独有的语句概念。
Jimple 一个简单的Stmt
例子就是AssignStmt
, 这是一个Jimple的赋值语句。例如
1 | x = y + z; |
Value
代码总是基于数据运行。为了表达数据,Soot提供了Value
接口。下面是一些不同的Value
实现
Local
sConstant
sExpressions
(Expr
)ParameterRefs
,CaughtExceptionRefs
和ThisRefs
.
Expr
接口有一整套的实现,例如 NewExpr
和 AddExpr
。一般来说,Expr
接受一个或者多个Value
然后产出一个Value
。
下面我们看一个真实的Value
示例
1 | x = y + 2; |
这是一个 AssignStmt
, 做操作符是x
, 右操作符是y + 2
(这是一个AddExpr
). AddExpr
包含俩个操作数y
和2
, y
是一个变量Local
而2
是一个常量Constant
.
在Jimple中,我们强制要求,所有的Value
最多只能包含一个表达式。Grimp取消了这个限制,从而可以产生更加易读但是更难分析的代码。
Boxes
Boxes 在Soot中是无处不在的。要时刻记住Box 就是一个指针
。它提供了直接访问Soot对象的方式。
其实更能表达指针含义的名字是Ref
而不是Box
,可是Ref
在Soot中已经被赋予了其他含义,因此我们只能用Box
这个名字了。
在Soot中有俩种Box
ValueBox
UnitBox
通过名字也能很清晰地看出,ValueBox
指向的是Value
而UnitBox
指向的是Unit
,这很类似于c++中的Unit *
和Value *
UnitBox
某些Unit
需要包含一些指向其他Unit的引用。例如GotoStmt
需要知道它的目标是什么。因此Soot提供了UnitBox
。
例如下面的jimple代码
1 | x = 5; |
每一个Unit
都必须提供getUnitBoxes()
方法。对于大多数UnitBox
返回的都是一个空列表。但是在GotoStmt
中,getUnitBoxes()
返回了一个单元素列表,它返回的是一个指向了l2
的Box
。
注意,SwitchStmt
通常会返回一个多元素的Box
列表。
Box
在修改代码时非常有用。例如我们有如下一个语句
1 | s: goto l2; |
还有一个l2语句
1 | l2: goto l3; |
我们可以很清楚的看到,不管s是什么类型,s都会指向l3,而不是l2. 对于这种情况, 我们可以将所有的Unit
统一修改
1 | public void readjustJumps(Unit s, Unit oldU, Unit newU) |
Some code similar to this is used in Unit itself, to enable the creation of PatchingChain, an implementation of Chain which adjusts pointers to Units which get removed from the Chain.
ValueBox
与Unit
情况类似,我们需要一种指向Value
的指针,ValueBox
.
我们可以用ValueBox
做常量展开,例如AssignStmt
会运算一个AddExpr
加法表达式将俩个常量加到一起,我们可以静态地将它们加到一起,然后再将结果放到UseBox
.
下面是AddExpr
的一种展开
1 | public void foldAdds(Unit u) |
注意,这段代码可以忽略类型,基于任何Unit都可以正常工作。
Unit revisited
现在我们看看Unit
还必须提供哪些方法
1 | public List getUseBoxes(); |
上面这些方法会返回当前Unit
使用到或者定义的ValueBox
列表。getUseBoxes()
会返回所有使用到的value,其中包含了构成它们的表达式。
1 | public List getUnitBoxes(); |
这个方法返回UnitBox
列表, 指向当前方法的所有的unit(方法调用图?).
1 | public List getBoxesPointingToThis(); |
这个方法返回UnitBox
列表, 包含了所有目标是当前unit的Unit列表。
1 | public boolean fallsThrough(); |
These methods have to do with the flow of execution after this Unit. The former method returns true if execution can continue to the following Unit, while the latter returns true if execution might continue to some Unit which isn’t immediately after this one.
1 | public void redirectJumpsToThisTo(Unit newLocation); |
This method uses getBoxesPointingToThis to change all jumps to this Unit, pointing them instead at newLocation.