@Nullable private BuilderInstruction getFirstNonNop(int startIndex) { for (int i = startIndex; i < instructionList.size() - 1; i++) { BuilderInstruction instruction = instructionList.get(i).instruction; assert instruction != null; if (instruction.getOpcode() != Opcode.NOP) { return instruction; } } return null; }
private void removeMoveResultIfNecessary(int address) { BuilderInstruction instruction = manipulator.getInstruction(address); int nextAddress = address + instruction.getCodeUnits(); BuilderInstruction nextInstr = manipulator.getInstruction(nextAddress); String opName = nextInstr.getOpcode().name; if (opName.startsWith("move-result")) { manipulator.removeInstruction(nextAddress); } }
offset += (bi.getFormat().size / 2); idx++;
private void setupInstruction(BuilderInstruction instruction, Opcode opcode) { when(location.getInstruction()).thenReturn(instruction); when(instruction.getLocation()).thenReturn(location); when(instruction.getCodeUnits()).thenReturn(0); when(instruction.getOpcode()).thenReturn(opcode); }
private static int[] buildTerminatingAddresses(List<BuilderInstruction> instructions) { TIntList addresses = new TIntLinkedList(); for (BuilderInstruction instruction : instructions) { int address = instruction.getLocation().getCodeAddress(); Opcode op = instruction.getOpcode(); switch (op) { case RETURN_VOID: case RETURN: case RETURN_WIDE: case RETURN_OBJECT: case THROW: break; default: continue; } addresses.add(address); } return addresses.toArray(); }
public void addInstruction(@Nonnull BuilderInstruction instruction) { MethodLocation last = instructionList.get(instructionList.size() - 1); last.instruction = instruction; instruction.location = last; int nextCodeAddress = last.codeAddress + instruction.getCodeUnits(); instructionList.add(new MethodLocation(null, nextCodeAddress, instructionList.size())); this.fixInstructions = true; }
boolean isNext = child.getAddress() == original.getLocation().getCodeAddress() + original.getCodeUnits(); peepAddresses.add(address); if (isNext) {
public static MethodLocation[] getLocations(BuilderInstruction... instructions) { MethodLocation[] locations = new MethodLocation[instructions.length]; for (int i = 0; i < locations.length; i++) { locations[i] = instructions[i].getLocation(); } return locations; }
int codeAddress = first.codeAddress + first.instruction.getCodeUnits(); for (int i = index1 + 1; i <= index2; i++) { MethodLocation location = instructionList.get(i); codeAddress += location.instruction.getCodeUnits();
@Override public Op create(MethodLocation location, TIntObjectMap<MethodLocation> addressToLocation, VirtualMachine vm) { BuilderInstruction instruction = (BuilderInstruction) location.getInstruction(); int address = instruction.getLocation().getCodeAddress(); int returnAddress = address + instruction.getCodeUnits(); MethodLocation returnLocation = addressToLocation.get(returnAddress); int branchOffset = ((OffsetInstruction) instruction).getCodeOffset(); int childAddress = address + branchOffset; MethodLocation child = addressToLocation.get(childAddress); Instruction31t instr = (Instruction31t) location.getInstruction(); int register = instr.getRegisterA(); return new FillArrayDataOp(location, child, returnLocation, register); } }
private BuilderInstruction buildInstruction22t(Opcode opcode, int offset) { BuilderInstruction instruction = mock(BuilderInstruction.class, withSettings().extraInterfaces(Instruction22t.class)); when(location.getInstruction()).thenReturn(instruction); when(instruction.getLocation()).thenReturn(location); when(instruction.getCodeUnits()).thenReturn(0); when(instruction.getOpcode()).thenReturn(opcode); when(((Instruction22t) instruction).getRegisterA()).thenReturn(ARG1_REGISTER); when(((Instruction22t) instruction).getRegisterB()).thenReturn(ARG2_REGISTER); when(((Instruction22t) instruction).getCodeOffset()).thenReturn(offset); return instruction; }
@Override public Op create(MethodLocation location, TIntObjectMap<MethodLocation> addressToLocation, VirtualMachine vm) { BuilderInstruction instruction = (BuilderInstruction) location.getInstruction(); int address = instruction.getLocation().getCodeAddress(); int branchOffset = ((OffsetInstruction) instruction).getCodeOffset(); int targetAddress = address + branchOffset; MethodLocation child = Utils.getNextLocation(location, addressToLocation); MethodLocation target = addressToLocation.get(targetAddress); String opName = instruction.getOpcode().name; IfType ifType = getIfType(opName); int register1 = ((OneRegisterInstruction) instruction).getRegisterA(); if (instruction instanceof Instruction22t) { // if-* vA, vB, :label Instruction22t instr = (Instruction22t) location.getInstruction(); return new IfOp(location, child, ifType, target, register1, instr.getRegisterB()); } else { // if-*z vA, vB, :label (Instruction 21t) return new IfOp(location, child, ifType, target, register1); } }
public void addNode(ExecutionNode node) { MethodLocation location = node.getOp().getInstruction().getLocation(); locationToNodePile.get(location).add(node); }
@Nullable private MethodLocation findSwitchForPayload(@Nonnull MethodLocation payloadLocation) { MethodLocation location = payloadLocation; MethodLocation switchLocation = null; do { for (Label label : location.getLabels()) { if (label instanceof SwitchPayloadReferenceLabel) { if (switchLocation != null) { throw new IllegalStateException("Multiple switch instructions refer to the same payload. " + "This is not currently supported. Please file a bug :)"); } switchLocation = ((SwitchPayloadReferenceLabel) label).switchLocation; } } // A switch instruction can refer to the payload instruction itself, or to a nop before the payload // instruction. // We need to search for all occurrences of a switch reference, so we can detect when multiple switch // statements refer to the same payload // TODO: confirm that it could refer to the first NOP in a series of NOPs preceding the payload if (location.index == 0) { return switchLocation; } location = instructionList.get(location.index - 1); if (location.instruction == null || location.instruction.getOpcode() != Opcode.NOP) { return switchLocation; } } while (true); }
void peepClassForName() { List<Integer> peepAddresses = addresses.stream().filter(this::canPeepClassForName).collect(Collectors.toList()); if (0 == peepAddresses.size()) { return; } madeChanges = true; peepCount += peepAddresses.size(); Collections.sort(peepAddresses, Collections.reverseOrder()); for (int address : peepAddresses) { BuilderInstruction original = manipulator.getInstruction(address); int nextAddress = address + original.getCodeUnits(); if (addresses.contains(nextAddress)) { BuilderInstruction nextInstruction = manipulator.getInstruction(nextAddress); if (nextInstruction.getOpcode().name.startsWith("move-result")) { // There is a move-result after the instruction being replaced. "Deal" with it. manipulator.removeInstruction(nextAddress); } } BuilderInstruction replacement = buildClassForNameReplacement(address); manipulator.replaceInstruction(address, replacement); } }
public void replaceInstruction(int index, @Nonnull BuilderInstruction replacementInstruction) { if (index >= instructionList.size() - 1) { throw new IndexOutOfBoundsException(); } MethodLocation replaceLocation = instructionList.get(index); replacementInstruction.location = replaceLocation; BuilderInstruction old = replaceLocation.instruction; assert old != null; old.location = null; replaceLocation.instruction = replacementInstruction; // TODO: factor out index/address fix up loop int codeAddress = replaceLocation.codeAddress + replaceLocation.instruction.getCodeUnits(); for (int i = index + 1; i < instructionList.size(); i++) { MethodLocation location = instructionList.get(i); location.codeAddress = codeAddress; Instruction instruction = location.getInstruction(); if (instruction != null) { codeAddress += instruction.getCodeUnits(); } else { assert i == instructionList.size() - 1; } } this.fixInstructions = true; }
@Before public void setUp() { vm = mock(VirtualMachine.class); classManager = mock(ClassManager.class); when(vm.getClassManager()).thenReturn(classManager); location = mock(MethodLocation.class); instruction = mock(BuilderInstruction.class, withSettings().extraInterfaces(SwitchPayload.class)); when(location.getInstruction()).thenReturn(instruction); when(location.getCodeAddress()).thenReturn(ADDRESS); when(instruction.getLocation()).thenReturn(location); when(instruction.getCodeUnits()).thenReturn(0); addressToLocation = new TIntObjectHashMap<MethodLocation>(); addressToLocation.put(ADDRESS, location); opFactory = new SwitchPayloadOpFactory(); }
private static void test(Object[][] expected, ExecutionGraphManipulator manipulator) { for (Object[] ex : expected) { int address = (Integer) ex[0]; BuilderInstruction actualInstruction = manipulator.getInstruction(address); Opcode expectedOpcode = (Opcode) ex[1]; assertEquals(expectedOpcode, actualInstruction.getOpcode()); Object[][][] exChildren = (Object[][][]) ex[2]; List<ExecutionNode> actualNodePile = manipulator.getNodePile(address); assertEquals(expectedOpcode.name + " @" + address + " node pile size", exChildren.length, actualNodePile.size()); for (int i = 0; i < exChildren.length; i++) { ExecutionNode actualNode = actualNodePile.get(i); List<ExecutionNode> childNodes = actualNode.getChildren(); BuilderInstruction[] children = new BuilderInstruction[childNodes.size()]; for (int j = 0; j < children.length; j++) { children[j] = childNodes.get(j).getOp().getInstruction(); } assertEquals(expectedOpcode.name + " @" + address + " children size", exChildren[i].length, children.length); for (int j = 0; j < exChildren[i].length; j++) { assertEquals(expectedOpcode.name + " @" + address + " child address", (int) exChildren[i][j][0], children[j].getLocation().getCodeAddress()); assertEquals(expectedOpcode.name + " @" + address + " child opcode", exChildren[i][j][1], children[j].getOpcode()); } } } }
public int getCodeUnits() { return getFormat().size / 2; }
public void addInstruction(MethodLocation location, BuilderInstruction instruction) { int index = location.getIndex(); implementation.addInstruction(index, instruction); MethodLocation newLocation = instruction.getLocation(); MethodLocation oldLocation = implementation.getInstructions().get(index + 1).getLocation(); try { java.lang.reflect.Method m = MethodLocation.class.getDeclaredMethod("mergeInto", MethodLocation.class); m.setAccessible(true); m.invoke(oldLocation, newLocation); } catch (Exception e) { log.error("Error invoking MethodLocation.mergeInto(). Wrong dexlib version?", e); } rebuildGraph(); }