1 /* 2 * [The "BSD licence"] 3 * Copyright (c) 2010 Ben Gruver (JesusFreke) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 package org.jf.dexlib.Debug; 30 31 import org.jf.dexlib.DebugInfoItem; 32 import org.jf.dexlib.DexFile; 33 import org.jf.dexlib.StringIdItem; 34 import org.jf.dexlib.TypeIdItem; 35 import org.jf.dexlib.Util.ByteArrayInput; 36 import org.jf.dexlib.Util.Input; 37 38 public class DebugInstructionIterator { 39 /** 40 * This method decodes the debug instructions in the given byte array and iterates over them, calling 41 * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction 42 * @param in an Input object that the debug instructions can be read from 43 * @param processDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called 44 * for each instruction that is encountered 45 */ 46 public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) { 47 int startDebugOffset; 48 49 while(true) 50 { 51 startDebugOffset = in.getCursor(); 52 byte debugOpcode = in.readByte(); 53 54 switch (debugOpcode) { 55 case 0x00: 56 { 57 processDebugInstruction.ProcessEndSequence(startDebugOffset); 58 return; 59 } 60 case 0x01: 61 { 62 int codeAddressDiff = in.readUnsignedLeb128(); 63 processDebugInstruction.ProcessAdvancePC(startDebugOffset, in.getCursor() - startDebugOffset, 64 codeAddressDiff); 65 break; 66 } 67 case 0x02: 68 { 69 int lineDiff = in.readSignedLeb128(); 70 processDebugInstruction.ProcessAdvanceLine(startDebugOffset, in.getCursor() - startDebugOffset, 71 lineDiff); 72 break; 73 } 74 case 0x03: 75 { 76 int registerNum = in.readUnsignedOrSignedLeb128(); 77 boolean isSignedRegister = false; 78 if (registerNum < 0) { 79 isSignedRegister = true; 80 registerNum = ~registerNum; 81 } 82 int nameIndex = in.readUnsignedLeb128() - 1; 83 int typeIndex = in.readUnsignedLeb128() - 1; 84 processDebugInstruction.ProcessStartLocal(startDebugOffset, in.getCursor() - startDebugOffset, 85 registerNum, nameIndex, typeIndex, isSignedRegister); 86 break; 87 } 88 case 0x04: 89 { 90 int registerNum = in.readUnsignedOrSignedLeb128(); 91 boolean isSignedRegister = false; 92 if (registerNum < 0) { 93 isSignedRegister = true; 94 registerNum = ~registerNum; 95 } 96 int nameIndex = in.readUnsignedLeb128() - 1; 97 int typeIndex = in.readUnsignedLeb128() - 1; 98 int signatureIndex = in.readUnsignedLeb128() - 1; 99 processDebugInstruction.ProcessStartLocalExtended(startDebugOffset, 100 in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, signatureIndex, 101 isSignedRegister); 102 break; 103 } 104 case 0x05: 105 { 106 int registerNum = in.readUnsignedOrSignedLeb128(); 107 boolean isSignedRegister = false; 108 if (registerNum < 0) { 109 isSignedRegister = true; 110 registerNum = ~registerNum; 111 } 112 processDebugInstruction.ProcessEndLocal(startDebugOffset, in.getCursor() - startDebugOffset, 113 registerNum, isSignedRegister); 114 break; 115 } 116 case 0x06: 117 { 118 int registerNum = in.readUnsignedOrSignedLeb128(); 119 boolean isSignedRegister = false; 120 if (registerNum < 0) { 121 isSignedRegister = true; 122 registerNum = ~registerNum; 123 } 124 processDebugInstruction.ProcessRestartLocal(startDebugOffset, in.getCursor() - startDebugOffset, 125 registerNum, isSignedRegister); 126 break; 127 } 128 case 0x07: 129 { 130 processDebugInstruction.ProcessSetPrologueEnd(startDebugOffset); 131 break; 132 } 133 case 0x08: 134 { 135 processDebugInstruction.ProcessSetEpilogueBegin(startDebugOffset); 136 break; 137 } 138 case 0x09: 139 { 140 int nameIndex = in.readUnsignedLeb128(); 141 processDebugInstruction.ProcessSetFile(startDebugOffset, in.getCursor() - startDebugOffset, 142 nameIndex); 143 break; 144 } 145 default: 146 { 147 int base = ((debugOpcode & 0xFF) - 0x0A); 148 processDebugInstruction.ProcessSpecialOpcode(startDebugOffset, debugOpcode, (base % 15) - 4, base / 15); 149 } 150 } 151 } 152 } 153 154 /** 155 * This method decodes the debug instructions in the given byte array and iterates over them, calling 156 * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction 157 * @param debugInfoItem the <code>DebugInfoItem</code> to iterate over 158 * @param registerCount the number of registers in the method that the given debug info is for 159 * @param processDecodedDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called 160 * for each instruction that is encountered 161 */ 162 public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount, 163 ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) { 164 int startDebugOffset; 165 int currentCodeAddress = 0; 166 int line = debugInfoItem.getLineStart(); 167 Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo()); 168 DexFile dexFile = debugInfoItem.getDexFile(); 169 170 Local[] locals = new Local[registerCount]; 171 172 while(true) 173 { 174 startDebugOffset = in.getCursor(); 175 byte debugOpcode = in.readByte(); 176 177 switch (DebugOpcode.getDebugOpcodeByValue(debugOpcode)) { 178 case DBG_END_SEQUENCE: 179 { 180 return; 181 } 182 case DBG_ADVANCE_PC: 183 { 184 int codeAddressDiff = in.readUnsignedLeb128(); 185 currentCodeAddress += codeAddressDiff; 186 break; 187 } 188 case DBG_ADVANCE_LINE: 189 { 190 int lineDiff = in.readSignedLeb128(); 191 line += lineDiff; 192 break; 193 } 194 case DBG_START_LOCAL: 195 { 196 int registerNum = in.readUnsignedLeb128(); 197 StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); 198 TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); 199 locals[registerNum] = new Local(registerNum, name, type, null); 200 processDecodedDebugInstruction.ProcessStartLocal(currentCodeAddress, 201 in.getCursor() - startDebugOffset, registerNum, name, type); 202 break; 203 } 204 case DBG_START_LOCAL_EXTENDED: 205 { 206 int registerNum = in.readUnsignedLeb128(); 207 StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); 208 TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); 209 StringIdItem signature = 210 dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); 211 locals[registerNum] = new Local(registerNum, name, type, signature); 212 processDecodedDebugInstruction.ProcessStartLocalExtended(currentCodeAddress, 213 in.getCursor() - startDebugOffset, registerNum, name, type, signature); 214 break; 215 } 216 case DBG_END_LOCAL: 217 { 218 int registerNum = in.readUnsignedLeb128(); 219 Local local = locals[registerNum]; 220 if (local == null) { 221 processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, 222 null, null, null); 223 } else { 224 processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, 225 local.name, local.type, local.signature); 226 } 227 break; 228 } 229 case DBG_RESTART_LOCAL: 230 { 231 int registerNum = in.readUnsignedLeb128(); 232 Local local = locals[registerNum]; 233 if (local == null) { 234 processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, 235 registerNum, null, null, null); 236 } else { 237 processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, 238 registerNum, local.name, local.type, local.signature); 239 } 240 241 break; 242 } 243 case DBG_SET_PROLOGUE_END: 244 { 245 processDecodedDebugInstruction.ProcessSetPrologueEnd(currentCodeAddress); 246 break; 247 } 248 case DBG_SET_EPILOGUE_BEGIN: 249 { 250 processDecodedDebugInstruction.ProcessSetEpilogueBegin(currentCodeAddress); 251 break; 252 } 253 case DBG_SET_FILE: 254 { 255 StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); 256 processDecodedDebugInstruction.ProcessSetFile(currentCodeAddress, in.getCursor() - startDebugOffset, name); 257 break; 258 } 259 case DBG_SPECIAL_OPCODE: 260 { 261 int base = ((debugOpcode & 0xFF) - 0x0A); 262 currentCodeAddress += base / 15; 263 line += (base % 15) - 4; 264 processDecodedDebugInstruction.ProcessLineEmit(currentCodeAddress, line); 265 } 266 } 267 } 268 } 269 270 public static class ProcessRawDebugInstructionDelegate 271 { 272 //TODO: add javadocs 273 public void ProcessEndSequence(int startDebugOffset) { 274 ProcessStaticOpcode(DebugOpcode.DBG_END_SEQUENCE, startDebugOffset, 1); 275 } 276 277 public void ProcessAdvancePC(int startDebugOffset, int length, int codeAddressDiff) { 278 ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_PC, startDebugOffset, length); 279 } 280 281 public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) { 282 ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_LINE, startDebugOffset, length); 283 } 284 285 public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex, 286 boolean registerIsSigned) { 287 } 288 289 public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, 290 int typeIndex,int signatureIndex, boolean registerIsSigned) { 291 } 292 293 public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { 294 ProcessStaticOpcode(DebugOpcode.DBG_END_LOCAL, startDebugOffset, length); 295 } 296 297 public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { 298 ProcessStaticOpcode(DebugOpcode.DBG_RESTART_LOCAL, startDebugOffset, length); 299 } 300 301 public void ProcessSetPrologueEnd(int startDebugOffset) { 302 ProcessStaticOpcode(DebugOpcode.DBG_SET_PROLOGUE_END, startDebugOffset, 1); 303 } 304 305 public void ProcessSetEpilogueBegin(int startDebugOffset) { 306 ProcessStaticOpcode(DebugOpcode.DBG_SET_EPILOGUE_BEGIN, startDebugOffset, 1); 307 } 308 309 public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { 310 } 311 312 public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, int codeAddressDiff) { 313 ProcessStaticOpcode(DebugOpcode.DBG_SPECIAL_OPCODE, startDebugOffset, 1); 314 } 315 316 public void ProcessStaticOpcode(DebugOpcode debugOpcode, int startDebugOffset, int length) { 317 } 318 } 319 320 public static class ProcessDecodedDebugInstructionDelegate 321 { 322 public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 323 TypeIdItem type) { 324 } 325 326 public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name, 327 TypeIdItem type, StringIdItem signature) { 328 } 329 330 public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type, 331 StringIdItem signature) { 332 } 333 334 public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, 335 TypeIdItem type, StringIdItem signature) { 336 } 337 338 public void ProcessSetPrologueEnd(int codeAddress) { 339 } 340 341 public void ProcessSetEpilogueBegin(int codeAddress) { 342 } 343 344 public void ProcessSetFile(int codeAddress, int length, StringIdItem name) { 345 } 346 347 public void ProcessLineEmit(int codeAddress, int line) { 348 } 349 } 350 351 private static class Local { 352 public final int register; 353 public final StringIdItem name; 354 public final TypeIdItem type; 355 public final StringIdItem signature; 356 public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) { 357 this.register = register; 358 this.name = name; 359 this.type = type; 360 this.signature = signature; 361 } 362 363 } 364 } 365