Soot:从头创建一个类

来源:互联网 发布:欧莱雅 曼秀雷敦 知乎 编辑:程序博客网 时间:2024/05/16 05:26

Soot:从头创建一个类

(Creating a class from scratch)

首先,我们需要创建一个类,以放入方法。接下来的步骤对于创建一个类文件是必要的。

加载Java.lang.Object和Library类

    加载java.lang.Object,Java类层次结构的root节点。

    该步骤对于那些扩展Soot框架的构建代码来说,是没必要的。在那种情况下,当用户代码被调用时,类文件就已经被加载了。

Scene.v().loadClassAndSupport("java.lang.Object");

    该行代码使得Soot去加载java.lang.Object类,并创建其对应的SootClass对象,也包括与其成员对应的SootMethod(s)和SootField(s)。当然java.lang.Object有着对其它对象的引用。loadClassAndSupport方法的调用,将加载指定类的传递闭包(transitive closure),以便所有为了加载java.lang.Object所需的类型能被加载。

这一过程被称为resolution。

因为我们的HelloWorld程序将使用标准库中的类,我们也需要解析(resolve)它们:

Scene.v().loadClassAndSupport("java.lang.System");

该行引用了Scene.v()。Scene是指在程序中所有SootClass(es)的容器,并且提供了各种实用的方法。有一个单例的Scene对象,可以通过Scene.v()来调用获取。

注意事项:Soot从类文件或.jimple的输入文件中加载这些类。当使用的是前者的话,Soot将加载所有在每个类文件的常量池中获取到的类名。从Jimple中加载时,Soot将只加载要求的类型。

创建一个新的SootClass对象

    创建一个"HelloWorld"类,并设置它的超类为"java.lang.Object"。

SootClass sClass = new SootClass("HelloWorld",Modifier.PUBLIC);

该行代码为一个名为HelloWorld的Public类创建SootClass对象。

sClass.setSuperClass(Scene.v().getSootClass("java.lang.Object"));

这将为这个新创建的类设置相应于java.lang.Object的SootClass对象,作为其超类。注意这里Scene的getSootClass功能方法的使用。

Scene.v().addClass(sClass);

这将添加新创建的HelloWorld类Scene中。所有的类,一旦它创建后,都应该归属到Scene中。

添加方法到SootClass中

为HelloWorld创建一个含有空方法体的main()方法。

现在,我们拥有一个SootClass,我们需要为其添加方法。

SootMethod method = new SootMethod("main",

Arrays.asList(new Type[]{ArrayType.v(RefType.v("java.lang.String"),1)}),

VoidType.v(), Modifier.Public | Modifier.STATIC);

我们创建了一个public static方法,main,声明了它携带了一个java.lang.String的对象,并且它返回void。

SootMethod的构造器要求一个list,所以我们使用Java实用方法Arrays.asList来从单元素数组(我们从newType[]中生成)中创建一个list。在该列表中,我们装入一个数组类型,对应于java.lang.String对象的一维数组类型。

Types: 每个SootClass表示一个Java对象。我们可以实例化该类,通过指定一个类的给定类型。两个概念:--type和class--密切相关,但带有区分。为了获取java.lang.Stirng类的type,通过名字,我们调用RefType.v("java.lang.String")。给定一个SootClass对象sc,我们也可以调用sc.getType()来获取对应的类型。

sClass.addMethod(method);

这一代码将方法添加到包含它的类中。

为方法添加代码

    方法如果不包含代码则没有意义。我们为main方法添加一些代码。为了达到该目的,我们需要为该代码选择一种中间表示。

