Search in sources :

Example 6 with MethodNode

use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.

the class ReflectiveFunctorFactory method makeBase.

private MethodNode makeBase(String name, String desc) {
    ClassNode owner = new ClassNode();
    owner.node.version = Opcodes.V1_7;
    owner.node.name = name;
    owner.node.superName = "java/lang/Object";
    owner.node.access = Opcodes.ACC_PUBLIC;
    MethodNode m = new MethodNode(new org.objectweb.asm.tree.MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "eval", desc, null, null), owner);
    owner.addMethod(m);
    return m;
}
Also used : ClassNode(org.mapleir.asm.ClassNode) MethodNode(org.mapleir.asm.MethodNode)

Example 7 with MethodNode

use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.

the class ReflectiveFunctorFactory method arithmetic.

@Override
public EvaluationFunctor<Number> arithmetic(Type t1, Type t2, Type rt, ArithmeticExpr.Operator op) {
    String name = t1.getClassName() + op.name() + t2.getClassName() + "RET" + rt.getClassName();
    if (cache.containsKey(name)) {
        return _get(name);
    }
    String desc = ("(" + t1.getDescriptor() + t2.getDescriptor() + ")" + rt.getDescriptor());
    MethodNode m = makeBase(name, desc);
    InsnList insns = new InsnList();
    {
        Type leftType = null;
        Type rightType = null;
        if (op == ArithmeticExpr.Operator.SHL || op == ArithmeticExpr.Operator.SHR || op == ArithmeticExpr.Operator.USHR) {
            leftType = rt;
            rightType = Type.INT_TYPE;
        } else {
            leftType = rightType = rt;
        }
        insns.add(new VarInsnNode(TypeUtils.getVariableLoadOpcode(t1), 0));
        cast(insns, t1, leftType);
        insns.add(new VarInsnNode(TypeUtils.getVariableLoadOpcode(t2), t1.getSize()));
        cast(insns, t2, rightType);
        int opcode;
        switch(op) {
            case ADD:
                opcode = TypeUtils.getAddOpcode(rt);
                break;
            case SUB:
                opcode = TypeUtils.getSubtractOpcode(rt);
                break;
            case MUL:
                opcode = TypeUtils.getMultiplyOpcode(rt);
                break;
            case DIV:
                opcode = TypeUtils.getDivideOpcode(rt);
                break;
            case REM:
                opcode = TypeUtils.getRemainderOpcode(rt);
                break;
            case SHL:
                opcode = TypeUtils.getBitShiftLeftOpcode(rt);
                break;
            case SHR:
                opcode = TypeUtils.bitShiftRightOpcode(rt);
                break;
            case USHR:
                opcode = TypeUtils.getBitShiftRightUnsignedOpcode(rt);
                break;
            case OR:
                opcode = TypeUtils.getBitOrOpcode(rt);
                break;
            case AND:
                opcode = TypeUtils.getBitAndOpcode(rt);
                break;
            case XOR:
                opcode = TypeUtils.getBitXorOpcode(rt);
                break;
            default:
                throw new RuntimeException();
        }
        insns.add(new InsnNode(opcode));
        insns.add(new InsnNode(TypeUtils.getReturnOpcode(rt)));
        m.node.instructions = insns;
    }
    return buildBridge(m);
}
Also used : VarInsnNode(org.objectweb.asm.tree.VarInsnNode) JumpInsnNode(org.objectweb.asm.tree.JumpInsnNode) InsnNode(org.objectweb.asm.tree.InsnNode) Type(org.objectweb.asm.Type) MethodNode(org.mapleir.asm.MethodNode) InsnList(org.objectweb.asm.tree.InsnList) VarInsnNode(org.objectweb.asm.tree.VarInsnNode)

Example 8 with MethodNode

use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.

the class DetectIrreducibleFlowPass method accept.

@Override
public PassResult accept(PassContext pcxt) {
    AnalysisContext cxt = pcxt.getAnalysis();
    for (Entry<MethodNode, ControlFlowGraph> e : cxt.getIRCache().entrySet()) {
        MethodNode mn = e.getKey();
        ControlFlowGraph cfg = e.getValue();
        if (!GraphUtils.isReducibleGraph(cfg, cfg.getEntries().iterator().next())) {
            return PassResult.with(pcxt, this).fatal(new IllegalStateException(String.format("%s contains irreducible loop", mn))).make();
        }
    }
    return PassResult.with(pcxt, this).finished().make();
}
Also used : MethodNode(org.mapleir.asm.MethodNode) ControlFlowGraph(org.mapleir.ir.cfg.ControlFlowGraph) AnalysisContext(org.mapleir.context.AnalysisContext)

