From c0ba420da1555e5e529f8151a7dfa6eb2191c428 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Fri, 18 Oct 2024 21:46:27 +0200 Subject: [PATCH 1/9] Allow conversion in dex for float <-> int and double <-> long --- src/main/java/soot/dexpler/DexBody.java | 20 +++++++++++++++++++ .../typing/fast/TypePromotionUseVisitor.java | 6 +++++- .../toolkits/typing/fast/TypeResolver.java | 6 +++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index 96c84219a04..a4f4047260e 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -70,6 +70,7 @@ import soot.DoubleType; import soot.FloatType; import soot.IntType; +import soot.IntegerType; import soot.Local; import soot.LongType; import soot.Modifier; @@ -141,6 +142,7 @@ import soot.jimple.toolkits.scalar.UnreachableCodeEliminator; import soot.jimple.toolkits.typing.fast.DefaultTypingStrategy; import soot.jimple.toolkits.typing.fast.ITypingStrategy; +import soot.jimple.toolkits.typing.fast.TypePromotionUseVisitor; import soot.options.JBOptions; import soot.options.Options; import soot.tagkit.LineNumberTag; @@ -819,6 +821,24 @@ public Body jimplify(Body b, SootMethod m) { new soot.jimple.toolkits.typing.fast.TypeResolver(jBody) { + protected soot.jimple.toolkits.typing.fast.TypePromotionUseVisitor createTypePromotionUseVisitor(JimpleBody jb, + soot.jimple.toolkits.typing.fast.Typing tg) { + return new TypePromotionUseVisitor(jb, tg) { + protected boolean allowConversion(Type ancestor, Type child) { + if ((ancestor instanceof IntegerType || ancestor instanceof FloatType) + && (child instanceof IntegerType || child instanceof FloatType)) { + return true; + } + if ((ancestor instanceof LongType || ancestor instanceof DoubleType) + && (child instanceof LongType || child instanceof DoubleType)) { + return true; + } + return super.allowConversion(ancestor, child); + } + }; + + } + @Override protected Collection reduceToAllowedTypesForLocal(Collection lcas, Local v) { Collection t = definiteConstraints.get(v); diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java index 4ee4909554b..9970d066919 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java @@ -99,7 +99,7 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { Type t = AugEvalFunction.eval_(this.tg, op, stmt, this.jb); - if (!AugHierarchy.ancestor_(useType, t)) { + if (!allowConversion(useType, t)) { logger.error(String.format("Failed Typing in %s at statement %s: Is not cast compatible: %s <-- %s", jb.getMethod().getSignature(), stmt, useType, t)); this.fail = true; @@ -118,6 +118,10 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { return op; } + protected boolean allowConversion(Type ancestor, Type child) { + return AugHierarchy.ancestor_(ancestor, child); + } + @Override public boolean finish() { return this.typingChanged || this.fail; diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index 1e7b1b647de..ec92ceba04f 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -322,7 +322,7 @@ private Typing typePromotion(Typing tg) { AugEvalFunction ef = new AugEvalFunction(this.jb); AugHierarchy h = new AugHierarchy(); UseChecker uc = new UseChecker(this.jb); - TypePromotionUseVisitor uv = new TypePromotionUseVisitor(jb, tg); + TypePromotionUseVisitor uv = createTypePromotionUseVisitor(jb, tg); do { Collection sigma = this.applyAssignmentConstraints(tg, ef, h); if (sigma.isEmpty()) { @@ -351,6 +351,10 @@ private Typing typePromotion(Typing tg) { return tg; } + protected TypePromotionUseVisitor createTypePromotionUseVisitor(JimpleBody jb, Typing tg) { + return new TypePromotionUseVisitor(jb, tg); + } + protected Type convert(Type t) { if (t instanceof Integer1Type) { return booleanType; From 72b7640f56a49fb40d8d02f35d4e23999fb19f7d Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Sat, 19 Oct 2024 13:41:35 +0200 Subject: [PATCH 2/9] Improve typing --- src/main/java/soot/dexpler/DexBody.java | 6 ++- .../typing/fast/TypePromotionUseVisitor.java | 9 +++- .../toolkits/typing/fast/TypeResolver.java | 50 +++++++++++++++++++ .../jimple/toolkits/typing/fast/Typing.java | 3 +- .../SharedInitializationLocalSplitter.java | 11 +++- 5 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index 9e97905c6a7..4eb5ebd1d45 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -722,7 +722,6 @@ public Body jimplify(Body b, SootMethod m) { handleKnownDexTypes(b, jimple); new SharedInitializationLocalSplitter(DalvikThrowAnalysis.v()).transform(jBody); - // split first to find undefined uses getLocalSplitter().transform(jBody); @@ -820,12 +819,17 @@ public Body jimplify(Body b, SootMethod m) { } } + getLocalSplitter().transform(jBody); + new soot.jimple.toolkits.typing.fast.TypeResolver(jBody) { protected soot.jimple.toolkits.typing.fast.TypePromotionUseVisitor createTypePromotionUseVisitor(JimpleBody jb, soot.jimple.toolkits.typing.fast.Typing tg) { return new TypePromotionUseVisitor(jb, tg) { protected boolean allowConversion(Type ancestor, Type child) { + if (ancestor == child) { + return true; + } if ((ancestor instanceof IntegerType || ancestor instanceof FloatType) && (child instanceof IntegerType || child instanceof FloatType)) { return true; diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java index 9970d066919..b72dbf76696 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java @@ -97,7 +97,12 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { return op; } - Type t = AugEvalFunction.eval_(this.tg, op, stmt, this.jb); + final Type t = AugEvalFunction.eval_(this.tg, op, stmt, this.jb); + final boolean eqType = TypeResolver.typesEqual(t, useType); + if (eqType) { + //shortcut + return op; + } if (!allowConversion(useType, t)) { logger.error(String.format("Failed Typing in %s at statement %s: Is not cast compatible: %s <-- %s", @@ -106,7 +111,7 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { } else if (!checkOnly && op instanceof Local && (t instanceof Integer1Type || t instanceof Integer127Type || t instanceof Integer32767Type || t instanceof WeakObjectType)) { Local v = (Local) op; - if (!TypeResolver.typesEqual(t, useType)) { + if (!eqType) { Type t_ = this.promote(t, useType); if (!TypeResolver.typesEqual(t, t_)) { this.tg.set(v, t_); diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index ec92ceba04f..2491c0ea6a5 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -33,6 +33,8 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import soot.ArrayType; @@ -83,6 +85,7 @@ public class TypeResolver { private final List assignments; private final HashMap depends; + private final Set singleAssignments; private final LocalGenerator localGenerator; public TypeResolver(JimpleBody jb) { @@ -90,6 +93,31 @@ public TypeResolver(JimpleBody jb) { this.assignments = new ArrayList(); this.depends = new HashMap(jb.getLocalCount()); this.localGenerator = Scene.v().createLocalGenerator(jb); + Map map = new HashMap<>(); + for (Unit stmt : this.jb.getUnits()) { + if (stmt instanceof DefinitionStmt) { + DefinitionStmt def = (DefinitionStmt) stmt; + + Value lhs = def.getLeftOp(); + if (lhs instanceof Local) { + Local l = (Local) lhs; + Integer c = map.get(l); + if (c == null) { + c = 0; + } + c++; + map.put(l, c); + } + } + } + Iterator> t = map.entrySet().iterator(); + while (t.hasNext()) { + if (t.next().getValue() > 1) { + t.remove(); + } + } + this.singleAssignments = map.keySet(); + this.initAssignments(); } @@ -504,6 +532,28 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction wl.set(0, numAssignments); sigma.add(new WorklistElement(tg, wl, new TypeDecision())); + if (tg.map.isEmpty()) { + //First get the easy cases out of the way. + for (int i = 0; i < numAssignments; i++) { + final DefinitionStmt stmt = this.assignments.get(i); + Value lhs = stmt.getLeftOp(); + if (lhs instanceof Local) { + Local v = (Local) lhs; + if (singleAssignments.contains(v)) { + Collection d = ef.eval(tg, stmt.getRightOp(), stmt); + if (d.size() == 1) { + Type t_ = d.iterator().next(); + d = reduceToAllowedTypesForLocal(Collections.singleton(t_), v); + if (d.size() == 1) { + tg.set(v, d.iterator().next()); + wl.clear(i); + } + } + } + } + } + } + Set throwable = null; while (!sigma.isEmpty()) { diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java b/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java index eaf41b03da3..1435a3564ae 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java @@ -29,6 +29,7 @@ import java.util.Map; import soot.Local; +import soot.NullType; import soot.Type; /** @@ -56,7 +57,7 @@ public Type get(Local v) { } public Type set(Local v, Type t) { - return (t instanceof BottomType) ? null : this.map.put(v, t); + return (t instanceof BottomType || t instanceof NullType) ? null : this.map.put(v, t); } public Collection getAllLocals() { diff --git a/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java b/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java index e53f7817b4f..df9e196be71 100644 --- a/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java +++ b/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java @@ -149,6 +149,10 @@ protected void internalTransform(Body body, String phaseName, Map clusters = clustersPerLocal.get(lcl); if (clusters.size() <= 1) { - // Not interesting - continue; + //Do we have any non-cluster writes? + if (clusters.iterator().next().constantInitializers.containsAll(defs.getDefsOf(lcl))) { + // Not interesting + continue; + } } for (Cluster cluster : clusters) { // we have an overlap, we need to split. From e105f6a0cb1e02ca2e8d7b8e9a92cf880063ecf4 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Sat, 19 Oct 2024 18:18:49 +0200 Subject: [PATCH 3/9] Do not check for switch loops that also may happen in the wild --- src/main/java/soot/dexpler/DexBody.java | 3 ++- src/main/java/soot/toDex/StmtVisitor.java | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index 4eb5ebd1d45..63c9e48bb91 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -141,6 +141,7 @@ import soot.jimple.toolkits.scalar.NopEliminator; import soot.jimple.toolkits.scalar.UnconditionalBranchFolder; import soot.jimple.toolkits.scalar.UnreachableCodeEliminator; +import soot.jimple.toolkits.typing.fast.BottomType; import soot.jimple.toolkits.typing.fast.DefaultTypingStrategy; import soot.jimple.toolkits.typing.fast.ITypingStrategy; import soot.jimple.toolkits.typing.fast.TypePromotionUseVisitor; @@ -1091,7 +1092,7 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { // for (Local l : jBody.getLocals()) { Type t = l.getType(); - if (t instanceof NullType) { + if (t instanceof NullType || t instanceof BottomType) { l.setType(objectType); } } diff --git a/src/main/java/soot/toDex/StmtVisitor.java b/src/main/java/soot/toDex/StmtVisitor.java index 206ca83f8e0..2b96c71bcf3 100644 --- a/src/main/java/soot/toDex/StmtVisitor.java +++ b/src/main/java/soot/toDex/StmtVisitor.java @@ -784,9 +784,6 @@ public void caseLookupSwitchStmt(LookupSwitchStmt stmt) { // create sparse-switch instruction that references the payload Value key = stmt.getKey(); Stmt defaultTarget = (Stmt) stmt.getDefaultTarget(); - if (defaultTarget == stmt) { - throw new RuntimeException("Looping switch block detected"); - } addInsn(buildSwitchInsn(Opcode.SPARSE_SWITCH, key, defaultTarget, payload, stmt), stmt); } From 1fcdf46eb404af353750bf0a02dc59c9e2848248 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Sat, 19 Oct 2024 23:00:56 +0200 Subject: [PATCH 4/9] Faster minimize --- .../typing/fast/DefaultTypingStrategy.java | 92 ++++++++++++------- 1 file changed, 61 insertions(+), 31 deletions(-) diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java b/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java index 14160f3a3c9..9300730a09c 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/DefaultTypingStrategy.java @@ -73,20 +73,46 @@ public static MultiMap getFlatTyping(List tgs) { return map; } - public static Set getObjectLikeTypings(List tgs) { + public static Set getUseTypingsForLocals(List tgs, IHierarchy h) { Set objectLikeTypeSet = new HashSet<>(); objectLikeTypeSet.add(Scene.v().getObjectType()); objectLikeTypeSet.add(RefType.v("java.io.Serializable")); objectLikeTypeSet.add(RefType.v("java.lang.Cloneable")); - Set objectLikeVars = new HashSet<>(); MultiMap ft = getFlatTyping(tgs); + Set localsToCheck = new HashSet<>(ft.keySet()); for (Local l : ft.keySet()) { - if (objectLikeTypeSet.equals(ft.get(l))) { - objectLikeVars.add(l); + Set typings = ft.get(l); + if (typings.size() == 1) { + //all typings agree on that type for that local + localsToCheck.remove(l); + continue; + } + if (objectLikeTypeSet.equals(typings)) { + localsToCheck.remove(l); + } + Type[] types = new Type[typings.size()]; + typings.toArray(types); + boolean allNotComparable = true; + outer: for (int x = 0; x < types.length; x++) { + Type ta = types[x]; + for (int y = x + 1; y < types.length; y++) { + Type tb = types[y]; + if (TypeResolver.typesEqual(ta, tb)) { + throw new AssertionError("Should not happen: " + ta + " == " + tb); + } else if (h.ancestor(ta, tb) || h.ancestor(tb, ta)) { + allNotComparable = false; + break outer; + } + } + } + if (allNotComparable) { + //we can't compare any of two types to each other, so why bother? + localsToCheck.remove(l); } + } - return objectLikeVars; + return localsToCheck; } @Override @@ -106,7 +132,10 @@ public void minimize(List tgs, IHierarchy h) { public void minimizeSequential(List tgs, IHierarchy h) { // int count = 0; // int tgsSize = tgs.size(); - Set objectVars = getObjectLikeTypings(tgs); + Set test = getUseTypingsForLocals(tgs, h); + if (test.isEmpty()) { + return; + } OUTER: for (ListIterator i = tgs.listIterator(); i.hasNext();) { Typing tgi = i.next(); // count++; @@ -126,7 +155,7 @@ public void minimizeSequential(List tgs, IHierarchy h) { if (tgj == null) { continue; // element is marked to be deleted } - int comp = compare(tgi, tgj, h, objectVars); + int comp = compare(tgi, tgj, h, test); if (comp == 1) { // if compare = 1, then tgi is the more general typing // We shouldn't pick that one as we would then end up @@ -144,31 +173,29 @@ public void minimizeSequential(List tgs, IHierarchy h) { } } - public int compare(Typing a, Typing b, IHierarchy h, Collection localsToIgnore) { + public int compare(Typing a, Typing b, IHierarchy h, Collection localsCheck) { int r = 0; - for (Local v : a.map.keySet()) { - if (!localsToIgnore.contains(v)) { - Type ta = a.get(v), tb = b.get(v); - - int cmp; - if (TypeResolver.typesEqual(ta, tb)) { - cmp = 0; - } else if (h.ancestor(ta, tb)) { - cmp = 1; - if (r == -1) { - return 2; - } - } else if (h.ancestor(tb, ta)) { - cmp = -1; - if (r == 1) { - return 2; - } - } else { - return -2; + for (Local v : localsCheck) { + Type ta = a.get(v), tb = b.get(v); + + int cmp; + if (TypeResolver.typesEqual(ta, tb)) { + cmp = 0; + } else if (h.ancestor(ta, tb)) { + cmp = 1; + if (r == -1) { + return 2; } - if (r == 0) { - r = cmp; + } else if (h.ancestor(tb, ta)) { + cmp = -1; + if (r == 1) { + return 2; } + } else { + return -2; + } + if (r == 0) { + r = cmp; } } return r; @@ -176,7 +203,10 @@ public int compare(Typing a, Typing b, IHierarchy h, Collection localsToI public void minimizeParallel(List tgs, IHierarchy h) { logger.debug("Performing parallel minimization"); - Set objectVars = getObjectLikeTypings(tgs); + Set test = getUseTypingsForLocals(tgs, h); + if (test.isEmpty()) { + return; + } // We don't know what type of list we get, we need a list that is thread safe for get/set // values. (get could return stale values, but this would not cause harm) @@ -203,7 +233,7 @@ public void minimizeParallel(List tgs, IHierarchy h) { if (tgj == null) { continue; } - int comp = compare(tgi, tgj, h, objectVars); + int comp = compare(tgi, tgj, h, test); if (comp == 1) { // if compare = 1, then tgi is the more general typing // We shouldn't pick that one as we would then end up From 4bae98c9f6e0159c4a83dda6c7100f4f712376b4 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Sun, 20 Oct 2024 20:02:55 +0200 Subject: [PATCH 5/9] Support non-0 and non-1 integer boolean's in dex --- src/main/java/soot/dexpler/DexBody.java | 52 +++++++++++++++++-- .../typing/fast/TypePromotionUseVisitor.java | 2 +- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index 63c9e48bb91..faa1b493bba 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -68,6 +68,8 @@ import soot.ArrayType; import soot.Body; +import soot.BooleanConstant; +import soot.BooleanType; import soot.DoubleType; import soot.FloatType; import soot.IntType; @@ -118,6 +120,7 @@ import soot.jimple.FloatConstant; import soot.jimple.IfStmt; import soot.jimple.IntConstant; +import soot.jimple.InvokeExpr; import soot.jimple.Jimple; import soot.jimple.JimpleBody; import soot.jimple.LongConstant; @@ -841,6 +844,20 @@ protected boolean allowConversion(Type ancestor, Type child) { } return super.allowConversion(ancestor, child); } + + public Type promote(Type tlow, Type thigh) { + if (thigh instanceof BooleanType) { + if (tlow instanceof IntegerType) { + //Well... in Android's dex code, 0 = false and everything else is true + //While the compiler should never generate such code, there can be found code like this in the wild. + //And Android accepts it! + //Thus, we allow the type promotion and then correct the boolean constants + return thigh; + } + } + return super.promote(tlow, thigh); + + } }; } @@ -1053,13 +1070,32 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { for (Unit u : jBody.getUnits()) { if (u instanceof AssignStmt) { - AssignStmt ass = (AssignStmt) u; - if (ass.getRightOp() instanceof CastExpr) { - CastExpr c = (CastExpr) ass.getRightOp(); + final AssignStmt ass = (AssignStmt) u; + final Value rop = ass.getRightOp(); + if (rop instanceof CastExpr) { + CastExpr c = (CastExpr) rop; if (c.getType() instanceof NullType) { ass.setRightOp(nullConstant); } } + if (rop instanceof IntConstant) { + if (ass.getLeftOp().getType() instanceof BooleanType) { + ass.setRightOp(fixBooleanConstant((IntConstant) rop)); + } + } + + } + Stmt s = (Stmt) u; + if (s.containsInvokeExpr()) { + InvokeExpr inv = s.getInvokeExpr(); + for (int p = 0; p < inv.getArgCount(); p++) { + if (inv.getMethodRef().getParameterType(p) instanceof BooleanType) { + Value arg = inv.getArg(p); + if (arg instanceof IntConstant) { + inv.setArg(p, fixBooleanConstant((IntConstant) arg)); + } + } + } } if (u instanceof DefinitionStmt) { DefinitionStmt def = (DefinitionStmt) u; @@ -1108,6 +1144,16 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { return jBody; } + /** + * In Dex, every int is a valid boolean. + * 0 = false and everything else = true. + * @param arg + * @return + */ + private static BooleanConstant fixBooleanConstant(IntConstant arg) { + return BooleanConstant.v(arg.value != 0); + } + /** * For non-object array instructions, we know from the bytecode already what the types are, or at least we can reduce it to * two possibilities (int/float or float/double). diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java index b72dbf76696..31f6689333a 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypePromotionUseVisitor.java @@ -56,7 +56,7 @@ public TypePromotionUseVisitor(JimpleBody jb, Typing tg) { this.typingChanged = false; } - private Type promote(Type tlow, Type thigh) { + public Type promote(Type tlow, Type thigh) { if (tlow instanceof Integer1Type) { if (thigh instanceof IntType) { return Integer127Type.v(); From a5b2ed435e14bd4fa5ef5a4b7e93d112171c43db Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 10:04:34 +0200 Subject: [PATCH 6/9] Fix typings --- src/main/java/soot/dexpler/DexBody.java | 139 +++++++++++++--- .../java/soot/jimple/internal/JEqExpr.java | 7 + .../java/soot/jimple/internal/JGeExpr.java | 7 + .../java/soot/jimple/internal/JGtExpr.java | 7 + .../java/soot/jimple/internal/JLeExpr.java | 7 + .../java/soot/jimple/internal/JLtExpr.java | 7 + .../java/soot/jimple/internal/JNeExpr.java | 7 + .../toolkits/typing/fast/TypeResolver.java | 89 ++++++++--- .../jimple/toolkits/typing/fast/Typing.java | 3 + src/main/java/soot/toDex/StmtVisitor.java | 2 + .../SharedInitializationLocalSplitter.java | 148 ++++++++++++++---- 11 files changed, 344 insertions(+), 79 deletions(-) diff --git a/src/main/java/soot/dexpler/DexBody.java b/src/main/java/soot/dexpler/DexBody.java index faa1b493bba..43ab33e55c6 100755 --- a/src/main/java/soot/dexpler/DexBody.java +++ b/src/main/java/soot/dexpler/DexBody.java @@ -70,6 +70,7 @@ import soot.Body; import soot.BooleanConstant; import soot.BooleanType; +import soot.ByteType; import soot.DoubleType; import soot.FloatType; import soot.IntType; @@ -81,8 +82,10 @@ import soot.PackManager; import soot.PhaseOptions; import soot.PrimType; +import soot.RefLikeType; import soot.RefType; import soot.Scene; +import soot.ShortType; import soot.SootClass; import soot.SootMethod; import soot.Trap; @@ -99,13 +102,17 @@ import soot.dexpler.instructions.OdexInstruction; import soot.dexpler.instructions.PseudoInstruction; import soot.dexpler.instructions.RetypeableInstruction; +import soot.dexpler.tags.ByteOpTag; import soot.dexpler.tags.DexplerTag; import soot.dexpler.tags.DoubleOpTag; import soot.dexpler.tags.FloatOpTag; +import soot.dexpler.tags.IntOpTag; import soot.dexpler.tags.IntOrFloatOpTag; import soot.dexpler.tags.LongOrDoubleOpTag; +import soot.dexpler.tags.ShortOpTag; import soot.dexpler.typing.DalvikTyper; import soot.jimple.AddExpr; +import soot.jimple.AndExpr; import soot.jimple.ArrayRef; import soot.jimple.AssignStmt; import soot.jimple.BinopExpr; @@ -128,9 +135,14 @@ import soot.jimple.NeExpr; import soot.jimple.NullConstant; import soot.jimple.NumericConstant; +import soot.jimple.OrExpr; import soot.jimple.RemExpr; +import soot.jimple.ShlExpr; +import soot.jimple.ShrExpr; import soot.jimple.Stmt; import soot.jimple.SubExpr; +import soot.jimple.UshrExpr; +import soot.jimple.XorExpr; import soot.jimple.internal.JIdentityStmt; import soot.jimple.toolkits.base.Aggregator; import soot.jimple.toolkits.scalar.ConditionalBranchFolder; @@ -146,7 +158,10 @@ import soot.jimple.toolkits.scalar.UnreachableCodeEliminator; import soot.jimple.toolkits.typing.fast.BottomType; import soot.jimple.toolkits.typing.fast.DefaultTypingStrategy; +import soot.jimple.toolkits.typing.fast.IHierarchy; import soot.jimple.toolkits.typing.fast.ITypingStrategy; +import soot.jimple.toolkits.typing.fast.Integer1Type; +import soot.jimple.toolkits.typing.fast.NeedCastResult; import soot.jimple.toolkits.typing.fast.TypePromotionUseVisitor; import soot.options.JBOptions; import soot.options.Options; @@ -723,12 +738,10 @@ public Body jimplify(Body b, SootMethod m) { // Make sure that we don't have any overlapping uses due to returns DexReturnInliner.v().transform(jBody); - handleKnownDexTypes(b, jimple); new SharedInitializationLocalSplitter(DalvikThrowAnalysis.v()).transform(jBody); - // split first to find undefined uses - getLocalSplitter().transform(jBody); + getLocalSplitter().transform(jBody); // Remove dead code and the corresponding locals before assigning types getUnreachableCodeEliminator().transform(jBody); DeadAssignmentEliminator.v().transform(jBody); @@ -812,6 +825,8 @@ public Body jimplify(Body b, SootMethod m) { } DexFillArrayDataTransformer.v().transform(jBody); // SharedInitializationLocalSplitter destroys the inserted casts, so we have to reintroduce them + getLocalSplitter().transform(jBody); + MultiMap maybetypeConstraints = new HashMultiMap<>(); handleKnownDexTypes(b, jimple); handleKnownDexArrayTypes(b, jimple, maybetypeConstraints); @@ -823,10 +838,7 @@ public Body jimplify(Body b, SootMethod m) { } } - getLocalSplitter().transform(jBody); - new soot.jimple.toolkits.typing.fast.TypeResolver(jBody) { - protected soot.jimple.toolkits.typing.fast.TypePromotionUseVisitor createTypePromotionUseVisitor(JimpleBody jb, soot.jimple.toolkits.typing.fast.Typing tg) { return new TypePromotionUseVisitor(jb, tg) { @@ -846,12 +858,15 @@ protected boolean allowConversion(Type ancestor, Type child) { } public Type promote(Type tlow, Type thigh) { - if (thigh instanceof BooleanType) { - if (tlow instanceof IntegerType) { - //Well... in Android's dex code, 0 = false and everything else is true - //While the compiler should never generate such code, there can be found code like this in the wild. - //And Android accepts it! - //Thus, we allow the type promotion and then correct the boolean constants + if (thigh instanceof BooleanType && tlow instanceof IntegerType) { + //Well... in Android's dex code, 0 = false and everything else is true + //While the compiler should never generate such code, there can be found code like this in the wild. + //And Android accepts it! + //Thus, we allow the type promotion and then correct the boolean constants + return thigh; + } + if (tlow instanceof IntegerType) { + if (thigh instanceof FloatType || thigh instanceof IntType) { return thigh; } } @@ -862,6 +877,24 @@ public Type promote(Type tlow, Type thigh) { } + protected soot.jimple.toolkits.typing.fast.BytecodeHierarchy createBytecodeHierarchy() { + return new soot.jimple.toolkits.typing.fast.BytecodeHierarchy() { + public java.util.Collection lcas(Type a, Type b, boolean useWeakObjectType) { + Collection s = super.lcas(a, b, useWeakObjectType); + if (s.isEmpty()) { + //when we merge a null constant and anything non-primitive, we use the non-primitive type + if (a instanceof Integer1Type && b instanceof RefLikeType) { + return Collections.singleton(b); + } + if (b instanceof Integer1Type && a instanceof RefLikeType) { + return Collections.singleton(a); + } + } + return s; + } + }; + } + @Override protected Collection reduceToAllowedTypesForLocal(Collection lcas, Local v) { Collection t = definiteConstraints.get(v); @@ -892,6 +925,49 @@ protected soot.jimple.toolkits.typing.fast.ITypingStrategy getTypingStrategy() { protected CastInsertionUseVisitor createCastInsertionUseVisitor(soot.jimple.toolkits.typing.fast.Typing tg, soot.jimple.toolkits.typing.fast.IHierarchy h, boolean countOnly) { return new CastInsertionUseVisitor(countOnly, jBody, tg, h) { + + protected NeedCastResult needCast(Type target, Type from, IHierarchy h) { + NeedCastResult r = super.needCast(target, from, h); + if (r == NeedCastResult.NEEDS_CAST) { + if (target instanceof IntType || target instanceof FloatType) { + if (from instanceof IntegerType || from instanceof FloatType) { + return NeedCastResult.DISCOURAGED_TARGET_TYPE; + } + } + if (target instanceof LongType || target instanceof DoubleType) { + if (from instanceof IntegerType || from instanceof LongType || from instanceof DoubleType) { + return NeedCastResult.DISCOURAGED_TARGET_TYPE; + } + } + return r; + } + + //we need to this since some types are final already. Otherwise, + //we get no casts at all. + if (target instanceof PrimType && from instanceof PrimType) { + if (!from.isAllowedInFinalCode()) { + from = from.getDefaultFinalType(); + } + if (target.isAllowedInFinalCode()) { + if (target == from) { + return NeedCastResult.DOESNT_NEED_CAST; + } + if (target instanceof IntType || target instanceof FloatType) { + if (from instanceof IntegerType || from instanceof FloatType) { + return NeedCastResult.DISCOURAGED_TARGET_TYPE; + } + } + if (target instanceof LongType || target instanceof DoubleType) { + if (from instanceof IntegerType || from instanceof LongType || from instanceof DoubleType) { + return NeedCastResult.DISCOURAGED_TARGET_TYPE; + } + } + return NeedCastResult.NEEDS_CAST; + } + } + return NeedCastResult.DOESNT_NEED_CAST; + } + @Override public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { if (op instanceof LongConstant && useType instanceof DoubleType) { @@ -1008,7 +1084,6 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { // again lead to unused locals which we have to remove. LocalPacker.v().transform(jBody); UnusedLocalEliminator.v().transform(jBody); - PackManager.v().getTransform("jb.lns").apply(jBody); // Some apps reference static fields as instance fields. We fix this // on the fly. @@ -1240,19 +1315,29 @@ private void handleKnownDexTypes(Body b, final Jimple jimple) { if (rop instanceof BinopExpr) { boolean isDouble = u.hasTag(DoubleOpTag.NAME); boolean isFloat = u.hasTag(FloatOpTag.NAME); + boolean isInt = u.hasTag(IntOpTag.NAME); + boolean isShort = u.hasTag(ShortOpTag.NAME); + boolean isByte = u.hasTag(ByteOpTag.NAME); if (rop instanceof AddExpr || rop instanceof SubExpr || rop instanceof MulExpr || rop instanceof DivExpr - || rop instanceof RemExpr) { + || rop instanceof RemExpr || rop instanceof XorExpr || rop instanceof UshrExpr || rop instanceof ShrExpr + || rop instanceof ShlExpr || rop instanceof AndExpr || rop instanceof OrExpr) { Type t = null; if (isDouble) { t = DoubleType.v(); } else if (isFloat) { t = FloatType.v(); + } else if (isInt) { + t = IntType.v(); + } else if (isShort) { + t = ShortType.v(); + } else if (isByte) { + t = ByteType.v(); } if (t != null) { Local l = createOrGetVariableOfType(b, convSingle, t); Value prev = def.getLeftOp(); def.setLeftOp(l); - units.insertAfter(jimple.newAssignStmt(prev, jimple.newCastExpr(l, t)), u); + units.insertAfter(jimple.newAssignStmt(prev, l), u); } } BinopExpr bop = (BinopExpr) rop; @@ -1276,8 +1361,7 @@ private void handleKnownDexTypes(Body b, final Jimple jimple) { convDouble[idxConvVar] = jimple.newLocal(freshLocalName("lclConvToDouble" + idxConvVar), DoubleType.v()); b.getLocals().add(convDouble[idxConvVar]); } - units.insertBefore( - jimple.newAssignStmt(convDouble[idxConvVar], jimple.newCastExpr(cmp.getValue(), DoubleType.v())), u); + units.insertBefore(jimple.newAssignStmt(convDouble[idxConvVar], cmp.getValue()), u); cmp.setValue(convDouble[idxConvVar]); idxConvVar++; } @@ -1287,8 +1371,7 @@ private void handleKnownDexTypes(Body b, final Jimple jimple) { convFloat[idxConvVar] = jimple.newLocal(freshLocalName("lclConvToFloat" + idxConvVar), FloatType.v()); b.getLocals().add(convFloat[idxConvVar]); } - units.insertBefore( - jimple.newAssignStmt(convFloat[idxConvVar], jimple.newCastExpr(cmp.getValue(), FloatType.v())), u); + units.insertBefore(jimple.newAssignStmt(convFloat[idxConvVar], cmp.getValue()), u); cmp.setValue(convFloat[idxConvVar]); idxConvVar++; } @@ -1298,6 +1381,24 @@ private void handleKnownDexTypes(Body b, final Jimple jimple) { } } + Stmt s = (Stmt) u; + if (s.containsInvokeExpr()) { + InvokeExpr inv = s.getInvokeExpr(); + for (int pidx = 0; pidx < inv.getArgCount(); pidx++) { + Value arg = inv.getArg(pidx); + if (arg instanceof Constant) { + Type t = inv.getMethodRef().getParameterType(pidx); + if (t instanceof DoubleType && arg instanceof LongConstant) { + long vVal = ((LongConstant) arg).value; + inv.setArg(pidx, DoubleConstant.v(Double.longBitsToDouble(vVal))); + } + if (t instanceof FloatType && arg instanceof IntConstant) { + int vVal = ((IntConstant) arg).value; + inv.setArg(pidx, FloatConstant.v(Float.intBitsToFloat(vVal))); + } + } + } + } u = units.getSuccOf(u); } for (Unit u1 : units) { diff --git a/src/main/java/soot/jimple/internal/JEqExpr.java b/src/main/java/soot/jimple/internal/JEqExpr.java index a558af0382d..3b8df026766 100644 --- a/src/main/java/soot/jimple/internal/JEqExpr.java +++ b/src/main/java/soot/jimple/internal/JEqExpr.java @@ -1,5 +1,7 @@ package soot.jimple.internal; +import soot.BooleanType; + /*- * #%L * Soot - a J*va Optimization Framework @@ -46,6 +48,11 @@ public void apply(Switch sw) { ((ExprSwitch) sw).caseEqExpr(this); } + @Override + public Type getType() { + return BooleanType.v(); + } + @Override protected Unit makeBafInst(Type opType) { throw new RuntimeException("unsupported conversion: " + this); diff --git a/src/main/java/soot/jimple/internal/JGeExpr.java b/src/main/java/soot/jimple/internal/JGeExpr.java index 251d6c3d571..21a9bad3c69 100644 --- a/src/main/java/soot/jimple/internal/JGeExpr.java +++ b/src/main/java/soot/jimple/internal/JGeExpr.java @@ -1,5 +1,7 @@ package soot.jimple.internal; +import soot.BooleanType; + /*- * #%L * Soot - a J*va Optimization Framework @@ -41,6 +43,11 @@ public final String getSymbol() { return " >= "; } + @Override + public Type getType() { + return BooleanType.v(); + } + @Override public void apply(Switch sw) { ((ExprSwitch) sw).caseGeExpr(this); diff --git a/src/main/java/soot/jimple/internal/JGtExpr.java b/src/main/java/soot/jimple/internal/JGtExpr.java index 99f2489eae3..60865b39944 100644 --- a/src/main/java/soot/jimple/internal/JGtExpr.java +++ b/src/main/java/soot/jimple/internal/JGtExpr.java @@ -1,5 +1,7 @@ package soot.jimple.internal; +import soot.BooleanType; + /*- * #%L * Soot - a J*va Optimization Framework @@ -41,6 +43,11 @@ public final String getSymbol() { return " > "; } + @Override + public Type getType() { + return BooleanType.v(); + } + @Override public void apply(Switch sw) { ((ExprSwitch) sw).caseGtExpr(this); diff --git a/src/main/java/soot/jimple/internal/JLeExpr.java b/src/main/java/soot/jimple/internal/JLeExpr.java index 7dfe93092e1..9deb8540577 100644 --- a/src/main/java/soot/jimple/internal/JLeExpr.java +++ b/src/main/java/soot/jimple/internal/JLeExpr.java @@ -1,5 +1,7 @@ package soot.jimple.internal; +import soot.BooleanType; + /*- * #%L * Soot - a J*va Optimization Framework @@ -41,6 +43,11 @@ public final String getSymbol() { return " <= "; } + @Override + public Type getType() { + return BooleanType.v(); + } + @Override public void apply(Switch sw) { ((ExprSwitch) sw).caseLeExpr(this); diff --git a/src/main/java/soot/jimple/internal/JLtExpr.java b/src/main/java/soot/jimple/internal/JLtExpr.java index 2c4fd3db7d6..3c5642771bb 100644 --- a/src/main/java/soot/jimple/internal/JLtExpr.java +++ b/src/main/java/soot/jimple/internal/JLtExpr.java @@ -1,5 +1,7 @@ package soot.jimple.internal; +import soot.BooleanType; + /*- * #%L * Soot - a J*va Optimization Framework @@ -41,6 +43,11 @@ public final String getSymbol() { return " < "; } + @Override + public Type getType() { + return BooleanType.v(); + } + @Override public void apply(Switch sw) { ((ExprSwitch) sw).caseLtExpr(this); diff --git a/src/main/java/soot/jimple/internal/JNeExpr.java b/src/main/java/soot/jimple/internal/JNeExpr.java index 79e61cb6d46..f0c154217e8 100644 --- a/src/main/java/soot/jimple/internal/JNeExpr.java +++ b/src/main/java/soot/jimple/internal/JNeExpr.java @@ -1,5 +1,7 @@ package soot.jimple.internal; +import soot.BooleanType; + /*- * #%L * Soot - a J*va Optimization Framework @@ -41,6 +43,11 @@ public final String getSymbol() { return " != "; } + @Override + public Type getType() { + return BooleanType.v(); + } + @Override public void apply(Switch sw) { ((ExprSwitch) sw).caseNeExpr(this); diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index 2491c0ea6a5..960c49807e3 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -44,6 +44,7 @@ import soot.IntegerType; import soot.Local; import soot.LocalGenerator; +import soot.LongType; import soot.PatchingChain; import soot.PrimType; import soot.RefType; @@ -83,16 +84,20 @@ public class TypeResolver { protected final JimpleBody jb; - private final List assignments; - private final HashMap depends; - private final Set singleAssignments; + private List assignments; + private HashMap depends; + private Set singleAssignments; + private BitSet simple; private final LocalGenerator localGenerator; public TypeResolver(JimpleBody jb) { this.jb = jb; - this.assignments = new ArrayList(); - this.depends = new HashMap(jb.getLocalCount()); this.localGenerator = Scene.v().createLocalGenerator(jb); + + } + + private void initAssignments() { + this.depends = new HashMap(jb.getLocalCount()); Map map = new HashMap<>(); for (Unit stmt : this.jb.getUnits()) { if (stmt instanceof DefinitionStmt) { @@ -118,10 +123,7 @@ public TypeResolver(JimpleBody jb) { } this.singleAssignments = map.keySet(); - this.initAssignments(); - } - - private void initAssignments() { + this.assignments = new ArrayList(); for (Unit stmt : this.jb.getUnits()) { if (stmt instanceof DefinitionStmt) { this.initAssignment((DefinitionStmt) stmt); @@ -174,9 +176,11 @@ private void addDepend(Local v, int stmtIndex) { public void inferTypes() { this.split_new(); + //split_new creates new assignments... + this.initAssignments(); ITypingStrategy typingStrategy = getTypingStrategy(); AugEvalFunction ef = createAugEvalFunction(this.jb); - BytecodeHierarchy bh = new BytecodeHierarchy(); + BytecodeHierarchy bh = createBytecodeHierarchy(); Collection sigma = this.applyAssignmentConstraints(typingStrategy.createTyping(this.jb.getLocals()), ef, bh); // If there is nothing to type, we can quit @@ -186,8 +190,9 @@ public void inferTypes() { int[] castCount = new int[1]; Typing tg = this.minCasts(sigma, bh, castCount); - - this.insertCasts(tg, bh, false); + if (castCount[0] > 0) { + this.insertCasts(tg, bh, false); + } final BottomType bottom = BottomType.v(); for (Local v : this.jb.getLocals()) { @@ -211,8 +216,12 @@ public void inferTypes() { } } - protected AugEvalFunction createAugEvalFunction(JimpleBody jb2) { - return new AugEvalFunction(this.jb); + protected BytecodeHierarchy createBytecodeHierarchy() { + return new BytecodeHierarchy(); + } + + protected AugEvalFunction createAugEvalFunction(JimpleBody jb) { + return new AugEvalFunction(jb); } protected ITypingStrategy getTypingStrategy() { @@ -242,6 +251,8 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { if (useType == t) { if (op instanceof CastExpr) { CastExpr ce = (CastExpr) op; + //by default, t only checks for the type of the cast target + t = AugEvalFunction.eval_(this.tg, ce.getOp(), stmt, this.jb); if (ce.getType() == t) { //no cast necessary! return ce.getOp(); @@ -250,19 +261,14 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { return op; } - boolean needCast = false; - if (useType instanceof PrimType && t instanceof PrimType) { - if (t.isAllowedInFinalCode() && useType.isAllowedInFinalCode()) { - needCast = true; - } - } - if (!needCast && this.h.ancestor(useType, t)) { + NeedCastResult needCast = needCast(useType, t, h); + if (needCast == NeedCastResult.DOESNT_NEED_CAST && this.h.ancestor(useType, t)) { return op; } this.count++; - if (countOnly) { + if (countOnly || needCast == NeedCastResult.DISCOURAGED_TARGET_TYPE) { return op; } else { // If we're referencing an array of the base type java.lang.Object, @@ -294,6 +300,15 @@ public Value visit(Value op, Type useType, Stmt stmt, boolean checkOnly) { } } + protected NeedCastResult needCast(Type target, Type from, IHierarchy h) { + if (target instanceof PrimType && from instanceof PrimType) { + if (from.isAllowedInFinalCode() && target.isAllowedInFinalCode()) { + return NeedCastResult.NEEDS_CAST; + } + } + return NeedCastResult.DOESNT_NEED_CAST; + } + private boolean isObjectLikeType(RefType rt) { if (rt instanceof WeakObjectType) { return true; @@ -347,9 +362,9 @@ public boolean finish() { private Typing typePromotion(Typing tg) { boolean conversionsPending; do { - AugEvalFunction ef = new AugEvalFunction(this.jb); + AugEvalFunction ef = createAugEvalFunction(this.jb); AugHierarchy h = new AugHierarchy(); - UseChecker uc = new UseChecker(this.jb); + UseChecker uc = createUseChecker(this.jb); TypePromotionUseVisitor uv = createTypePromotionUseVisitor(jb, tg); do { Collection sigma = this.applyAssignmentConstraints(tg, ef, h); @@ -379,6 +394,10 @@ private Typing typePromotion(Typing tg) { return tg; } + protected UseChecker createUseChecker(JimpleBody jb) { + return new UseChecker(jb); + } + protected TypePromotionUseVisitor createTypePromotionUseVisitor(JimpleBody jb, Typing tg) { return new TypePromotionUseVisitor(jb, tg); } @@ -403,7 +422,7 @@ protected Type convert(Type t) { } private int insertCasts(Typing tg, IHierarchy h, boolean countOnly) { - UseChecker uc = new UseChecker(this.jb); + UseChecker uc = createUseChecker(this.jb); CastInsertionUseVisitor uv = createCastInsertionUseVisitor(tg, h, countOnly); uc.check(tg, uv); return uv.getCount(); @@ -533,6 +552,7 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction sigma.add(new WorklistElement(tg, wl, new TypeDecision())); if (tg.map.isEmpty()) { + simple = new BitSet(numAssignments); //First get the easy cases out of the way. for (int i = 0; i < numAssignments; i++) { final DefinitionStmt stmt = this.assignments.get(i); @@ -546,12 +566,17 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction d = reduceToAllowedTypesForLocal(Collections.singleton(t_), v); if (d.size() == 1) { tg.set(v, d.iterator().next()); + simple.set(i); wl.clear(i); } } } } } + } else { + if (simple != null) { + wl.andNot(simple); + } } Set throwable = null; @@ -578,6 +603,11 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction boolean isFirstType = true; for (Type t_ : ef.eval(tg, stmt.getRightOp(), stmt)) { + if (t_ instanceof LongType) + DebugPoint.Break(); + if (t_ instanceof BooleanType) { + ef.eval(tg, stmt.getRightOp(), stmt); + } if (lhs instanceof ArrayRef) { /* * We only need to consider array references on the LHS of assignments where there is supertyping between array @@ -657,6 +687,14 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction } // end for } } + + for (Typing p : r) { + for (Entry x : p.map.entrySet()) { + if (x.getValue() instanceof Integer1Type) { + DebugPoint.Break(); + } + } + } typingStrategy.minimize(r, h); return r; } @@ -721,7 +759,6 @@ private void split_new() { units.insertAfter(assignStmt, Util.findLastIdentityUnit(body, assign)); assign.setLeftOp(newlocal); - this.initAssignment(assignStmt); } } break; diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java b/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java index 1435a3564ae..8b5b420790f 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.Map; +import soot.BooleanType; import soot.Local; import soot.NullType; import soot.Type; @@ -57,6 +58,8 @@ public Type get(Local v) { } public Type set(Local v, Type t) { + if (t instanceof BooleanType || t instanceof Integer1Type) + DebugPoint.Break(); return (t instanceof BottomType || t instanceof NullType) ? null : this.map.put(v, t); } diff --git a/src/main/java/soot/toDex/StmtVisitor.java b/src/main/java/soot/toDex/StmtVisitor.java index 2b96c71bcf3..f93647addfd 100644 --- a/src/main/java/soot/toDex/StmtVisitor.java +++ b/src/main/java/soot/toDex/StmtVisitor.java @@ -659,6 +659,8 @@ private Insn buildArrayGetInsn(Register destinationReg, ArrayRef sourceRef) { Register indexReg = regAlloc.asImmediate(index, constantV); Local array = (Local) sourceRef.getBase(); Register arrayReg = regAlloc.asLocal(array); + if (!(array.getType() instanceof ArrayType)) + System.out.println(); String arrayTypeDescriptor = SootToDexUtils.getArrayTypeDescriptor((ArrayType) array.getType()); Opcode opc = getPutGetOpcodeWithTypeSuffix(AGET_OPCODE, arrayTypeDescriptor); return new Insn23x(opc, destinationReg, arrayReg, indexReg); diff --git a/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java b/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java index df9e196be71..269acf080ce 100644 --- a/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java +++ b/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java @@ -22,6 +22,9 @@ * #L% */ +import java.util.BitSet; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -42,6 +45,7 @@ import soot.dexpler.DexNullThrowTransformer; import soot.jimple.AssignStmt; import soot.jimple.Constant; +import soot.jimple.DefinitionStmt; import soot.jimple.Jimple; import soot.jimple.toolkits.scalar.ConstantPropagatorAndFolder; import soot.jimple.toolkits.scalar.CopyPropagator; @@ -73,13 +77,19 @@ * * @author Marc Miltenberger */ + // @formatter:on +/** + * + */ public class SharedInitializationLocalSplitter extends BodyTransformer { private static final Logger logger = LoggerFactory.getLogger(SharedInitializationLocalSplitter.class); protected ThrowAnalysis throwAnalysis; protected boolean omitExceptingUnitEdges; + private boolean actAsNormalLocalSplitter; + public SharedInitializationLocalSplitter(Singletons.Global g) { } @@ -98,27 +108,16 @@ public static SharedInitializationLocalSplitter v() { private static final class Cluster { - protected final List constantInitializers; - protected final Unit use; - - public Cluster(Unit use, List constantInitializers) { - this.use = use; - this.constantInitializers = constantInitializers; - } + protected final BitSet constantInitializers; + protected final BitSet uses; + protected final BitSet nonConstantDefs; - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Constant intializers:\n"); - for (Unit r : constantInitializers) { - sb.append("\n - ").append(toStringUnit(r)); - } - return sb.toString(); + public Cluster(BitSet uses, BitSet constantDefs, BitSet nonConstantDefs) { + this.uses = uses; + this.constantInitializers = constantDefs; + this.nonConstantDefs = nonConstantDefs; } - private String toStringUnit(Unit u) { - return u + " (" + System.identityHashCode(u) + ")"; - } } @Override @@ -152,6 +151,17 @@ protected void internalTransform(Body body, String phaseName, Map clustersPerLocal = new HashMultiMap(); + final Map stmtToIndex = new HashMap<>(); + final Map indexToStmt = new HashMap<>(); final Chain units = body.getUnits(); + int idx = 0; + for (Unit s : units) { + stmtToIndex.put(s, idx); + indexToStmt.put(idx, s); + idx++; + + } + for (Unit s : units) { - for (ValueBox useBox : s.getUseBoxes()) { + nextUse: for (ValueBox useBox : s.getUseBoxes()) { Value v = useBox.getValue(); if (v instanceof Local) { Local luse = (Local) v; List allAffectingDefs = defs.getDefsOfAt(luse, s); - // Make sure we are only affected by Constant definitions via AssignStmt - if (allAffectingDefs.isEmpty() || !allAffectingDefs.stream() - .allMatch(u -> (u instanceof AssignStmt) && (((AssignStmt) u).getRightOp() instanceof Constant))) { - continue; + + BitSet constantDefs = new BitSet(idx); + BitSet nonConstantDefs = null; + + for (Unit affect : allAffectingDefs) { + if (affect instanceof DefinitionStmt) { + DefinitionStmt def = (DefinitionStmt) affect; + int actualidx = stmtToIndex.get(def); + if (def.getRightOp() instanceof Constant) { + constantDefs.set(actualidx); + } else { + if (nonConstantDefs == null) { + nonConstantDefs = new BitSet(idx); + } + nonConstantDefs.set(actualidx); + } + } + } + int useidx = stmtToIndex.get(s); + BitSet useset = new BitSet(useidx); + useset.set(useidx); + if (nonConstantDefs != null) { + Iterator it = clustersPerLocal.get(luse).iterator(); + while (it.hasNext()) { + Cluster existing = it.next(); + if (existing.nonConstantDefs == null) { + continue; + } + + //the idea is: When there is an overlap in any non-constant definition units, we need to merge them, since two different usages have overlapping definitions, i.e. we + //can only change all these uses + if (existing.nonConstantDefs.intersects(nonConstantDefs)) { + //we have an overlap + useset.or(existing.uses); + constantDefs.or(existing.constantInitializers); + nonConstantDefs.or(existing.nonConstantDefs); + + //we only keep the new definition with an overlap + it.remove(); + } + } } - clustersPerLocal.put(luse, new Cluster(s, allAffectingDefs)); + clustersPerLocal.put(luse, new Cluster(useset, constantDefs, nonConstantDefs)); } } } @@ -180,29 +237,52 @@ public void transformOnly(Body body) { int w = 0; for (Local lcl : clustersPerLocal.keySet()) { Set clusters = clustersPerLocal.get(lcl); - if (clusters.size() <= 1) { - //Do we have any non-cluster writes? - if (clusters.iterator().next().constantInitializers.containsAll(defs.getDefsOf(lcl))) { - // Not interesting - continue; - } + if (clusters.size() == 1) { + // Not interesting + continue; } for (Cluster cluster : clusters) { // we have an overlap, we need to split. Local newLocal = (Local) lcl.clone(); newLocal.setName(newLocal.getName() + '_' + ++w); locals.add(newLocal); - - for (Unit u : cluster.constantInitializers) { - AssignStmt assign = (AssignStmt) u; + BitSet constantInit = cluster.constantInitializers; + if (!actAsNormalLocalSplitter && constantInit.isEmpty()) { + continue; + } + for (int i = constantInit.nextSetBit(0); i != -1; i = constantInit.nextSetBit(i + 1)) { + AssignStmt assign = (AssignStmt) indexToStmt.get(i); + if (assign == null) { + throw new AssertionError("Wrong indice"); + } AssignStmt newAssign = Jimple.v().newAssignStmt(newLocal, assign.getRightOp()); units.insertAfter(newAssign, assign); CopyPropagator.copyLineTags(newAssign.getUseBoxes().get(0), assign); } - replaceLocalsInUnitUses(cluster.use, lcl, newLocal); + BitSet uses = cluster.uses; + for (int i = uses.nextSetBit(0); i != -1; i = uses.nextSetBit(i + 1)) { + Unit use = indexToStmt.get(i); + if (use == null) { + throw new AssertionError("Wrong indice"); + } + replaceLocalsInUnitUses(use, lcl, newLocal); + } + BitSet nonConstantDefs = cluster.nonConstantDefs; + if (nonConstantDefs != null) { + for (int i = nonConstantDefs.nextSetBit(0); i != -1; i = nonConstantDefs.nextSetBit(i + 1)) { + DefinitionStmt def = (DefinitionStmt) indexToStmt.get(i); + if (def == null) { + throw new AssertionError("Wrong indice"); + } + if (def.getLeftOp() == lcl) { + def.getLeftOpBox().setValue(newLocal); + } + } + } } } + UnusedLocalEliminator.v().transform(body); } private void replaceLocalsInUnitUses(Unit change, Value oldLocal, Local newLocal) { From 48ab3dea5996f6891612fc7c1841446723e37a56 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 10:08:33 +0200 Subject: [PATCH 7/9] Remove debug --- .../jimple/toolkits/typing/fast/TypeResolver.java | 13 ------------- .../soot/jimple/toolkits/typing/fast/Typing.java | 3 --- src/main/java/soot/toDex/StmtVisitor.java | 2 -- .../scalar/SharedInitializationLocalSplitter.java | 8 +++----- 4 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java index 960c49807e3..c7ab0bf7be7 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/TypeResolver.java @@ -44,7 +44,6 @@ import soot.IntegerType; import soot.Local; import soot.LocalGenerator; -import soot.LongType; import soot.PatchingChain; import soot.PrimType; import soot.RefType; @@ -603,11 +602,6 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction boolean isFirstType = true; for (Type t_ : ef.eval(tg, stmt.getRightOp(), stmt)) { - if (t_ instanceof LongType) - DebugPoint.Break(); - if (t_ instanceof BooleanType) { - ef.eval(tg, stmt.getRightOp(), stmt); - } if (lhs instanceof ArrayRef) { /* * We only need to consider array references on the LHS of assignments where there is supertyping between array @@ -688,13 +682,6 @@ protected Collection applyAssignmentConstraints(Typing tg, IEvalFunction } } - for (Typing p : r) { - for (Entry x : p.map.entrySet()) { - if (x.getValue() instanceof Integer1Type) { - DebugPoint.Break(); - } - } - } typingStrategy.minimize(r, h); return r; } diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java b/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java index 8b5b420790f..1435a3564ae 100644 --- a/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java +++ b/src/main/java/soot/jimple/toolkits/typing/fast/Typing.java @@ -28,7 +28,6 @@ import java.util.HashMap; import java.util.Map; -import soot.BooleanType; import soot.Local; import soot.NullType; import soot.Type; @@ -58,8 +57,6 @@ public Type get(Local v) { } public Type set(Local v, Type t) { - if (t instanceof BooleanType || t instanceof Integer1Type) - DebugPoint.Break(); return (t instanceof BottomType || t instanceof NullType) ? null : this.map.put(v, t); } diff --git a/src/main/java/soot/toDex/StmtVisitor.java b/src/main/java/soot/toDex/StmtVisitor.java index f93647addfd..2b96c71bcf3 100644 --- a/src/main/java/soot/toDex/StmtVisitor.java +++ b/src/main/java/soot/toDex/StmtVisitor.java @@ -659,8 +659,6 @@ private Insn buildArrayGetInsn(Register destinationReg, ArrayRef sourceRef) { Register indexReg = regAlloc.asImmediate(index, constantV); Local array = (Local) sourceRef.getBase(); Register arrayReg = regAlloc.asLocal(array); - if (!(array.getType() instanceof ArrayType)) - System.out.println(); String arrayTypeDescriptor = SootToDexUtils.getArrayTypeDescriptor((ArrayType) array.getType()); Opcode opc = getPutGetOpcodeWithTypeSuffix(AGET_OPCODE, arrayTypeDescriptor); return new Insn23x(opc, destinationReg, arrayReg, indexReg); diff --git a/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java b/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java index 269acf080ce..30c21ed874b 100644 --- a/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java +++ b/src/main/java/soot/toolkits/scalar/SharedInitializationLocalSplitter.java @@ -79,9 +79,6 @@ */ // @formatter:on -/** - * - */ public class SharedInitializationLocalSplitter extends BodyTransformer { private static final Logger logger = LoggerFactory.getLogger(SharedInitializationLocalSplitter.class); @@ -215,8 +212,9 @@ public void transformOnly(Body body) { continue; } - //the idea is: When there is an overlap in any non-constant definition units, we need to merge them, since two different usages have overlapping definitions, i.e. we - //can only change all these uses + //the idea is: When there is an overlap in any non-constant definition units, + //we need to merge them, since two different usages have overlapping definitions, + //i.e. we can only change all these uses if (existing.nonConstantDefs.intersects(nonConstantDefs)) { //we have an overlap useset.or(existing.uses); From 056f2faf8ab041ccdea87eb84867058b8e769d01 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 10:09:04 +0200 Subject: [PATCH 8/9] Add missing class --- .../toolkits/typing/fast/NeedCastResult.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/soot/jimple/toolkits/typing/fast/NeedCastResult.java diff --git a/src/main/java/soot/jimple/toolkits/typing/fast/NeedCastResult.java b/src/main/java/soot/jimple/toolkits/typing/fast/NeedCastResult.java new file mode 100644 index 00000000000..66ee02c3a2a --- /dev/null +++ b/src/main/java/soot/jimple/toolkits/typing/fast/NeedCastResult.java @@ -0,0 +1,47 @@ +package soot.jimple.toolkits.typing.fast; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2008 Ben Bellamy + * + * All rights reserved. + * %% + * This program 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 program 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +/** + * Whether a cast is needed or not + */ +public enum NeedCastResult { + /** + * The cast is needed + */ + NEEDS_CAST, + + /** + * No cast is needed + */ + DOESNT_NEED_CAST, + + /** + * Actually, no cast is needed, but it is discouraged. + * For example, in Dex, int and float are essentially the same. + * In some contexts however, we prefer one of the types. + */ + DISCOURAGED_TARGET_TYPE +} From 371d6640d0b9bf9592c851797b2ffe69f92c0381 Mon Sep 17 00:00:00 2001 From: Marc Miltenberger Date: Mon, 21 Oct 2024 10:14:42 +0200 Subject: [PATCH 9/9] Fix tests --- .../soot/portedtest/RedundantJimpleStatementsTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java b/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java index 3880327cb79..092d11c09a8 100644 --- a/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java +++ b/src/test/java/soot/portedtest/RedundantJimpleStatementsTest.java @@ -248,10 +248,9 @@ public void test09() { "$r3 = staticinvoke (22)", "$r4 = staticinvoke (23)", "$r2 = (MyClass) $r1", - "$r7 = virtualinvoke $r2.($r3, $r4)", - "$r5 = (java.lang.Integer) $r7", - "r6 = $r5", - "$i0 = virtualinvoke r6.()", + "$r6 = virtualinvoke $r2.($r3, $r4)", + "r5 = (java.lang.Integer) $r6", + "$i0 = virtualinvoke r5.()", "return $i0").collect(Collectors.toCollection(ArrayList::new)); SootMethod method = Scene.v().getMethod(""); assertJimpleStmts(method, expectedBodyStmts);