创建JimpleBody

    在Soot中,我们将Body附着到一个SootMethod方法以关联该方法的一些代码。每个Body知道它对应的SootMethod,但一个SootMethod在一个时刻只有有一个active的Body(通过SootMethod.getActiveBody())。各种中间表示提供了不同的Body的类型,Soot有JimpleBody,ShimpleBody,BafBody和GrimpleBody。

    更确切的讲,Body有3种重要的特征:Locals,Traps和Units的Chain。Chain是一种类似list结构的,提供O(1)的插入和删除元素访问。Locals指的是Body的本地变量。Traps指的是哪些语句捕获了哪些异常。而Units指的是这些语句本身。

    注意,Unit是这么个术语,既表示Jimple中的Statement也表示Baf的instruction。

    为main方法创建一个Jimple Body,添加locals和instruction到Body上。

JimpleBody body = JimpleBody.v().newBody(method);

Method.setActiveBody(body);

    我们调用Jimple的单例对象以创建一个新的JimpleBody,并关联到我们的方法中,并且设置为方法的active body。

添加Local

Local arg = Jimple.v().newLocal("10",ArrayType.v(RefType.v("java.lang.String"),1));

body.getLocals().add(arg);

    我们创建少数的新的Jimple Local并添加到Body中。

添加Unit

Units.add(Jimple.v().newIdentifyStmt(arg,

Jimple.v().newParameterRef(ArrayType.v

(RefType.v("java.lang.String"),1),0)));

    Soot方法声明了它拥有参数,但没有将其与Body的Locals绑定。而IdentityStmt完成这一切。它将第一个参数,即字符串数组赋值给args。

//insert "tmpRef.println("Hello World1")"

{

SootMethod toCall = Scene.v().getMethod

("<java.io.PrintStream: void println(java.lang.String)>");

Units.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr

(tmpRef, toCall.makeRef(), StringConstant.v("Hello World!"));

))

}

    我们获取方法签名:"<java.io.PrintStream: void println(java.lang.String)>"(它的名字叫println,术语PrintStream),返回void并且将一个String作为它的参数---这对于唯一确定一个方法足够了,并且携带一个StringConstant:"Hello World"。

写入类文件

    为了将程序输出到class文件中,方法体必须从Jimple转换为Jasmine,并且二进制汇编到字节码。二进制汇编到字节码是过JasminOutputStream来执行的。

    我们首先构建了输出流,它携带了Jasmine源,并且输出了.class文件。我们可手动指定文件名,或者可以让Soot决策合适的文件名。我们使用后者,如下:

String filename = SourceLocator.v().getFileNameFor(sClass,Options.output_format_class)

OutputStream streamOut = new JasminOutputStream(new FileOutpuStream(fileName));

PrintWriter writeOut = new PrintWriter(new OutputStreamWriter(streamOut));

    我们现在将Jimple转换为Jasmin,并且打印Jasmine的结果类到输出流中。

JasminClass jasmineClass = new soot.jimple.JasminClass(sClass);

jasminClass.print(writerOut);

writerOut.flush();

streamOut.close();

    如果我们希望输出是Jimple源而表示class文件,则如下:

String filename = SourceLocator.v().getFileNameFor(sClass,Options.output_fomart_jimple);

OutputStream streamOut = new FileOutputStream(filename);

PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));

Print.v().printTo(sClass,writerOut);

writerOut.flush();

streamOut.close();

    我们忽略了JasminOutputStream,并且在Printer上调用了printTo方法。

Jimple格式的Hellow类如下:

public class HelloWorld extends java.lang.Object

{

public static void main(java.lang.String[])

{

java.lang.String[] r0;

java.io.PrintStream r1;

 

r0 := @parameter0: java.lang.String[];

r1 = <java.lang.System: java.io.PrintStream out>;

virtualinvoke r1.<java.io.PrintStream: void println(java.lang.String)>("Hello world!");

return;

}

}

完整代码如下:

/* Soot - a J*va Optimization Framework

 * Copyright (C) 1997-1999 Raja Vallee-Rai

 *

 * This library is free software; you can redistribute it and/or

 * modify it under the terms of the GNU Lesser General Public

 * License as published by the Free Software Foundation; either

 * version 2.1 of the License, or (at your option) any later version.

 *

 * This library is distributed in the hope that it will be useful,

 * but WITHOUT ANY WARRANTY; without even the implied warranty of

 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU

 * Lesser General Public License for more details.

 *

 * You should have received a copy of the GNU Lesser General Public

 * License along with this library; if not, write to the

 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,

 * Boston, MA 02111-1307, USA.

 */

 

