1959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle/* 2959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Copyright (C) 2014 The Android Open Source Project 3959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * 4959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Licensed under the Apache License, Version 2.0 (the "License"); 5959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * you may not use this file except in compliance with the License. 6959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * You may obtain a copy of the License at 7959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * 8959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * http://www.apache.org/licenses/LICENSE-2.0 9959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * 10959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Unless required by applicable law or agreed to in writing, software 11959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * distributed under the License is distributed on an "AS IS" BASIS, 12959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * See the License for the specific language governing permissions and 14959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * limitations under the License. 15959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 16959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 17959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kylepackage dexfuzz.program; 18959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 19959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.Log; 20959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.CodeItem; 21959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.EncodedCatchHandler; 22959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.EncodedTypeAddrPair; 23959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.Instruction; 24959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.Opcode; 25959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.TryItem; 26959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.formats.ContainsTarget; 27959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport dexfuzz.rawdex.formats.RawInsnHelper; 28959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 29959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.ArrayList; 30959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.Collections; 31959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.Comparator; 32959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.HashMap; 33959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.LinkedList; 34959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.List; 35959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyleimport java.util.Map; 36959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 37959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle/** 38959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Translates from a CodeItem (the raw list of Instructions) to MutatableCode 39959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * (graph of Instructions, using MInsns and subclasses) and vice-versa. 40959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 41959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kylepublic class CodeTranslator { 42959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 43959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 44959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Given a raw DEX file's CodeItem, produce a MutatableCode object, that CodeMutators 45959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * are designed to operate on. 46959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * @param codeItemIdx Used to make sure the correct CodeItem is updated later after mutation. 47959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * @return A new MutatableCode object, which contains all relevant information 48959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * obtained from the CodeItem. 49959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 50959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public MutatableCode codeItemToMutatableCode(Program program, CodeItem codeItem, 51959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int codeItemIdx, int mutatableCodeIdx) { 52959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Translating CodeItem " + codeItemIdx 53959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle + " (" + codeItem.meta.methodName + ") to MutatableCode"); 54959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 55959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MutatableCode mutatableCode = new MutatableCode(program); 56959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 57959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.registerMutatableCode(mutatableCode); 58959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 59959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.name = codeItem.meta.methodName; 60959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.shorty = codeItem.meta.shorty; 61959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.isStatic = codeItem.meta.isStatic; 62959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 63959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.codeItemIdx = codeItemIdx; 64959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 65959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.mutatableCodeIdx = mutatableCodeIdx; 66959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 67959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.registersSize = codeItem.registersSize; 68959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.insSize = codeItem.insSize; 69959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.outsSize = codeItem.outsSize; 70959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.triesSize = codeItem.triesSize; 71959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 72959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Temporary map from bytecode offset -> instruction. 73959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Map<Integer,MInsn> insnLocationMap = new HashMap<Integer,MInsn>(); 74959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 75959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle List<Instruction> inputInsns = codeItem.insns; 76959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 77959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Create the MInsns. 78959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int loc = 0; 79959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (Instruction insn : inputInsns) { 80959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn mInsn = null; 81959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 82959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (isInstructionSwitch(insn)) { 83959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn = new MSwitchInsn(); 84959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (isInstructionBranch(insn)) { 85959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn = new MBranchInsn(); 86959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (isInstructionFillArrayData(insn)) { 87959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn = new MInsnWithData(); 88959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else { 89959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn = new MInsn(); 90959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 91959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 92959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn.insn = insn; 93959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 94959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Populate the temporary map. 95959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle insnLocationMap.put(loc, mInsn); 96959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 97959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Populate the proper list of mutatable instructions. 98959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.addInstructionToEnd(mInsn); 99959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 100959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Calculate the offsets for each instruction. 101959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn.location = loc; 102959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn.locationUpdated = false; 103959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 104959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle loc += mInsn.insn.getSize(); 105959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 106959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 107959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now make branch/switch instructions point at the right target instructions. 108959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MInsn mInsn : mutatableCode.getInstructions()) { 109959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mInsn instanceof MSwitchInsn) { 110959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle readSwitchInstruction((MSwitchInsn) mInsn, insnLocationMap); 111959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (mInsn instanceof MInsnWithData) { 112959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 113959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn); 114959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ((MInsnWithData)mInsn).dataTarget = insnLocationMap.get(targetLoc); 115959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (((MInsnWithData)mInsn).dataTarget == null) { 116959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit("Bad offset calculation in data-target insn"); 117959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 118959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (mInsn instanceof MBranchInsn) { 119959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 120959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn); 121959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ((MBranchInsn)mInsn).target = insnLocationMap.get(targetLoc); 122959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (((MBranchInsn)mInsn).target == null) { 123959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit("Bad offset calculation in branch insn"); 124959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 125959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 126959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 127959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 128959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now create try blocks. 129959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mutatableCode.triesSize > 0) { 130959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle readTryBlocks(codeItem, mutatableCode, insnLocationMap); 131959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 132959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 133959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return mutatableCode; 134959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 135959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 136959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 137959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Given a MutatableCode item that may have been mutated, update the original CodeItem 138959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * correctly, to allow valid DEX to be written back to the output file. 139959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 140959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public void mutatableCodeToCodeItem(CodeItem codeItem, MutatableCode mutatableCode) { 141959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Translating MutatableCode " + mutatableCode.name 142959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle + " to CodeItem " + mutatableCode.codeItemIdx); 143959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 144959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // We must first align any data instructions at the end of the code 145959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // before we recalculate any offsets. 146959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // This also updates their sizes... 147959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle alignDataInstructions(mutatableCode); 148959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 149959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Validate that the tracked locations for instructions are valid. 150959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Also mark locations as no longer being updated. 151959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int loc = 0; 152959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MInsn mInsn : mutatableCode.getInstructions()) { 153959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mInsn.insn.justRaw) { 154959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // All just_raw instructions need alignment! 155959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if ((loc % 2) != 0) { 156959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle loc++; 157959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 158959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 159959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mInsn.location != loc) { 160959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit(String.format("%s does not have expected location 0x%x", 161959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn, loc)); 162959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 163959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mInsn.locationUpdated = false; 164959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle loc += mInsn.insn.getSize(); 165959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 166959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 167959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // This new list will be attached to the CodeItem at the end... 168959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle List<Instruction> outputInsns = new LinkedList<Instruction>(); 169959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 170959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Go through our new list of MInsns, adding them to the new 171959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // list of instructions that will be attached to the CodeItem. 172959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Also recalculate offsets for branches. 173959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MInsn mInsn : mutatableCode.getInstructions()) { 174959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mInsn instanceof MSwitchInsn) { 175959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle updateSwitchInstruction((MSwitchInsn)mInsn, mutatableCode); 176959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (mInsn instanceof MInsnWithData) { 177959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn target = ((MInsnWithData) mInsn).dataTarget; 178959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int dataOffset = target.location - mInsn.location; 179959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 180959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle containsTarget.setTarget(mInsn.insn, dataOffset); 181959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (mInsn instanceof MBranchInsn) { 182959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn target = ((MBranchInsn) mInsn).target; 183959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int branchOffset = target.location - mInsn.location; 184959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format; 185959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle containsTarget.setTarget(mInsn.insn, branchOffset); 186959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 187959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle outputInsns.add(mInsn.insn); 188959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 189959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 190959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Calculate the new insns_size. 191959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int newInsnsSize = 0; 192959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (Instruction insn : outputInsns) { 193959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle newInsnsSize += insn.getSize(); 194959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 195959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 196959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mutatableCode.triesSize > 0) { 197959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle updateTryBlocks(codeItem, mutatableCode); 198959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 199959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 200959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.insnsSize = newInsnsSize; 201959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.insns = outputInsns; 202959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.registersSize = mutatableCode.registersSize; 203959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.insSize = mutatableCode.insSize; 204959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.outsSize = mutatableCode.outsSize; 205959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.triesSize = mutatableCode.triesSize; 206959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 207959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 208959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 209959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * The TryItem specifies an offset into the EncodedCatchHandlerList for a given CodeItem, 210959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * but we only have an array of the EncodedCatchHandlers that the List contains. 211959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * This function produces a map that offers a way to find out the index into our array, 212959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * from the try handler's offset. 213959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 214959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private Map<Short,Integer> createTryHandlerOffsetToIndexMap(CodeItem codeItem) { 215959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Create a sorted set of offsets. 216959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle List<Short> uniqueOffsets = new ArrayList<Short>(); 217959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (TryItem tryItem : codeItem.tries) { 218959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int index = 0; 219959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle while (true) { 220959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if ((index == uniqueOffsets.size()) 221959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle || (uniqueOffsets.get(index) > tryItem.handlerOff)) { 222959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // First condition means we're at the end of the set (or we're inserting 223959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // into an empty set) 224959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Second condition means that the offset belongs here 225959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // ...so insert it here, pushing the element currently in this position to the 226959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // right, if it exists 227959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle uniqueOffsets.add(index, tryItem.handlerOff); 228959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle break; 229959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (uniqueOffsets.get(index) == tryItem.handlerOff) { 230959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // We've already seen it, and we're making a set, not a list. 231959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle break; 232959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else { 233959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Keep searching. 234959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle index++; 235959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 236959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 237959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 238959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now we have an (implicit) index -> offset mapping! 239959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 240959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now create the reverse mapping. 241959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Map<Short,Integer> offsetIndexMap = new HashMap<Short,Integer>(); 242959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < uniqueOffsets.size(); i++) { 243959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle offsetIndexMap.put(uniqueOffsets.get(i), i); 244959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 245959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 246959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return offsetIndexMap; 247959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 248959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 249959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private void readTryBlocks(CodeItem codeItem, MutatableCode mutatableCode, 250959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Map<Integer,MInsn> insnLocationMap) { 251959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.mutatableTries = new LinkedList<MTryBlock>(); 252959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 253959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem); 254959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 255959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Read each TryItem into a MutatableTryBlock. 256959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (TryItem tryItem : codeItem.tries) { 257959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MTryBlock mTryBlock = new MTryBlock(); 258959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 259959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Get the MInsns that form the start and end of the try block. 260959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int startLocation = tryItem.startAddr; 261959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mTryBlock.startInsn = insnLocationMap.get(startLocation); 26202de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov 26302de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov // The instructions vary in size, so we have to find the last instruction in the block in a 26402de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov // few tries. 26502de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov int endLocation = tryItem.startAddr + tryItem.insnCount - 1; 266959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mTryBlock.endInsn = insnLocationMap.get(endLocation); 26702de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov while ((mTryBlock.endInsn == null) && (endLocation >= startLocation)) { 26802de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov endLocation--; 26902de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov mTryBlock.endInsn = insnLocationMap.get(endLocation); 27002de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov } 271959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 272959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Sanity checks. 273959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mTryBlock.startInsn == null) { 274959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit(String.format( 275959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle "Couldn't find a mutatable insn at start offset 0x%x", 276959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle startLocation)); 277959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 278959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mTryBlock.endInsn == null) { 279959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit(String.format( 280959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle "Couldn't find a mutatable insn at end offset 0x%x", 281959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle endLocation)); 282959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 283959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 284959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Get the EncodedCatchHandler. 285959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int handlerIdx = offsetIndexMap.get(tryItem.handlerOff); 286959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle EncodedCatchHandler encodedCatchHandler = codeItem.handlers.list[handlerIdx]; 287959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 288959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Do we have a catch all? If so, associate the MInsn that's there. 289959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (encodedCatchHandler.size <= 0) { 290959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mTryBlock.catchAllHandler = 291959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle insnLocationMap.get(encodedCatchHandler.catchAllAddr); 292959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Sanity check. 293959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mTryBlock.catchAllHandler == null) { 294959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit( 295959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle String.format("Couldn't find a mutatable insn at catch-all offset 0x%x", 296959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle encodedCatchHandler.catchAllAddr)); 297959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 298959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 299959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Do we have explicitly-typed handlers? This will remain empty if not. 300959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mTryBlock.handlers = new LinkedList<MInsn>(); 301959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 302959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Associate all the explicitly-typed handlers. 303959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) { 304959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i]; 305959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn handlerInsn = insnLocationMap.get(handler.addr); 306959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Sanity check. 307959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (handlerInsn == null) { 308959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit(String.format( 309959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle "Couldn't find a mutatable instruction at handler offset 0x%x", 310959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle handler.addr)); 311959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 312959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mTryBlock.handlers.add(handlerInsn); 313959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 314959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 315959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now finally add the new MutatableTryBlock into this MutatableCode's list! 316959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.mutatableTries.add(mTryBlock); 317959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 318959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 319959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 320959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private void updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode) { 321959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 322959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // TODO: Support ability to add extra try blocks/handlers? 323959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 324959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MTryBlock mTryBlock : mutatableCode.mutatableTries) { 325959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mTryBlock.startInsn.location > mTryBlock.endInsn.location) { 326959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Mutation has put this try block's end insn before its start insn. Fix this. 327959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn tempInsn = mTryBlock.startInsn; 328959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mTryBlock.startInsn = mTryBlock.endInsn; 329959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mTryBlock.endInsn = tempInsn; 330959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 331959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 332959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 333959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // First, manipulate the try blocks if they overlap. 334959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < mutatableCode.mutatableTries.size() - 1; i++) { 335959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MTryBlock first = mutatableCode.mutatableTries.get(i); 336959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MTryBlock second = mutatableCode.mutatableTries.get(i + 1); 337959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 338959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Do they overlap? 339959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (first.endInsn.location > second.startInsn.location) { 340959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 341959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Found overlap in TryBlocks, moving 2nd TryBlock..."); 342959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("1st TryBlock goes from " + first.startInsn + " to " + first.endInsn); 343959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("2nd TryBlock goes from " + second.startInsn + " to " + second.endInsn); 344959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 345959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Find the first instruction that comes after that does not overlap 346959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // with the first try block. 347959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn newInsn = second.startInsn; 348959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int ptr = mutatableCode.getInstructionIndex(newInsn); 349959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle while (first.endInsn.location > newInsn.location) { 350959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ptr++; 351959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle newInsn = mutatableCode.getInstructionAt(ptr); 352959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 353959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle second.startInsn = newInsn; 354959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 355959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Now 2nd TryBlock goes from " + second.startInsn + " to " + second.endInsn); 356959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 357959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 358959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 359959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem); 360959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 361959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int tryItemIdx = 0; 362959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MTryBlock mTryBlock : mutatableCode.mutatableTries) { 363959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle TryItem tryItem = codeItem.tries[tryItemIdx]; 364959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 365959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryItem.startAddr = mTryBlock.startInsn.location; 36602de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov int insnCount = mTryBlock.endInsn.location - mTryBlock.startInsn.location + 36702de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov mTryBlock.endInsn.insn.getSize(); 36802de20038ae0b75d809c33eb0b36127dff7e7220Branislav Rankov tryItem.insnCount = (short) insnCount; 369959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 370959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Get the EncodedCatchHandler. 371959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle EncodedCatchHandler encodedCatchHandler = 372959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle codeItem.handlers.list[offsetIndexMap.get(tryItem.handlerOff)]; 373959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 374959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (encodedCatchHandler.size <= 0) { 375959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle encodedCatchHandler.catchAllAddr = mTryBlock.catchAllHandler.location; 376959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 377959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) { 378959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn handlerInsn = mTryBlock.handlers.get(i); 379959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i]; 380959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle handler.addr = handlerInsn.location; 381959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 382959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle tryItemIdx++; 383959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 384959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 385959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 386959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 387959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Given a switch instruction, find the associated data's raw[] form, and update 388959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * the targets of the switch instruction to point to the correct instructions. 389959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 390959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private void readSwitchInstruction(MSwitchInsn switchInsn, 391959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Map<Integer,MInsn> insnLocationMap) { 392959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Find the data. 393959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format; 394959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int dataLocation = switchInsn.location + (int) containsTarget.getTarget(switchInsn.insn); 395959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle switchInsn.dataTarget = insnLocationMap.get(dataLocation); 396959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (switchInsn.dataTarget == null) { 397959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit("Bad offset calculation for data target in switch insn"); 398959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 399959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 400959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now read the data. 401959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Instruction dataInsn = switchInsn.dataTarget.insn; 402959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 403959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int rawPtr = 2; 404959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 405959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int targetsSize = (int) RawInsnHelper.getUnsignedShortFromTwoBytes(dataInsn.rawBytes, rawPtr); 406959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 2; 407959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 408959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int[] keys = new int[targetsSize]; 409959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int[] targets = new int[targetsSize]; 410959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 411959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (dataInsn.rawType == 1) { 412959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle switchInsn.packed = true; 413959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Dealing with a packed-switch. 414959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Read the first key. 415959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle keys[0] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, rawPtr); 416959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 4; 417959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Calculate the rest of the keys. 418959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 1; i < targetsSize; i++) { 419959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle keys[i] = keys[i - 1] + 1; 420959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 421959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (dataInsn.rawType == 2) { 422959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Dealing with a sparse-switch. 423959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Read all of the keys. 424959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < targetsSize; i++) { 425959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle keys[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, 426959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr); 427959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 4; 428959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 429959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 430959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 431959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now read the targets. 432959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < targetsSize; i++) { 433959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle targets[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, 434959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr); 435959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 4; 436959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 437959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 438959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Store the keys. 439959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle switchInsn.keys = keys; 440959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 441959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Convert our targets[] offsets into pointers to MInsns. 442959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int target : targets) { 443959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int targetLocation = switchInsn.location + target; 444959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn targetInsn = insnLocationMap.get(targetLocation); 445959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle switchInsn.targets.add(targetInsn); 446959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (targetInsn == null) { 447959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.errorAndQuit("Bad offset calculation for target in switch insn"); 448959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 449959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 450959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 451959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 452959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 453959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Given a mutatable switch instruction, which may have had some of its branch 454959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * targets moved, update all the target offsets in the raw[] form of the instruction. 455959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 456959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private void updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode) { 457959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Update the offset to the data instruction 458959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn dataTarget = switchInsn.dataTarget; 459959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int dataOffset = dataTarget.location - switchInsn.location; 460959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format; 461959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle containsTarget.setTarget(switchInsn.insn, dataOffset); 462959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 463959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int targetsSize = switchInsn.targets.size(); 464959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 465959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int[] keys = switchInsn.keys; 466959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int[] targets = new int[targetsSize]; 467959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 468959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Calculate the new offsets. 469959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int targetIdx = 0; 470959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MInsn target : switchInsn.targets) { 471959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle targets[targetIdx] = target.location - switchInsn.location; 472959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle targetIdx++; 473959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 474959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 475959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Now write the data back to the raw bytes. 476959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Instruction dataInsn = switchInsn.dataTarget.insn; 477959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 478959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int rawPtr = 2; 479959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 480959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Write out the size. 481959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle RawInsnHelper.writeUnsignedShortToTwoBytes(dataInsn.rawBytes, rawPtr, targetsSize); 482959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 2; 483959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 484959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Write out the keys. 485959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (switchInsn.packed) { 486959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Only write out one key - the first. 487959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[0]); 488959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 4; 489959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else { 490959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Write out all the keys. 491959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < targetsSize; i++) { 492959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[i]); 493959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 4; 494959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 495959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 496959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 497959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Write out all the targets. 498959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (int i = 0; i < targetsSize; i++) { 499959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, targets[i]); 500959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle rawPtr += 4; 501959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 502959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 503959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 504959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 505959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * After mutation, data instructions may no longer be 4-byte aligned. 506959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * If this is the case, insert nops to align them all. 507959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * This makes a number of assumptions about data currently: 508959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * - data is always at the end of method insns 509959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * - all data instructions are stored contiguously 510959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 511959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private void alignDataInstructions(MutatableCode mutatableCode) { 512959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Find all the switch data instructions. 513959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle List<MInsn> dataInsns = new ArrayList<MInsn>(); 514959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 515959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Update raw sizes of the data instructions as well. 516959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MInsn mInsn : mutatableCode.getInstructions()) { 517959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (mInsn instanceof MSwitchInsn) { 518959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Update the raw size of the instruction. 519959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MSwitchInsn switchInsn = (MSwitchInsn) mInsn; 520959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int targetsSize = switchInsn.targets.size(); 521959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Instruction dataInsn = switchInsn.dataTarget.insn; 522959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (switchInsn.packed) { 523959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle dataInsn.rawSize = (targetsSize * 2) + 4; 524959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else { 525959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle dataInsn.rawSize = (targetsSize * 4) + 2; 526959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 527959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle dataInsns.add(switchInsn.dataTarget); 528959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (mInsn instanceof MInsnWithData) { 529959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsnWithData insnWithData = 530959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle (MInsnWithData) mInsn; 531959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle dataInsns.add(insnWithData.dataTarget); 532959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 533959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 534959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 535959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Only need to align switch data instructions if there are any! 536959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (!dataInsns.isEmpty()) { 537959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 538959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Found data instructions, checking alignment..."); 539959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 540959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Sort data_insns by location. 541959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Collections.sort(dataInsns, new Comparator<MInsn>() { 542959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle @Override 543959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle public int compare(MInsn first, MInsn second) { 544959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (first.location < second.location) { 545959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return -1; 546959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } else if (first.location > second.location) { 547959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return 1; 548959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 549959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return 0; 550959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 551959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle }); 552959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 553959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle boolean performedAlignment = false; 554959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 555959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle // Go through all the data insns, and insert an alignment nop if they're unaligned. 556959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle for (MInsn dataInsn : dataInsns) { 557959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (dataInsn.location % 2 != 0) { 558959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Aligning data instruction with a nop."); 559959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle int alignmentNopIdx = mutatableCode.getInstructionIndex(dataInsn); 560959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle MInsn nop = new MInsn(); 561959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle nop.insn = new Instruction(); 562959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle nop.insn.info = Instruction.getOpcodeInfo(Opcode.NOP); 563959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle mutatableCode.insertInstructionAt(nop, alignmentNopIdx); 564959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle performedAlignment = true; 565959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 566959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 567959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 568959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (!performedAlignment) { 569959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Log.debug("Alignment okay."); 570959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 571959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 572959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 573959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 574959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 575959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Determine if a particular instruction is a branch instruction, based on opcode. 576959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 577959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private boolean isInstructionBranch(Instruction insn) { 578959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Opcode opcode = insn.info.opcode; 579959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ) 580959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle || Opcode.isBetween(opcode, Opcode.GOTO, Opcode.GOTO_32)) { 581959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return true; 582959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 583959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return false; 584959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 585959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 586959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle /** 587959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle * Determine if a particular instruction is a switch instruction, based on opcode. 588959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle */ 589959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private boolean isInstructionSwitch(Instruction insn) { 590959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle Opcode opcode = insn.info.opcode; 591959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle if (Opcode.isBetween(opcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)) { 592959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return true; 593959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 594959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return false; 595959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 596959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle 597959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle private boolean isInstructionFillArrayData(Instruction insn) { 598959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle return (insn.info.opcode == Opcode.FILL_ARRAY_DATA); 599959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle } 600959ffdf65f280ee90b7944a8dd610564e7f99e69Stephen Kyle} 601