Soot:Java代码优化框架
1 引言
软件教练郭恒提了一个关于Soot的问题,这是一个Java代码优化框架,我们这篇文章就来看看Soot。
2 Soot
Soot是一个Java优化框架。它提供了四种中间表示法,用于分析和转换Java字节码。
1. Baf:字节码的精简表示法,操作简单。
2. Jimple:一种适合优化的类型化3地址中间表示法。
3. Shimple:Jimple的SSA变体。
4. Grimp:Jimple的聚合版本,适合反编译和代码检查。
2.1 许可
LGPL-2.1
2.2 官方网站
https://github.com/soot-oss/soot
2.3 使用方法
2.3.1 引入Soot
<dependencies>
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>soot</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
2.3.2 配置示例
分别针对Java 8, Java >= 9 Classpath, Java >= 9 Modulepath。
if(java < 9 ) {
Options.v().set_prepend_classpath(true);
Options.v().set_process_dir(Arrays.asList(applicationClassPath().split(File.pathSeparator)));
Options.v().set_claspath(sootClassPath();
}
if(java >= 9 && USE_CLASSPATH){
Options.v().set_soot_classpath("VIRTUAL_FS_FOR_JDK" + File.pathSeparator + sootClassPath());
Options.v().set_process_dir(Arrays.asList(applicationClassPath().split(File.pathSeparator)));
}
if(java>=9 && USE_MODULEPATH){
Options.v().set_prepend_classpath(true);
Options.v().set_soot_modulepath(ootClassPath());
Options.v().set_process_dir(Arrays.asList(applicationClassPath().split(File.pathSeparator)));
}
2.4 工作原理
现在我们来看看他是如何工作的。
2.4.1 Soot的基础类
Soot的基础类
Scene是一个单实例类,它用以调用其他所有的类,这些类由SootClass表示。每个SootClass可能包含几个方法(SootMethod),每个方法可能有一个Body对象,用来保存语句(Units)。在设置好Soot之后,我们可以通过Soot API来访问这些对象。
2.4.2 Jimple
Jimple是一个Java程序的中间表示法,它的设计比Java字节码更容易优化。它是类型化的,有一个具体的语法,并基于三地址代码。
Jimple只包括15种不同的操作,因此简化了流程分析。相比之下,java字节码包括了200多种不同的操作。
与java字节码不同,在Jimple中,局部变量和堆栈变量都是类型化的,Jimple本身就是类型安全的。
转换到Jimple,就是将字节码转换为三地址代码。这种转换背后的思想,首先由Clark Verbrugge研究,是将一个变量关联到堆栈的每个位置。因此,堆栈操作成为涉及堆栈变量的赋值。
2.4.2.1 例子
考虑以下字节码:
iload 1 //加载变量x1,并将其推到堆栈上。
iload 2 //加载变量x2,并将其推到堆栈上。
iadd // 弹出两个值,并将它们的总和推到堆栈上。
istore 1 //从堆栈中弹出一个值,并将其存储在变量x1中。
翻译成以下三个地址代码:
stack1 = x1 // iload 1
stack2 = x2 // iload 2
stack1 = stack1 + stack2 // iadd
x1 = stack1 // istore 1
一般情况下,所产生的代码不具有静态的单一赋值形式。
2.4.3 Soot实例演示
比如我们有这样的类:
public class FizzBuzz {
public void printFizzBuzz(int k){
if (k%15==0)
System.out.println("FizzBuzz");
else if (k%5==0)
System.out.println("Buzz");
else if (k%3==0)
System.out.println("Fizz");
else
System.out.println(k);
}
public static void main(String[] args){
FizzBuzz fb = new FizzBuzz();
for (int i=1; i<=100; i++)
fb.printFizzBuzz(i);
}
}
使用Soot, 我们可以这样:
SootClass mainClass = Scene.v().getSootClass("FizzBuzz");
SootMethod sm = mainClass.getMethodByName("printFizzBuzz");
JimpleBody body = (JimpleBody) sm.retrieveActiveBody();
Soot提供了几种Java程序的中间表示法(IR),以使静态分析更加方便。Soot默认的IR是Jimple(Java Simple),介于Java和Java字节码之间。Java语言比较适合人类程序员,因为人类程序员很容易读懂,而Java字节码则适合机器。Jimple是一种基于语句的、类型化的(每个变量都有一个类型)和3个地址(每个语句最多有3个变量)的中间表示法。
下面的代码是Jimple中printFizzBuzz方法的表示:
r0 := @this: FizzBuzz
i0 := @parameter0: int
$i1 = i0 % 15
if $i1 != 0 goto $i2 = i0 % 5
$r4 = <java.lang.System: java.io.PrintStream out>
virtualinvoke $r4.<java.io.PrintStream: void println(java.lang.String)>("FizzBuzz")
goto [?= return]
$i2 = i0 % 5
if $i2 != 0 goto $i3 = i0 % 3
$r3 = <java.lang.System: java.io.PrintStream out>
virtualinvoke $r3.<java.io.PrintStream: void println(java.lang.String)>("Buzz")
goto [?= return]
$i3 = i0 % 3
if $i3 != 0 goto $r1 = <java.lang.System: java.io.PrintStream out>
$r2 = <java.lang.System: java.io.PrintStream out>
virtualinvoke $r2.<java.io.PrintStream: void println(java.lang.String)>("Fizz")
goto [?= return]
$r1 = <java.lang.System: java.io.PrintStream out>
virtualinvoke $r1.<java.io.PrintStream: void println(int)>(i0)
return
在Jimple中没有什么隐含的东西。例如,this表示为r0,它是一个Local对象(Soot中变量的数据结构)。或者函数的参数显式定义为i0,其类型为int。每一行代表一个Unit(或Stmt,因为默认的IR是Jimple)。在Jimple中,有15种不同类型的Stmt,但在BranchDetectorAnalysis中,我们只对其中一种感兴趣:JIfStmt。下面是打印分支语句的代码。
for(Unit u : body.getUnits()){
if (u instanceof JIfStmt)
System.out.println(u.toString());
}
body.getUnits()返回printFizzBuzz body中的Unit列表(或者更准确的说是Chain)。我们简单地遍历这些单元,并打印其中任何一个是JIfStmt的子类。
2.4.4 控制流程图
分支语句控制语句的执行流程。一个方法中所有可能执行的路径都被表示为控制流图(CFG)。Soot能够通过一个叫做UnitGraph的接口来创建方法的CFG。下图是 printFizzBuzz 方法的 CFG:
2.4.5 Soot命令行选项
java javaOptions soot.Main [ sootOption* ] classname*
Soot选项的描述中谈到了三种类:参数类、应用类和库类。
参数类是你明确地指定给Soot的那些类。当你使用Soot的命令行界面时,参数类是指那些在命令行中明确列出的类,或者是在用-process-dir选项指定的目录中找到的类。当你使用Soot的Eclipse插件时,参数类是那些你在启动Soot之前从Navigator弹出菜单中选择的类,如果你从Project菜单启动Soot,则是当前项目中的所有类。
应用类是指 Soot 分析、转换并转化为输出文件的类。
库类是直接或间接被应用类引用的类,但它们本身不是应用类。Soot解析这些类,并为它们读取.class或.jimple源文件,但它不会对库类进行转换,也不会为它们编写输出文件。
所有的参数类都必然是应用类。当Soot不处于 "应用模式 "时,参数类是唯一的应用类;从参数类引用的其他类成为库类。
当Soot处于应用模式时,从参数类直接或间接引用的每个类也是应用类,除非它的包名表明它是标准Java运行时系统的一部分。
用户可以使用应用模式选项对应用类和库类的指定进行微调。
下面是一个简单的例子来说明问题。假设你的程序由以下来源生成的三个类文件组成。
// UI.java
interface UI {
public void display(String msg);
}
// HelloWorld.java
class HelloWorld {
public static void main(String[] arg) {
UI ui = new TextUI();
ui.display("Hello World");
}
}
// TextUI.java
import java.io.*;
class TextUI implements UI {
public void display(String msg) {
System.out.println(msg);
}
}
如果你运行
java soot.Main HelloWorld
HelloWorld是唯一的参数类,也是唯一的应用类。UI和TextUI是库类,还有java.lang.System,java.lang.String,java.io.PrintStream,以及其他一系列来自Java运行时系统的类,这些类通过对String和System.out的引用被间接引入。
如果你运行
java soot.Main --app HelloWorld
HelloWorld仍然是唯一的参数类,但应用类包括UI和TextUI以及HelloWorld。java.lang.System等仍然是库类。
如果你运行
java soot.Main -i java. --app HelloWorld
HelloWorld仍然是唯一的参数类,但应用类的集合包括了Java运行时中名字以java.开头的包中的引用类,以及HelloWorld、UI和textUI。库类的集合包括Java运行时中其他包中的引用类。
命令选项参见如下链接:
https://www.sable.mcgill.ca/soot/tutorial/usage/
2.4.6 目前支持的功能
1. 自动模块(从模块路径中的jar中自动创建的模块)。
2. 已命名的模块
3. Exploded模块
4. 模块化jar文件
5. 在Soot的ModuleScene中解决模块问题
6. Spark
2.4.7 还不支持的功能
1. 匿名模块(混合模块和类路径)
2. 多模块jar文件
2.5 业界状态
在静态程序分析中,Soot是一个由Java的中间语言组成的字节码操作和优化框架。它是由麦吉尔大学的Sable研究小组开发的。Soot提供了四种中间表示法,通过它的API供其他分析程序访问和构建:
目前发布的Soot软件还包含了可以开箱即用的详细程序分析,如上下文敏感的流不敏感的zh指针分析、调用图分析和支配分析(如回答 "事件a必须跟随事件b吗?")。它还有一个名为dava的反编译器。
Soot是GNU Lesser General Public License(LGPL)下的自由软件。2010年,关于Soot的两篇研究论文(Vallée-Rai等1999年和Pominville等2000年)在425篇参赛论文中的12篇中被选为IBM CASCON第一个十年高影响力论文。
3 参考
https://en.wikipedia.org/wiki/Soot_(software)
https://github.com/soot-oss/soot
https://www.sable.mcgill.ca/soot/
https://medium.com/@noidsirius/a-beginners-guide-to-static-program-analysis-using-soot-5aee14a878d
https://medium.com/@noidsirius/know-the-basic-tools-in-soot-18f394318a9c
http://www.iro.umontreal.ca/~dufour/cours/ift6315/docs/soot-tutorial.pdf
https://cs.au.dk/~amoeller/mis/soot.pdf
https://www.bodden.de/pubs/lblh11soot.pdf
https://www.sable.mcgill.ca/soot/doc/soot/coffi/CFG.html
https://github.com/soot-oss/soot/wiki/Introduction:-Soot-as-a-command-line-tool
- 点赞
- 收藏
- 关注作者
评论(0)