/*

 * Modified by the Sable Research Group and others 1997-1999.  

 * See the 'credits' file distributed with Soot for the complete list of

 * contributors.  (Soot is distributed at http://www.sable.mcgill.ca/soot)

 */

 

 

//package ashes.examples.createclass;

 

import soot.*;

import soot.jimple.*;

import soot.options.Options;

import soot.util.*;

import java.io.*;

import java.util.*;

 

/** Example of using Soot to create a classfile from scratch.

 * The 'createclass' example creates a HelloWorld class file using Soot.

 * It proceeds as follows:

 *

 * - Create a SootClass <code>HelloWorld</code> extending java.lang.Object.

 *

 * - Create a 'main' method and add it to the class.

 *

 * - Create an empty JimpleBody and add it to the 'main' method.

 *

 * - Add locals and statements to JimpleBody.

 *

 * - Write the result out to a class file.

 */

 

public class Main

{

    public static void main(String[] args) throws FileNotFoundException, IOException

    {

        SootClass sClass;

        SootMethod method;

 

        // Resolve dependencies

           Scene.v().loadClassAndSupport("java.lang.Object");

           Scene.v().loadClassAndSupport("java.lang.System");

 

        // Declare 'public class HelloWorld'   

           sClass = new SootClass("HelloWorld", Modifier.PUBLIC);

 

        // 'extends Object'

           sClass.setSuperclass(Scene.v().getSootClass("java.lang.Object"));

           Scene.v().addClass(sClass);

 

        // Create the method, public static void main(String[])

           method = new SootMethod("main",

                Arrays.asList(new Type[] {ArrayType.v(RefType.v("java.lang.String"), 1)}),

                VoidType.v(), Modifier.PUBLIC | Modifier.STATIC);

 

           sClass.addMethod(method);

 

        // Create the method body

        {

            // create empty body

            JimpleBody body = Jimple.v().newBody(method);

 

            method.setActiveBody(body);

            Chain units = body.getUnits();

            Local arg, tmpRef;

 

            // Add some locals, java.lang.String l0

                arg = Jimple.v().newLocal("l0", ArrayType.v(RefType.v("java.lang.String"), 1));

                body.getLocals().add(arg);

 

            // Add locals, java.io.printStream tmpRef

                tmpRef = Jimple.v().newLocal("tmpRef", RefType.v("java.io.PrintStream"));

                body.getLocals().add(tmpRef);

 

            // add "l0 = @parameter0"

                units.add(Jimple.v().newIdentityStmt(arg, 

                     Jimple.v().newParameterRef(ArrayType.v(RefType.v("java.lang.String"), 1), 0)));

 

            // add "tmpRef = java.lang.System.out"

                units.add(Jimple.v().newAssignStmt(tmpRef, Jimple.v().newStaticFieldRef(

                    Scene.v().getField("<java.lang.System: java.io.PrintStream out>").makeRef())));

 

            // insert "tmpRef.println("Hello world!")"

            {

                SootMethod toCall = Scene.v().getMethod("<java.io.PrintStream: void println(java.lang.String)>");

                units.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(tmpRef, toCall.makeRef(), StringConstant.v("Hello world!"))));

            }                        

 

            // insert "return"

                units.add(Jimple.v().newReturnVoidStmt());

 

        }

 

        String fileName = SourceLocator.v().getFileNameFor(sClass, Options.output_format_class);

        OutputStream streamOut = new JasminOutputStream(

                                    new FileOutputStream(fileName));

        PrintWriter writerOut = new PrintWriter(

                                    new OutputStreamWriter(streamOut));

        JasminClass jasminClass = new soot.jimple.JasminClass(sClass);

        jasminClass.print(writerOut);

        writerOut.flush();

        streamOut.close();

    }

 

}