Example 9 with MethodNode

use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.

the class FieldRenamerPass method accept.

@Override
public PassResult accept(PassContext pcxt) {
    AnalysisContext cxt = pcxt.getAnalysis();
    Map<FieldNode, String> remapped = new HashMap<>();
    // int totalFields = 0;
    // int i = RenamingUtil.computeMinimum(totalFields);
    ApplicationClassSource source = cxt.getApplication();
    int i = RenamingUtil.numeric("aaaaa");
    for (ClassNode cn : source.iterate()) {
        // totalFields += cn.fields.size();
        for (FieldNode fn : cn.getFields()) {
            remapped.put(fn, RenamingUtil.createName(i++));
        }
    }
    InvocationResolver resolver = cxt.getInvocationResolver();
    for (ClassNode cn : source.iterate()) {
        for (MethodNode m : cn.getMethods()) {
            ControlFlowGraph cfg = cxt.getIRCache().getFor(m);
            for (BasicBlock b : cfg.vertices()) {
                for (Stmt stmt : b) {
                    if (stmt.getOpcode() == Opcode.FIELD_STORE) {
                        FieldStoreStmt fs = (FieldStoreStmt) stmt;
                        FieldNode f = resolver.findField(fs.getOwner(), fs.getName(), fs.getDesc(), fs.getInstanceExpression() == null);
                        if (f != null) {
                            if (remapped.containsKey(f)) {
                                fs.setName(remapped.get(f));
                            } else if (mustMark(source, f.getOwner())) {
                                System.err.println("  no remap for " + f + ", owner: " + f.getOwner());
                            }
                        } else {
                            if (mustMark(source, fs.getOwner())) {
                                System.err.println("  can't resolve field(set): " + fs.getOwner() + "." + fs.getName() + " " + fs.getDesc() + ", " + (fs.getInstanceExpression() == null));
                            }
                        }
                    }
                    for (Expr e : stmt.enumerateOnlyChildren()) {
                        if (e.getOpcode() == Opcode.FIELD_LOAD) {
                            FieldLoadExpr fl = (FieldLoadExpr) e;
                            FieldNode f = resolver.findField(fl.getOwner(), fl.getName(), fl.getDesc(), fl.getInstanceExpression() == null);
                            if (f != null) {
                                if (remapped.containsKey(f)) {
                                    fl.setName(remapped.get(f));
                                } else if (mustMark(source, f.getOwner())) {
                                    System.err.println("  no remap for " + f + ", owner: " + f.getOwner());
                                }
                            } else {
                                if (mustMark(source, fl.getOwner())) {
                                    System.err.println("  can't resolve field(get): " + fl.getOwner() + "." + fl.getName() + " " + fl.getDesc() + ", " + (fl.getInstanceExpression() == null));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    for (Entry<FieldNode, String> e : remapped.entrySet()) {
        e.getKey().node.name = e.getValue();
    }
    System.out.printf("  Renamed %d fields.%n", remapped.size());
    return PassResult.with(pcxt, this).finished().make();
}
Also used : FieldStoreStmt(org.mapleir.ir.code.stmt.FieldStoreStmt) ClassNode(org.mapleir.asm.ClassNode) FieldLoadExpr(org.mapleir.ir.code.expr.FieldLoadExpr) FieldNode(org.mapleir.asm.FieldNode) HashMap(java.util.HashMap) BasicBlock(org.mapleir.ir.cfg.BasicBlock) AnalysisContext(org.mapleir.context.AnalysisContext) FieldStoreStmt(org.mapleir.ir.code.stmt.FieldStoreStmt) Stmt(org.mapleir.ir.code.Stmt) ApplicationClassSource(org.mapleir.app.service.ApplicationClassSource) MethodNode(org.mapleir.asm.MethodNode) Expr(org.mapleir.ir.code.Expr) FieldLoadExpr(org.mapleir.ir.code.expr.FieldLoadExpr) ControlFlowGraph(org.mapleir.ir.cfg.ControlFlowGraph) InvocationResolver(org.mapleir.app.service.InvocationResolver)

Example 10 with MethodNode

use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.

the class DefaultInvocationResolver method computeVTable.

private void computeVTable(ClassNode c) {
    if (c == null) {
        throw new NullPointerException();
    }
    /* only visit each class once */
    if (hasVisited(c)) {
        return;
    }
    /* ensure parents loaded */
    ClassNode superKlass = null;
    /* if the super class is null it means we're at object and so
		 * we don't even have to consider interfaces: as we stop
		 * here */
    if (c.node.superName != null) {
        computeVTable(superKlass = app.findClassNode(c.node.superName));
        for (String i : c.node.interfaces) {
            computeVTable(app.findClassNode(i));
        }
    }
    /* the general plan of attack here is:
		 *  1. add our own methods.
		 *  2. merge without our super class methods.
		 *  3. merge and resolve with our interfaces. */
    /* we build our local tables for the current class. it's
		 * important to keep these separate from what will become the
		 * completely hierarchy sensitive lookup tables later and so
		 * technically should be immutable after the loop but we can
		 * ignore this as long as we do not modify them later. */
    Map<Selector, MethodNode> thisMethodSet = new HashMap<>();
    Map<Selector, MethodNode> thisAbstractSet = new HashMap<>();
    for (MethodNode m : c.getMethods()) {
        /* we can easily resolve these as they come
			 * so we don't even need them in the table. */
        if (Modifier.isStatic(m.node.access)) {
            continue;
        }
        /* immutable lookup key */
        Selector s = new Selector(m.getName(), m.getDesc());
        /* store our local declarations. */
        if (Modifier.isAbstract(m.node.access)) {
            putOrThrow(thisAbstractSet, s, m);
        } else {
            putOrThrow(thisMethodSet, s, m);
        }
    }
    if (debugLevel >= 2) {
        LOGGER.debug("Class: " + c);
        LOGGER.debug("  super: " + c.node.superName);
        LOGGER.debug("  interfaces: " + c.node.interfaces.toString());
        LOGGER.debug(" implSet: ");
        print(thisMethodSet);
        LOGGER.debug(" absSet: ");
        print(thisAbstractSet);
    }
    /* Store the local results as the global as in the case of
		 * java/lang/Object we don't have a super class and so the
		 * ensuing analysis (below) is never executed. It's important
		 * to note that we shouldn't be looking in the global vtable
		 * for a given class while processing that class; the local
		 * maps/vtable values are used but not the field
		 * references. */
    concreteVTables.put(c, thisMethodSet);
    abstractVTables.put(c, thisAbstractSet);
    if (superKlass != null) {
        if (!hasVisited(superKlass)) {
            throw new IllegalStateException(String.format("Parent of %s, %s is not initialised", c, superKlass));
        }
        Map<Selector, MethodNode> globalCVT = new HashMap<>();
        Map<Selector, MethodNode> globalAVT = new HashMap<>();
        /* inherit all super class methods */
        globalCVT.putAll(concreteVTables.get(superKlass));
        globalAVT.putAll(abstractVTables.get(superKlass));
        assertIntersection(thisAbstractSet.entrySet(), thisMethodSet.entrySet(), Collections.emptySet());
        /* (1) and (2) 
			 * NOTE: thisAbstractSet and thisMethodSet should
			 *       intersect to give the null set. */
        for (Selector s : thisAbstractSet.keySet()) {
            globalCVT.remove(s);
        }
        for (Selector s : thisMethodSet.keySet()) {
            globalAVT.remove(s);
        }
        /* we shouldn't ever get merge errors from considering the
			 * current class with it's super. (this can happen with
			 * interfaces, however) */
        assertIntersection(globalAVT.entrySet(), globalCVT.entrySet(), Collections.emptySet());
        /* add our own declarations to the tables. this could possibly
			 * override methods from the super class and we're happy
			 * about this. */
        globalCVT.putAll(thisMethodSet);
        globalAVT.putAll(thisAbstractSet);
        assertIntersection(globalAVT.entrySet(), globalCVT.entrySet(), Collections.emptySet());
        concreteVTables.put(c, globalCVT);
        abstractVTables.put(c, globalAVT);
        if (debugLevel >= 3) {
            LOGGER.debug(" globalCVT: ");
            print(globalCVT);
            LOGGER.debug(" globalAVT: ");
            print(globalAVT);
        }
        NullPermeableHashMap<Selector, Set<MethodNode>> mergeMap = new NullPermeableHashMap<>(HashSet::new);
        if (debugLevel >= 2) {
            LOGGER.debug(" process interfaces:");
        }
        for (String i : c.node.interfaces) {
            if (debugLevel >= 2) {
                LOGGER.debug("  " + i);
            }
            ClassNode interfaceKlass = app.findClassNode(i);
            /* An abstract interface method cannot kill
				 * a concrete class implementation of the method
				 * from the super class. However, if the reaching
				 * definition is a default implementation from
				 * another interface which is a superclass of the
				 * current interface, then the reaching definition
				 * in the current interface is an abstract method
				 * and so that is propagated into the implementor.
				 * 
				 * If for example the super interface defines the
				 * abstract method and the sub interface has a
				 * default implementation and we implement the 
				 * default interface, the default method will become
				 * the reaching definition for the current class,
				 * not the abstract one.
				 * 
				 * If a class that implements a default method
				 * already has a reaching definition in the parent,
				 * the subinterface default method takes priority.*/
            add(concreteVTables.get(interfaceKlass), mergeMap);
            add(abstractVTables.get(interfaceKlass), mergeMap);
        }
        if (debugLevel >= 3) {
            LOGGER.debug("   mergeMap:");
            printMap(mergeMap);
        }
        for (Entry<Selector, Set<MethodNode>> e : mergeMap.entrySet()) {
            Selector selector = e.getKey();
            Set<MethodNode> conflictingMethods = e.getValue();
            MethodNode resolve;
            if (conflictingMethods.size() > 1) {
                if (debugLevel >= 2) {
                    LOGGER.debug("    conflicts: " + conflictingMethods.size());
                    LOGGER.debug("      " + selector);
                    for (MethodNode m : conflictingMethods) {
                        LOGGER.debug("      ^" + m);
                    }
                }
                /* if we have any sort of declaration, we can easily say that
					 * that is the target without having to check the hierarchy
					 * and globalCVT rules. 
					 * 
					 * 05/10/17: globalCVT should contain super defs as well or ours
					 *           so surely we use this as it doesn't contain any
					 *           abstracts?*/
                if (globalCVT.containsKey(selector)) {
                    resolve = globalCVT.get(selector);
                    if (debugLevel >= 2) {
                        LOGGER.debug("    (1)resolved to " + resolve);
                    }
                } else if (thisAbstractSet.containsKey(selector)) {
                    resolve = thisAbstractSet.get(selector);
                    if (debugLevel >= 2) {
                        LOGGER.debug("    (2)resolved to " + resolve);
                    }
                } else {
                    /* here is where it gets tricky. */
                    /* 05/10/17: weird bug? example:
						 *    javax/swing/LayoutComparator implements Comparable
						 *    Comparable extends Object by OUR own definition.
						 *    Comparable reabstracts equals(Object) but LayoutComparator
						 *    extends Object implicitly, so the Object definition is
						 *    reaching unless it is redefined in LayoutComparator.
						 *    Note that you can't have default methods for Object methods.
						 *    
						 *    So do we check for it here or inherit Objects methods before we
						 *    do anything else?
						 *    solution: use global vtable merge interface merges above?
						 */
                    Collection<MethodNode> contenders = getMaximallySpecific(conflictingMethods);
                    if (contenders.isEmpty()) {
                        throw new IllegalStateException();
                    }
                    if (contenders.size() == 1) {
                        /* design decision: if there is only 1 contender but multiple
							 * conflicting methods, all of the conflicting methods are
							 * defaults, then we have a clear target method, but if any of
							 * them are abstract, we are actually propagating a
							 * reabstraction through this class, so we can insert a
							 * miranda. */
                        boolean anyAbstract = false;
                        for (MethodNode m : contenders) {
                            anyAbstract |= Modifier.isAbstract(m.node.access);
                        }
                        if (anyAbstract) {
                            /* valid: miranda (reabstraction) */
                            resolve = null;
                        } else {
                            resolve = contenders.iterator().next();
                        }
                        if (debugLevel >= 2) {
                            LOGGER.debug("    (3)resolved to " + resolve);
                        }
                    } else {
                        if (Modifier.isAbstract(c.node.access)) {
                            boolean allAbstract = true;
                            for (MethodNode m : contenders) {
                                allAbstract &= Modifier.isAbstract(m.node.access);
                            }
                            if (allAbstract) {
                                /* valid: miranda */
                                resolve = null;
                            } else {
                                /* require a declaration but we don't
									 * have one -> die. */
                                throw new IllegalStateException(String.format("Method %s is ambiguous in %s, candidates:%s", selector, c, conflictingMethods));
                            }
                        } else {
                            /* require a declaration but we don't
								 * have one -> die. */
                            throw new IllegalStateException(String.format("Method %s is ambiguous in %s, candidates:%s", selector, c, conflictingMethods));
                        }
                    }
                }
            } else {
                /* no conflicts, so we can just choose the resolve method as
					 * the target method from one of the tables. */
                if (globalCVT.containsKey(selector)) {
                    resolve = globalCVT.get(selector);
                } else if (thisAbstractSet.containsKey(selector)) {
                    /* thisAbstractSet instead of the global one because
						 * these conflicts would make things confusing if
						 * the global table was constantly changing. */
                    resolve = thisAbstractSet.get(selector);
                } else {
                    /* no conflict but the current class just does not implement
						 * the method itself. we could either create a miranda here or
						 * reference to method. we will do the latter. */
                    // size == 1
                    resolve = conflictingMethods.iterator().next();
                }
            }
            /* delete from both so we can do a clean add after. */
            globalAVT.remove(selector);
            globalCVT.remove(selector);
            if (resolve == null && !Modifier.isAbstract(c.node.access)) {
                throw new IllegalStateException(String.format("Miranda %s in non abstract class %s", conflictingMethods, c));
            }
            if (resolve == null) {
                // TODO: sigs?
                resolve = new MethodNode(new org.objectweb.asm.tree.MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, selector.name, selector.desc, null, getExceptionClasses(conflictingMethods)), c);
                c.addMethod(resolve);
                if (debugLevel >= 2) {
                    LOGGER.debug("  generated miranda " + resolve);
                }
            }
            if (Modifier.isAbstract(resolve.node.access)) {
                globalAVT.put(selector, resolve);
            } else {
                globalCVT.put(selector, resolve);
            }
        }
        assertIntersection(concreteVTables.get(c).entrySet(), abstractVTables.get(c).entrySet(), Collections.emptySet());
        if (debugLevel >= 2) {
            LOGGER.debug(" cvtable: ");
            print(concreteVTables.get(c));
            LOGGER.debug(" avtable: ");
            print(abstractVTables.get(c));
        }
    }
}
Also used : ClassNode(org.mapleir.asm.ClassNode) NullPermeableHashMap(org.mapleir.stdlib.collections.map.NullPermeableHashMap) MethodNode(org.mapleir.asm.MethodNode) NullPermeableHashMap(org.mapleir.stdlib.collections.map.NullPermeableHashMap)

Aggregations

MethodNode (org.mapleir.asm.MethodNode)36 ClassNode (org.mapleir.asm.ClassNode)21 ControlFlowGraph (org.mapleir.ir.cfg.ControlFlowGraph)17 AnalysisContext (org.mapleir.context.AnalysisContext)13 ApplicationClassSource (org.mapleir.app.service.ApplicationClassSource)8 Expr (org.mapleir.ir.code.Expr)8 Stmt (org.mapleir.ir.code.Stmt)7 BasicBlock (org.mapleir.ir.cfg.BasicBlock)6 Type (org.objectweb.asm.Type)6 InvocationResolver (org.mapleir.app.service.InvocationResolver)5 IRCache (org.mapleir.context.IRCache)5 ControlFlowGraphBuilder (org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder)5 InsnList (org.objectweb.asm.tree.InsnList)5 InsnNode (org.objectweb.asm.tree.InsnNode)5 JumpInsnNode (org.objectweb.asm.tree.JumpInsnNode)5 VarInsnNode (org.objectweb.asm.tree.VarInsnNode)5 HashSet (java.util.HashSet)4 SimpleApplicationContext (org.mapleir.app.client.SimpleApplicationContext)4 BasicAnalysisContext (org.mapleir.context.BasicAnalysisContext)4 IPass (org.mapleir.deob.IPass)4