public String toString(boolean onlyPeekCachedRegisters) { StringBuilder sb = new StringBuilder(); int localsCount = getRegisterCount() - getParameterCount(); sb.append("params: ").append(parameterCount).append(", "); sb.append("locals: ").append(localsCount).append('\n'); StringBuilder ctx = new StringBuilder(); for (int register = 0; register < getRegisterCount(); register++) { if (onlyPeekCachedRegisters && !hasRegister(register)) { continue; boolean isLocal = register < getParameterStart(); if (!isLocal) { ctx.append("(p").append(register - getParameterStart()).append(')'); HeapItem item = peekRegister(register); ctx.append(": ").append(item).append('\n'); if (onlyPeekCachedRegisters && hasRegister(ResultRegister)) { ctx.append("result: ").append(peekRegister(ResultRegister)).append('\n'); if (onlyPeekCachedRegisters && hasRegister(ReturnRegister)) { ctx.append("return: ").append(peekRegister(ReturnRegister)).append('\n');
private static Set<Integer> getNormalRegistersAssigned(MethodState mState) { Set<Integer> assigned = new HashSet<>(); for (int register : mState.getRegistersAssigned()) { if (register < 0) { continue; } assigned.add(register); } for (int i = 0; i < mState.getParameterCount(); i++) { int parameterRegister = mState.getParameterStart() + i; assigned.remove(parameterRegister); } return assigned; }
private boolean allArgumentsKnown(MethodState mState) { for (int parameterRegister = mState.getParameterStart(); parameterRegister < mState.getRegisterCount(); ) { HeapItem item = mState.peekParameter(parameterRegister); if (item.isUnknown()) { return false; } String type = item.getType(); parameterRegister += Utils.getRegisterSize(type); } return true; }
private static void moveException(MethodState mState, int toRegister) { HeapItem exception = mState.peekExceptionRegister(); mState.assignRegister(toRegister, exception); }
@Override public void execute(ExecutionNode node, MethodState mState) { HeapItem item = mState.readRegister(register); // Mark register as assigned because next op will be payload, and it uses assigned register in this op to // determine target register for payload. mState.assignRegister(register, item); // It needs to know return address when finished since payload ops do not continue to next address. mState.setPseudoInstructionReturnLocation(returnLocation); }
@Override public void execute(ExecutionNode node, MethodState mState) { // Use result register to store value to compare. Comparison is handled by payload op. HeapItem item = mState.readRegister(register); mState.assignResultRegister(item); // If switch "falls through", will need the immediate op after this. mState.setPseudoInstructionReturnLocation(child); }
private static void moveRegister(MethodState mState, int toRegister, int fromRegister) { HeapItem item = mState.readRegister(fromRegister); mState.assignRegister(toRegister, item); }
@Override public void execute(VirtualMachine vm, Op op, ExecutionContext context) { MethodState mState = context.getMethodState(); String argumentType = mState.peekParameter(0).getType(); VirtualType virtualType = vm.getClassManager().getVirtualType(argumentType); try { Class<?> value = vm.getClassLoader().loadClass(virtualType.getBinaryName()); mState.assignReturnRegister(value, RETURN_TYPE); } catch (ClassNotFoundException e) { throw new RuntimeException("Class not found: " + argumentType, e); } }
@Override public void execute(ExecutionNode node, MethodState mState) { MethodState parent = mState.getParent(); int targetRegister = parent.getRegistersAssigned()[0]; // Peek rather than read. This pseudo-instruction shouldn't count as an actual usage for the optimizer. HeapItem arrayItem = mState.peekRegister(targetRegister); if (!arrayItem.isUnknown()) { Object array = arrayItem.getValue(); Class<?> expectedClass = array.getClass().getComponentType(); for (int i = 0; i < arrayElements.size(); i++) { Number number = arrayElements.get(i); Object value = getProperValue(number, expectedClass); Array.set(array, i, value); } // Poke rather than assign for the optimizer. mState.pokeRegister(targetRegister, arrayItem); } MethodLocation returnLocation = mState.getParent().getPseudoInstructionReturnInstruction(); node.setChildLocations(returnLocation); }
HeapItem originalInstanceItem = callerMethodState.peekRegister(parameterRegisters[0]); HeapItem newInstanceItem = calleeContext.getMethodState().peekParameter(0); if (originalInstanceItem.getValue() != newInstanceItem.getValue()) { callerMethodState.assignRegisterAndUpdateIdentities(parameterRegisters[0], newInstanceItem); } else { boolean isMutable = !vm.getConfiguration().isImmutable(newInstanceItem.getType()); callerMethodState.assignRegister(parameterRegisters[0], newInstanceItem); HeapItem returnItem = calleeContext.getMethodState().readReturnRegister(); callerMethodState.assignResultRegister(returnItem);
private static void testException(String methodDescriptor, Class<?> exceptionClass, String exceptionMessage, VMState initial) { ExecutionGraph graph = VMTester.execute(CLASS_NAME, methodDescriptor, initial); HeapItem item = graph.getTerminatingRegisterConsensus(0); Assert.assertEquals(exceptionClass, item.getValue().getClass()); Assert.assertEquals(ClassNameUtils.toInternal(exceptionClass), item.getType()); Assert.assertEquals(exceptionMessage, ((Throwable) item.getValue()).getMessage()); assertFalse("Should not reach next instruction in non-exception execution path", graph.wasAddressReached(1)); MethodState mState = graph.getNodePile(0).get(0).getContext().getMethodState(); Assert.assertEquals(0, mState.getRegistersAssigned().length); }
public void assignRegister(int register, HeapItem item) { assignRegister(register, item, METHOD_HEAP); }
public HeapItem peekExceptionRegister() { return peekRegister(ExceptionRegister); }
for (int i = 0; i < dimensionRegisters.length; i++) { int register = dimensionRegisters[i]; HeapItem item = mState.readRegister(register); mState.assignResultRegister(HeapItem.newUnknown("[I")); } else { mState.assignResultRegister(dimensions, "[I");
private void assignCalleeMethodArguments(MethodState callerState, MethodState calleeState) { int parameterRegister = calleeState.getParameterStart(); for (int i = 0; i < parameterRegisters.length; i++) { int callerRegister = parameterRegisters[i]; HeapItem item = callerState.readRegister(callerRegister); String parameterType = analyzedParameterTypes[i]; Object value = item.getValue(); if (item.isPrimitive() && !item.isUnknown()) { boolean hasNullByteValue = item.getType().equals("I") && value instanceof Number && item.asInteger() == 0; if (hasNullByteValue && ClassNameUtils.isObject(parameterType)) { value = null; } else { // The "I" type may actually be "S", "B", "C", etc. Cast to the given parameter type. value = Utils.castToPrimitive(value, parameterType); } } HeapItem parameterItem = new HeapItem(value, parameterType); calleeState.assignParameter(parameterRegister, parameterItem); parameterRegister += Utils.getRegisterSize(parameterType); } }
public static MethodState forMethod(ExecutionContext context) { VirtualMethod method = context.getMethod(); int registerCount = method.getRegisterCount(); List<String> parameterTypes = method.getParameterTypeNames(); int parameterSize = Utils.getRegisterSize(parameterTypes); MethodState mState = new MethodState(context, registerCount, parameterTypes.size(), parameterSize); int firstParameter = mState.getParameterStart(); int parameterRegister = firstParameter; for (String type : parameterTypes) { HeapItem item; if (parameterRegister == firstParameter && !method.isStatic() && method.getName().equals("<init>")) { UninitializedInstance instance = new UninitializedInstance(method.getDefiningClass()); item = new HeapItem(instance, type); } else { item = HeapItem.newUnknown(type); } mState.assignParameter(parameterRegister, item); parameterRegister += Utils.getRegisterSize(type); } return mState; }
@SuppressWarnings({ "unchecked" }) private Object invokeEnumInit(MethodState mState, String name, ClassLoader classLoader) throws ClassNotFoundException { /* * Enums can't be instantiated by calling newInstance() on the constructor, * even with setAccessible(true). It fails with InstantiationException. * http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9 */ HeapItem instance = mState.peekParameter(mState.getParameterStart()); String enumType = ClassNameUtils.internalToSource(instance.getType()); Class<? extends Enum> enumClass = (Class<? extends Enum>) classLoader.loadClass(enumType); try { return Enum.valueOf(enumClass, name); } catch (IllegalArgumentException e) { enumAnalyzer.analyze(enumClass); name = enumAnalyzer.getObfuscatedName(name); return Enum.valueOf(enumClass, name); } }
callerContext.getMethodState().assignResultRegister(consensus); } else { if (methodSignature.contains(";-><init>(")) { int calleeInstanceRegister = calleeContext.getMethodState().getParameterStart(); HeapItem newInstance = graph.getTerminatingRegisterConsensus(calleeInstanceRegister); int instanceRegister = parameterRegisters[0]; callerContext.getMethodState().assignRegisterAndUpdateIdentities(instanceRegister, newInstance);
private static void executeParameterLogicWithKnownParameter(int parameterValue) throws VirtualMachineException { String methodSignature = "Lorg/cf/demosmali/Main;->parameterLogic(I)I"; ExecutionContext context = vm.spawnRootContext(methodSignature); MethodState mState = context.getMethodState(); // This method has 4 locals (r0, r1, r2, r3). Since it's virtual (not static) the first parameter (p0 or r4) // is the 'this' reference, or contains a reference to the object. // First method parameter starts at r5, or parameter start + 1. mState.assignParameter(mState.getParameterStart() + 1, parameterValue, "I"); ExecutionGraph graph = vm.execute(methodSignature, context); HeapItem item = graph.getTerminatingRegisterConsensus(MethodState.ReturnRegister); System.out.println("With context, returns " + parameterValue + ": " + item); }
public @Nullable HeapItem peekRegister(int register) { if (register == MethodState.ResultRegister) { if (!hasRegister(register, METHOD_HEAP)) { if (getParent() != null && !getParent().hasRegister(register, METHOD_HEAP)) { // ResultRegister can only be read by the instruction immediately after it's set. // It's not in this instruction or its parent, so it effectively doesn't exist. // log.warn("Attempting to read result register but it's not in current or parent context! // Returning null."); return null; } } } return peekRegister(register, METHOD_HEAP); }