Skip to content

Soot migration

Alexey Volkov edited this page Dec 20, 2022 · 10 revisions

Soot migration

Terminology:

Soot JacoDB
bytecode storage - JacoDB
scope of visible classes Scene JcClasspath
class SootClass JcClassOrInterface
class method SootMethod JcMethod
class field SootField JcField
type (with generics substitution) - JcJvmType
3-address bytecode representation JimpleBody JcRawInstList
control flow graph ClassicCompleteUnitGraph JcGraph
class hierarchy Hierarchy HierarchyExt
call graph CallGraph UsagesExt

Recommendations

  1. do not forget to close resource: JcClasspath, JacoDB.
  2. Creating classpath is a heavy operation involving I/O. All jars/folders passed to classpath are checked for:
    1. processed before
    2. has changed since processed before
    3. processing if they are not processed As a result good code should try to reuse classpath instance. After that it's recommended to call close method to take JacoDB possibility to delete processed resources that are out-of-date with file-system
  3. If there is a chance that code will call for class hierarchies then it's better to install InMemoryHierarchy feature.
  4. Persist data in file system is usable if your code base is huge enough or live after process restart is possible.
  5. Install only features that are required for this database.

Operations

create storage

Soot
// points to specific runtime version
G.v().initJdk(new G.JreInfo(location, version)); 
Options options = Options.v();
options.set_soot_classpath(files);
Scene.v().loadNecessaryClasses();
PackManager.v().runPacks();
JacoDB(Java)
var db = JacoDB.async(new JcSettings()
    // points to specific runtime version
     .useJavaRuntime(runtimeFolder)
    // jars to process
     .loadByteCode(Arrays.asList(jar1, jar2))
    // persist all information to improve performance between restarts
     .persistent("/home/user/jcdb.db", false)
).get();
var classpath = db.asyncClasspath(listOf(jar1)).get();
JacoDB(Kotlin)
val db = jacodb {
    // points to specific runtime version
    useJavaRuntime(runtimeFolder)
    // jars to process
    loadByteCode(listOf(jar1, jar2))
    // persist all information to improve performance between restarts
    persistent(location = "/home/user/jcdb.db", clearOnStart = false)
}
val classpath = db.classpath(listOf(jar1))

find class

Soot
SootClass clazz = Scene.v().getSootClass("java.lang.String");
JacoDB(Java)
var clazz = classpath.findClassOrNull("java.lang.String")
JacoDB(Kotlin)
val clazz = classpath.findClassOrNull("java.lang.String")

Get 3-address bytecode representation

Soot
SootClass clazz = Scene.v().getSootClass("java.lang.String");
clazz.getMethod("length", Lists.emptyList()).retrieveActiveBody()
JacoDB(Java)
var clazz = classpath.findClassOrNull("java.lang.String");
Api.findMethodOrNull(clazz, "length").instructionList();
JacoDB(Kotlin)
val clazz = classpath.findClassOrNull("java.lang.String") ?: throw  IllegalStateException()
classpath.findMethodOrNull("length").instructionList()

Get control flow graph

Soot
new ClassicCompleteUnitGraph(sootMethod.getActiveBody());
JacoDB(Java)
var cfg = jcMethod.instructionList().graph()
JacoDB(Kotlin)
val cfg = jcMethod.instructionList().graph()

Hierarchy

Soot
Hierarchy h = new Hierarchy();
h.getDirectSubclassesOf(clazz);
h.getDirectSubinterfacesOf(clazz);
JacoDB(Java)
   var db = JacoDB.async(new JcSettings()
     .install(InMemoryHierarchy.INSTANCE)
   ).get();
   val ext = classpath.asyncHierarchy().get();
   ext.findSubClasses(clazz, allHierarchy = true)
   ext.findOverrides(method)
JacoDB(Kotlin)
val db = jacodb {
    // highly recommend to install this extension
    install(InMemoryHierarchy)
}

val ext = classpath.hierarchyExt()
ext.findSubClasses(clazz, allHierarchy = true)
ext.findOverrides(method)

Call graph/Usages

Soot
CallGraph cg = new CallGraph();
cg.edgesInto(edge);
cg.edgesOutOf(edge);
JacoDB(Java)
   var db = JacoDB.async(new JcSettings()
     // highly recommend to install InMemoryHierarchy extension
     .install(InMemoryHierarchy.INSTANCE, Usages.INSTANCE)
     ).get(); 
     
   var ext = classpath.asyncUsages();
   ext.findUsages(field, FieldUsageMode.READ);
   ext.findUsages(field, FieldUsageMode.WRITE);
   ext.findUsages(method);
JacoDB(Kotlin)
val db = jacodb {
    // highly recommend to install InMemoryHierarchy extension
    install(Usages, InMemoryHierarchy)
}

val ext = classpath.usagesExt()
ext.findUsages(field, FieldUsageMode.READ)
ext.findUsages(field, FieldUsageMode.WRITE)
ext.findUsages(method)
Clone this wiki locally