1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package dexfuzz.rawdex; 18 19import dexfuzz.Log; 20import dexfuzz.program.MutatableCode; 21 22import java.io.IOException; 23import java.util.LinkedList; 24import java.util.List; 25 26public class CodeItem implements RawDexObject { 27 public short registersSize; 28 public short insSize; 29 public short outsSize; 30 public short triesSize; 31 public int debugInfoOff; // NB: this is a special case 32 public int insnsSize; 33 public List<Instruction> insns; 34 public TryItem[] tries; 35 public EncodedCatchHandlerList handlers; 36 37 private MutatableCode mutatableCode; 38 39 public static class MethodMetaInfo { 40 public String methodName; 41 public boolean isStatic; 42 public String shorty; 43 } 44 45 public MethodMetaInfo meta = new MethodMetaInfo(); 46 47 @Override 48 public void read(DexRandomAccessFile file) throws IOException { 49 file.alignForwards(4); 50 file.getOffsetTracker().getNewOffsettable(file, this); 51 registersSize = file.readUShort(); 52 insSize = file.readUShort(); 53 outsSize = file.readUShort(); 54 triesSize = file.readUShort(); 55 debugInfoOff = file.readUInt(); 56 insnsSize = file.readUInt(); 57 populateInstructionList(file); 58 if (triesSize > 0) { 59 if ((insnsSize % 2) != 0) { 60 // Consume padding. 61 file.readUShort(); 62 } 63 tries = new TryItem[triesSize]; 64 for (int i = 0; i < triesSize; i++) { 65 (tries[i] = new TryItem()).read(file); 66 } 67 (handlers = new EncodedCatchHandlerList()).read(file); 68 } 69 } 70 71 private void populateInstructionList(DexRandomAccessFile file) throws IOException { 72 insns = new LinkedList<Instruction>(); 73 long insnsOffset = file.getFilePointer(); 74 if (insnsOffset != 0) { 75 long finger = insnsOffset; 76 long insnsEnd = insnsOffset + (2 * insnsSize); 77 78 while (finger < insnsEnd) { 79 file.seek(finger); 80 Instruction newInsn = new Instruction(); 81 newInsn.read(file); 82 insns.add(newInsn); 83 finger += (2 * newInsn.getSize()); 84 } 85 86 file.seek(finger); 87 } 88 } 89 90 @Override 91 public void write(DexRandomAccessFile file) throws IOException { 92 file.alignForwards(4); 93 file.getOffsetTracker().updatePositionOfNextOffsettable(file); 94 file.writeUShort(registersSize); 95 file.writeUShort(insSize); 96 file.writeUShort(outsSize); 97 file.writeUShort(triesSize); 98 // We do not support retaining debug info currently. 99 file.writeUInt(0 /*debug_info_off*/); 100 file.writeUInt(insnsSize); 101 for (Instruction insn : insns) { 102 insn.write(file); 103 } 104 if (triesSize > 0) { 105 if ((insnsSize % 2) != 0) { 106 // produce padding 107 file.writeUShort((short) 0); 108 } 109 for (TryItem tryItem : tries) { 110 tryItem.write(file); 111 } 112 handlers.write(file); 113 } 114 } 115 116 /** 117 * CodeTranslator should call this to notify a CodeItem about its 118 * mutatable code, so it can always get the "latest" view of its 119 * instructions. 120 */ 121 public void registerMutatableCode(MutatableCode mutatableCode) { 122 this.mutatableCode = mutatableCode; 123 } 124 125 @Override 126 public void incrementIndex(IndexUpdateKind kind, int insertedIdx) { 127 if (kind == IndexUpdateKind.TYPE_ID && triesSize > 0) { 128 // EncodedCatchHandlerList (well, the EncodedTypeAddrPairs it owns) 129 // are only interested in TYPE_IDs. 130 handlers.incrementIndex(kind, insertedIdx); 131 } 132 133 if (kind == IndexUpdateKind.PROTO_ID) { 134 // The only kind we can't encounter in an instruction. 135 return; 136 } 137 138 List<Instruction> insnsToIncrement = insns; 139 140 // If we have an associated MutatableCode, then it may have created some new insns 141 // that we won't know about yet, during the mutation phase. 142 // 143 // Ask for the latest view of the insns. 144 if (mutatableCode != null) { 145 insnsToIncrement = mutatableCode.requestLatestInstructions(); 146 } 147 148 for (Instruction insn : insnsToIncrement) { 149 Opcode opcode = insn.info.opcode; 150 switch (kind) { 151 case STRING_ID: 152 if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) { 153 // STRING@BBBB 154 if (insn.vregB >= insertedIdx) { 155 insn.vregB++; 156 } 157 } 158 break; 159 case TYPE_ID: 160 if (opcode == Opcode.CONST_CLASS 161 || opcode == Opcode.CHECK_CAST 162 || opcode == Opcode.NEW_INSTANCE 163 || opcode == Opcode.FILLED_NEW_ARRAY 164 || opcode == Opcode.FILLED_NEW_ARRAY_RANGE) { 165 // TYPE@BBBB 166 if (insn.vregB >= insertedIdx) { 167 insn.vregB++; 168 } 169 } else if (opcode == Opcode.INSTANCE_OF || opcode == Opcode.NEW_ARRAY) { 170 // TYPE@CCCC 171 if (insn.vregC >= insertedIdx) { 172 insn.vregC++; 173 } 174 } 175 break; 176 case FIELD_ID: 177 if (Opcode.isBetween(opcode, Opcode.SGET, Opcode.SPUT_SHORT)) { 178 // FIELD@BBBB 179 if (insn.vregB >= insertedIdx) { 180 insn.vregB++; 181 } 182 } else if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.IPUT_SHORT)) { 183 // FIELD@CCCC 184 if (insn.vregC >= insertedIdx) { 185 insn.vregC++; 186 } 187 } 188 break; 189 case METHOD_ID: 190 if (Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE) 191 || Opcode.isBetween(opcode, 192 Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)) { 193 // METHOD@BBBB 194 if (insn.vregB >= insertedIdx) { 195 insn.vregB++; 196 } 197 } 198 break; 199 default: 200 Log.errorAndQuit("Unexpected IndexUpdateKind requested " 201 + "in Instruction.incrementIndex()"); 202 } 203 } 204 } 205} 206