/* * [The "BSD licence"] * Copyright (c) 2010 Ben Gruver (JesusFreke) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jf.dexlib.Debug; import org.jf.dexlib.DebugInfoItem; import org.jf.dexlib.DexFile; import org.jf.dexlib.StringIdItem; import org.jf.dexlib.TypeIdItem; import org.jf.dexlib.Util.ByteArrayInput; import org.jf.dexlib.Util.Input; public class DebugInstructionIterator { /** * This method decodes the debug instructions in the given byte array and iterates over them, calling * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction * @param in an Input object that the debug instructions can be read from * @param processDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called * for each instruction that is encountered */ public static void IterateInstructions(Input in, ProcessRawDebugInstructionDelegate processDebugInstruction) { int startDebugOffset; while(true) { startDebugOffset = in.getCursor(); byte debugOpcode = in.readByte(); switch (debugOpcode) { case 0x00: { processDebugInstruction.ProcessEndSequence(startDebugOffset); return; } case 0x01: { int codeAddressDiff = in.readUnsignedLeb128(); processDebugInstruction.ProcessAdvancePC(startDebugOffset, in.getCursor() - startDebugOffset, codeAddressDiff); break; } case 0x02: { int lineDiff = in.readSignedLeb128(); processDebugInstruction.ProcessAdvanceLine(startDebugOffset, in.getCursor() - startDebugOffset, lineDiff); break; } case 0x03: { int registerNum = in.readUnsignedOrSignedLeb128(); boolean isSignedRegister = false; if (registerNum < 0) { isSignedRegister = true; registerNum = ~registerNum; } int nameIndex = in.readUnsignedLeb128() - 1; int typeIndex = in.readUnsignedLeb128() - 1; processDebugInstruction.ProcessStartLocal(startDebugOffset, in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, isSignedRegister); break; } case 0x04: { int registerNum = in.readUnsignedOrSignedLeb128(); boolean isSignedRegister = false; if (registerNum < 0) { isSignedRegister = true; registerNum = ~registerNum; } int nameIndex = in.readUnsignedLeb128() - 1; int typeIndex = in.readUnsignedLeb128() - 1; int signatureIndex = in.readUnsignedLeb128() - 1; processDebugInstruction.ProcessStartLocalExtended(startDebugOffset, in.getCursor() - startDebugOffset, registerNum, nameIndex, typeIndex, signatureIndex, isSignedRegister); break; } case 0x05: { int registerNum = in.readUnsignedOrSignedLeb128(); boolean isSignedRegister = false; if (registerNum < 0) { isSignedRegister = true; registerNum = ~registerNum; } processDebugInstruction.ProcessEndLocal(startDebugOffset, in.getCursor() - startDebugOffset, registerNum, isSignedRegister); break; } case 0x06: { int registerNum = in.readUnsignedOrSignedLeb128(); boolean isSignedRegister = false; if (registerNum < 0) { isSignedRegister = true; registerNum = ~registerNum; } processDebugInstruction.ProcessRestartLocal(startDebugOffset, in.getCursor() - startDebugOffset, registerNum, isSignedRegister); break; } case 0x07: { processDebugInstruction.ProcessSetPrologueEnd(startDebugOffset); break; } case 0x08: { processDebugInstruction.ProcessSetEpilogueBegin(startDebugOffset); break; } case 0x09: { int nameIndex = in.readUnsignedLeb128(); processDebugInstruction.ProcessSetFile(startDebugOffset, in.getCursor() - startDebugOffset, nameIndex); break; } default: { int base = ((debugOpcode & 0xFF) - 0x0A); processDebugInstruction.ProcessSpecialOpcode(startDebugOffset, debugOpcode, (base % 15) - 4, base / 15); } } } } /** * This method decodes the debug instructions in the given byte array and iterates over them, calling * the ProcessDebugInstructionDelegate.ProcessDebugInstruction method for each instruction * @param debugInfoItem the <code>DebugInfoItem</code> to iterate over * @param registerCount the number of registers in the method that the given debug info is for * @param processDecodedDebugInstruction a <code>ProcessDebugInstructionDelegate</code> object that gets called * for each instruction that is encountered */ public static void DecodeInstructions(DebugInfoItem debugInfoItem, int registerCount, ProcessDecodedDebugInstructionDelegate processDecodedDebugInstruction) { int startDebugOffset; int currentCodeAddress = 0; int line = debugInfoItem.getLineStart(); Input in = new ByteArrayInput(debugInfoItem.getEncodedDebugInfo()); DexFile dexFile = debugInfoItem.getDexFile(); Local[] locals = new Local[registerCount]; while(true) { startDebugOffset = in.getCursor(); byte debugOpcode = in.readByte(); switch (DebugOpcode.getDebugOpcodeByValue(debugOpcode)) { case DBG_END_SEQUENCE: { return; } case DBG_ADVANCE_PC: { int codeAddressDiff = in.readUnsignedLeb128(); currentCodeAddress += codeAddressDiff; break; } case DBG_ADVANCE_LINE: { int lineDiff = in.readSignedLeb128(); line += lineDiff; break; } case DBG_START_LOCAL: { int registerNum = in.readUnsignedLeb128(); StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); locals[registerNum] = new Local(registerNum, name, type, null); processDecodedDebugInstruction.ProcessStartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, name, type); break; } case DBG_START_LOCAL_EXTENDED: { int registerNum = in.readUnsignedLeb128(); StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); TypeIdItem type = dexFile.TypeIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); StringIdItem signature = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); locals[registerNum] = new Local(registerNum, name, type, signature); processDecodedDebugInstruction.ProcessStartLocalExtended(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, name, type, signature); break; } case DBG_END_LOCAL: { int registerNum = in.readUnsignedLeb128(); Local local = locals[registerNum]; if (local == null) { processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, null, null, null); } else { processDecodedDebugInstruction.ProcessEndLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, local.name, local.type, local.signature); } break; } case DBG_RESTART_LOCAL: { int registerNum = in.readUnsignedLeb128(); Local local = locals[registerNum]; if (local == null) { processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, null, null, null); } else { processDecodedDebugInstruction.ProcessRestartLocal(currentCodeAddress, in.getCursor() - startDebugOffset, registerNum, local.name, local.type, local.signature); } break; } case DBG_SET_PROLOGUE_END: { processDecodedDebugInstruction.ProcessSetPrologueEnd(currentCodeAddress); break; } case DBG_SET_EPILOGUE_BEGIN: { processDecodedDebugInstruction.ProcessSetEpilogueBegin(currentCodeAddress); break; } case DBG_SET_FILE: { StringIdItem name = dexFile.StringIdsSection.getOptionalItemByIndex(in.readUnsignedLeb128() - 1); processDecodedDebugInstruction.ProcessSetFile(currentCodeAddress, in.getCursor() - startDebugOffset, name); break; } case DBG_SPECIAL_OPCODE: { int base = ((debugOpcode & 0xFF) - 0x0A); currentCodeAddress += base / 15; line += (base % 15) - 4; processDecodedDebugInstruction.ProcessLineEmit(currentCodeAddress, line); } } } } public static class ProcessRawDebugInstructionDelegate { //TODO: add javadocs public void ProcessEndSequence(int startDebugOffset) { ProcessStaticOpcode(DebugOpcode.DBG_END_SEQUENCE, startDebugOffset, 1); } public void ProcessAdvancePC(int startDebugOffset, int length, int codeAddressDiff) { ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_PC, startDebugOffset, length); } public void ProcessAdvanceLine(int startDebugOffset, int length, int lineDiff) { ProcessStaticOpcode(DebugOpcode.DBG_ADVANCE_LINE, startDebugOffset, length); } public void ProcessStartLocal(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex, boolean registerIsSigned) { } public void ProcessStartLocalExtended(int startDebugOffset, int length, int registerNum, int nameIndex, int typeIndex,int signatureIndex, boolean registerIsSigned) { } public void ProcessEndLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { ProcessStaticOpcode(DebugOpcode.DBG_END_LOCAL, startDebugOffset, length); } public void ProcessRestartLocal(int startDebugOffset, int length, int registerNum, boolean registerIsSigned) { ProcessStaticOpcode(DebugOpcode.DBG_RESTART_LOCAL, startDebugOffset, length); } public void ProcessSetPrologueEnd(int startDebugOffset) { ProcessStaticOpcode(DebugOpcode.DBG_SET_PROLOGUE_END, startDebugOffset, 1); } public void ProcessSetEpilogueBegin(int startDebugOffset) { ProcessStaticOpcode(DebugOpcode.DBG_SET_EPILOGUE_BEGIN, startDebugOffset, 1); } public void ProcessSetFile(int startDebugOffset, int length, int nameIndex) { } public void ProcessSpecialOpcode(int startDebugOffset, int debugOpcode, int lineDiff, int codeAddressDiff) { ProcessStaticOpcode(DebugOpcode.DBG_SPECIAL_OPCODE, startDebugOffset, 1); } public void ProcessStaticOpcode(DebugOpcode debugOpcode, int startDebugOffset, int length) { } } public static class ProcessDecodedDebugInstructionDelegate { public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type) { } public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type, StringIdItem signature) { } public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type, StringIdItem signature) { } public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name, TypeIdItem type, StringIdItem signature) { } public void ProcessSetPrologueEnd(int codeAddress) { } public void ProcessSetEpilogueBegin(int codeAddress) { } public void ProcessSetFile(int codeAddress, int length, StringIdItem name) { } public void ProcessLineEmit(int codeAddress, int line) { } } private static class Local { public final int register; public final StringIdItem name; public final TypeIdItem type; public final StringIdItem signature; public Local(int register, StringIdItem name, TypeIdItem type, StringIdItem signature) { this.register = register; this.name = name; this.type = type; this.signature = signature; } } }