Soot:Java代码优化框架

举报
Jet Ding 发表于 2020/09/28 16:32:47 2020/09/28
【摘要】 Soot是一个Java优化框架。它提供了四种中间表示法,用于分析和转换Java字节码。

1   引言 

软件教练郭恒提了一个关于Soot的问题,这是一个Java代码优化框架,我们这篇文章就来看看Soot

2   Soot

Soot是一个Java优化框架。它提供了四种中间表示法,用于分析和转换Java字节码。

 

1.   Baf字节码的精简表示法,操作简单。

2.   Jimple:一种适合优化的类型化3地址中间表示法

3.   ShimpleJimpleSSA变体。

4.   GrimpJimple的聚合版本,适合反编译和代码检查。 

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=1i<=100i++)

            fb.printFizzBuzz(i);

    }

}

 

使用Soot, 我们可以这样:

 

SootClass mainClass = Scene.v().getSootClass("FizzBuzz");

SootMethod sm = mainClass.getMethodByName("printFizzBuzz");

JimpleBody body = (JimpleBody) sm.retrieveActiveBody();

 

Soot提供了几种Java程序的中间表示法(IR),以使静态分析更加方便Soot默认的IRJimple(Java Simple),介于JavaJava字节码之间。Java语言比较适合人类程序员,因为人类程序员很容易读懂,而Java字节码则适合机器Jimple是一种基于语句的、类型化的(每个变量都有一个类型)和3个地址(每个语句最多有3个变量)的中间表示法

下面的代码是JimpleprintFizzBuzz方法的表示:

 

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,因为默认的IRJimple)。在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 控制流程图

 

分支语句控制语句的执行流程。一个方法中所有可能执行的路径都被表示为控制流图(CFGSoot能够通过一个叫做UnitGraph的接口来创建方法的CFG。下图是 printFizzBuzz 方法的 CFG:

2.4.5 Soot命令行选项 

java javaOptions soot.Main [ sootOption* ] classname*

Soot选项的描述中谈到了三类:参数类、应用类和库类

参数类是你明确地指定给Soot的那些类。当你使用Soot的命令行界面时,参数类是指那些在命令行中明确列出的类,或者是在用-process-dir选项指定的目录中找到的类。当你使用SootEclipse插件时,参数类是那些你在启动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是唯一的参数类,也是唯一的应用类UITextUI是库类,还有java.lang.Systemjava.lang.Stringjava.io.PrintStream,以及其他一系列来自Java运行时系统的类,这些类通过对StringSystem.out的引用被间接引入。 

如果你运行

java soot.Main --app HelloWorld

HelloWorld仍然是唯一的参数类,但应用类包括UITextUI以及HelloWorldjava.lang.System等仍然是库类。

如果你运行

java soot.Main -i java. --app HelloWorld

HelloWorld仍然是唯一的参数类,但应用类的集合包括了Java运行时中名字以java.开头的包中的引用类,以及HelloWorldUItextUI。库类的集合包括Java运行时中其他包中的引用类。 

命令选项参见如下链接:

https://www.sable.mcgill.ca/soot/tutorial/usage/

https://soot-build.cs.uni-paderborn.de/public/origin/develop/soot/soot-develop/options/soot_options.htm

 

2.4.6 目前支持的功能

1.   自动模块(从模块路径中的jar中自动创建的模块)。

2.  已命名的模块

3.  Exploded模块

4.  模块化jar文件

5.  SootModuleScene中解决模块问题

6.  Spark

 

2.4.7 还不支持的功能

1.   匿名模块(混合模块和类路径)

2.   多模块jar文件 

2.5   业界状态

 

在静态程序分析中,Soot是一个由Java的中间语言组成的字节码操作优化框架。它是由麦吉尔大学的Sable研究小组开发的。Soot提供了四种中间表示法,通过它的API供其他分析程序访问和构建:

目前发布的Soot软件还包含了可以开箱即用的详细程序分析,如上下文敏感的流不敏感的zh指针分析、调用图分析和支配分析(如回答 "事件a必须跟随事件b吗?")。它还有一个名为dava的反编译器。

SootGNU Lesser General Public LicenseLGPL)下的自由软件。2010年,关于Soot的两篇研究论文(Vallée-Rai1999年和Pominville2000年)在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

https://o2lab.github.io/710/p/a1.html

https://www.sable.mcgill.ca/soot/tutorial/usage/

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。