diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g index 2e68c01a..04e2bd45 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g @@ -480,8 +480,25 @@ LOCAL_PHRASE WS? ',' WS? SIMPLE_NAME_EMIT WS - FIELD_TYPE_DESCRIPTOR_EMITCHILD; + FIELD_TYPE_DESCRIPTOR_EMITCHILD + WS? + ( ',' WS? STRING_LITERAL_EMIT)?; +END_LOCAL_PHRASE + : END_LOCAL_DIRECTIVE_EMIT + WS + REGISTER_EMIT; + +RESTART_LOCAL_PHRASE + : RESTART_LOCAL_DIRECTIVE_EMIT + WS + REGISTER_EMIT; + +PROLOGUE_PHRASE + : PROLOGUE_DIRECTIVE_EMIT; + +EPILOGUE_PHRASE + : EPILOGUE_DIRECTIVE_EMIT; //TODO: add support for both relative and absolute offsets? fragment OFFSET_EMIT @@ -583,6 +600,26 @@ fragment LOCAL_DIRECTIVE_EMIT : LOCAL_DIRECTIVE {emit($LOCAL_DIRECTIVE, LOCAL_DIRECTIVE);}; fragment LOCAL_DIRECTIVE : '.local'; + +fragment END_LOCAL_DIRECTIVE_EMIT + : END_LOCAL_DIRECTIVE {emit($END_LOCAL_DIRECTIVE, END_LOCAL_DIRECTIVE);}; +fragment END_LOCAL_DIRECTIVE + : '.end local'; + +fragment RESTART_LOCAL_DIRECTIVE_EMIT + : RESTART_LOCAL_DIRECTIVE {emit($RESTART_LOCAL_DIRECTIVE, RESTART_LOCAL_DIRECTIVE);}; +fragment RESTART_LOCAL_DIRECTIVE + : '.restart local'; + +fragment PROLOGUE_DIRECTIVE_EMIT + : PROLOGUE_DIRECTIVE {emit($PROLOGUE_DIRECTIVE, PROLOGUE_DIRECTIVE);}; +fragment PROLOGUE_DIRECTIVE + : '.prologue'; + +fragment EPILOGUE_DIRECTIVE_EMIT + : EPILOGUE_DIRECTIVE {emit($EPILOGUE_DIRECTIVE, EPILOGUE_DIRECTIVE);}; +fragment EPILOGUE_DIRECTIVE + : '.epilogue'; fragment REGISTER_EMIT : REGISTER {emit($REGISTER, REGISTER);}; diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g index 9d33329b..05021687 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g @@ -71,6 +71,10 @@ tokens { I_ORDERED_DEBUG_DIRECTIVES; I_LINE; I_LOCAL; + I_END_LOCAL; + I_RESTART_LOCAL; + I_PROLOGUE; + I_EPILOGUE; I_STATEMENTS; I_STATEMENT_FORMAT10t; I_STATEMENT_FORMAT10x; @@ -252,14 +256,40 @@ parameter_directive ordered_debug_directive : line_directive - | local_directive; + | local_directive + | end_local_directive + | restart_local_directive + | prologue_directive + | epilogue_directive + | source_directive; line_directive - : LINE_DIRECTIVE integral_literal -> ^(I_LINE integral_literal I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + : LINE_DIRECTIVE integral_literal + -> ^(I_LINE integral_literal I_ADDRESS[$start, Integer.toString($method::currentAddress)]); local_directive - : LOCAL_DIRECTIVE REGISTER SIMPLE_NAME field_type_descriptor - -> ^(I_LOCAL[$start, "I_LOCAL"] REGISTER SIMPLE_NAME field_type_descriptor I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + : LOCAL_DIRECTIVE REGISTER SIMPLE_NAME field_type_descriptor STRING_LITERAL? + -> ^(I_LOCAL[$start, "I_LOCAL"] REGISTER SIMPLE_NAME field_type_descriptor STRING_LITERAL? I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +end_local_directive + : END_LOCAL_DIRECTIVE REGISTER + -> ^(I_END_LOCAL[$start, "I_END_LOCAL"] REGISTER I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +restart_local_directive + : RESTART_LOCAL_DIRECTIVE REGISTER + -> ^(I_RESTART_LOCAL[$start, "I_RESTART_LOCAL"] REGISTER I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +prologue_directive + : PROLOGUE_DIRECTIVE + -> ^(I_PROLOGUE[$start, "I_PROLOGUE"] I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +epilogue_directive + : EPILOGUE_DIRECTIVE + -> ^(I_EPILOGUE[$start, "I_EPILOGUE"] I_ADDRESS[$start, Integer.toString($method::currentAddress)]); + +source_directive + : SOURCE_DIRECTIVE STRING_LITERAL + -> ^(I_SOURCE[$start, "I_SOURCE"] STRING_LITERAL I_ADDRESS[$start, Integer.toString($method::currentAddress)]); label : LABEL -> ^(I_LABEL LABEL I_ADDRESS[$start, Integer.toString($method::currentAddress)]); diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g index 4bb45d15..520e9e59 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g @@ -446,7 +446,14 @@ parameter )); ordered_debug_directives - : ^(I_ORDERED_DEBUG_DIRECTIVES (line | local)*); + : ^(I_ORDERED_DEBUG_DIRECTIVES ( line + | local + | end_local + | restart_local + | prologue + | epilogue + | source + )*); line : ^(I_LINE integral_literal address) @@ -454,15 +461,51 @@ line $method::debugInfo.addLine($address.address, $integral_literal.value); }; -local : ^(I_LOCAL REGISTER SIMPLE_NAME field_type_descriptor address) +local + : ^(I_LOCAL REGISTER SIMPLE_NAME field_type_descriptor string_literal? address) { - int registerNumber = parseRegister_short($REGISTER.text); - - $method::debugInfo.addLocal($address.address, registerNumber, $SIMPLE_NAME.text, $field_type_descriptor.type.toString()); + if ($string_literal.value != null) { + $method::debugInfo.addLocalExtended($address.address, registerNumber, $SIMPLE_NAME.text, $field_type_descriptor.type.toString(), $string_literal.value); + } else { + $method::debugInfo.addLocal($address.address, registerNumber, $SIMPLE_NAME.text, $field_type_descriptor.type.toString()); + } + }; + +end_local + : ^(I_END_LOCAL REGISTER address) + { + int registerNumber = parseRegister_short($REGISTER.text); + + $method::debugInfo.addEndLocal($address.address, registerNumber); + }; + +restart_local + : ^(I_RESTART_LOCAL REGISTER address) + { + int registerNumber = parseRegister_short($REGISTER.text); + + $method::debugInfo.addRestartLocal($address.address, registerNumber); }; +prologue + : ^(I_PROLOGUE address) + { + $method::debugInfo.addPrologue($address.address); + }; + +epilogue + : ^(I_EPILOGUE address) + { + $method::debugInfo.addEpilogue($address.address); + }; + +source + : ^(I_SOURCE string_literal address) + { + $method::debugInfo.addSetFile($address.address, $string_literal.value); + }; statements returns[ArrayList instructions] @init diff --git a/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java b/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java index 22594dba..4fb17a14 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java @@ -33,13 +33,21 @@ import org.JesusFreke.dexlib.*; public class EndLocal extends CompositeField implements DebugInstruction { private final Field[] fields; + private final ByteField opcode; + private final Leb128Field registerNumber; + public EndLocal() { fields = new Field[] { - new ByteField((byte)0x05), - new Leb128Field() + opcode = new ByteField((byte)0x05), + registerNumber = new Leb128Field() }; } + public EndLocal(int registerNumber) { + this(); + this.registerNumber.cacheValue(registerNumber); + } + protected Field[] getFields() { return fields; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java b/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java index 0d1e4c02..8b63f1a3 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java @@ -33,13 +33,21 @@ import org.JesusFreke.dexlib.*; public class RestartLocal extends CompositeField implements DebugInstruction { private final Field[] fields; + private final ByteField opcode; + private final Leb128Field registerNumber; + public RestartLocal() { fields = new Field[] { - new ByteField((byte)0x06), - new SignedLeb128Field() + opcode = new ByteField((byte)0x06), + registerNumber = new Leb128Field() }; } + public RestartLocal(int registerNumber) { + this(); + this.registerNumber.cacheValue(registerNumber); + } + protected Field[] getFields() { return fields; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java b/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java index 0371cc71..f63fb0ff 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java @@ -33,13 +33,21 @@ import org.JesusFreke.dexlib.*; public class SetFile extends CompositeField implements DebugInstruction { private final Field[] fields; + private final ByteField opcode; + private final IndexedItemReference fileName; + public SetFile(DexFile dexFile) { fields = new Field[] { - new ByteField((byte)0x09), - new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()) + opcode = new ByteField((byte)0x09), + fileName = new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()) }; } + public SetFile(DexFile dexFile, StringIdItem fileName) { + this(dexFile); + this.fileName.setReference(fileName); + } + protected Field[] getFields() { return fields; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java b/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java index 386c1bd7..779f1bda 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java @@ -51,8 +51,7 @@ public class StartLocal extends CompositeField implements DebugInstr this(dexFile); this.registerNumber.cacheValue(registerNumber); this.localName.setReference(localName); - this.localType.setReference(localType); - + this.localType.setReference(localType); } protected Field[] getFields() { diff --git a/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java b/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java index 19537e8e..12c4fa05 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java @@ -33,16 +33,32 @@ import org.JesusFreke.dexlib.*; public class StartLocalExtended extends CompositeField implements DebugInstruction { private final Field[] fields; + private final ByteField opcodeField; + //TODO: signed or unsigned leb? + private final SignedLeb128Field registerNumber; + private final IndexedItemReference localName; + private final IndexedItemReference localType; + private final IndexedItemReference signature; + public StartLocalExtended(DexFile dexFile) { fields = new Field[] { - new ByteField((byte)0x04), - new SignedLeb128Field(), - new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()), - new IndexedItemReference(dexFile.TypeIdsSection, new Leb128p1Field()), - new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()) + opcodeField = new ByteField((byte)0x04), + registerNumber = new SignedLeb128Field(), + localName = new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()), + localType = new IndexedItemReference(dexFile.TypeIdsSection, new Leb128p1Field()), + signature = new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()) }; } + public StartLocalExtended(DexFile dexFile, int registerNumber, StringIdItem localName, TypeIdItem localType, + StringIdItem signature) { + this(dexFile); + this.registerNumber.cacheValue(registerNumber); + this.localName.setReference(localName); + this.localType.setReference(localType); + this.signature.setReference(signature); + } + protected Field[] getFields() { return fields; } diff --git a/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java b/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java index 2a58f673..b1a0d127 100644 --- a/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java +++ b/src/main/java/org/JesusFreke/dexlib/util/DebugInfoBuilder.java @@ -97,6 +97,55 @@ public class DebugInfoBuilder events.add(new StartLocalEvent(address, registerNumber, localName, localType)); } + public void addLocalExtended(int address, int registerNumber, String localName, String localType, + String signature) { + hasData = true; + + checkAddress(address); + + events.add(new StartLocalExtendedEvent(address, registerNumber, localName, localType, signature)); + } + + public void addEndLocal(int address, int registerNumber) { + hasData = true; + + checkAddress(address); + + events.add(new EndLocalEvent(address, registerNumber)); + } + + public void addRestartLocal(int address, int registerNumber) { + hasData = true; + + checkAddress(address); + + events.add(new RestartLocalEvent(address, registerNumber)); + } + + public void addPrologue(int address) { + hasData = true; + + checkAddress(address); + + events.add(new PrologueEvent(address)); + } + + public void addEpilogue(int address) { + hasData = true; + + checkAddress(address); + + events.add(new EpilogueEvent(address)); + } + + public void addSetFile(int address, String fileName) { + hasData = true; + + checkAddress(address); + + events.add(new SetFileEvent(address, fileName)); + } + public int getParameterNameCount() { return parameterNames.size(); } @@ -137,6 +186,15 @@ public class DebugInfoBuilder void emit(DexFile dexFile, List debugInstructions); } + private void emitAdvancePC(int address, List debugInstructions) { + int addressDelta = address-currentAddress; + + if (addressDelta > 0) { + debugInstructions.add(new AdvancePC(addressDelta)); + currentAddress = address; + } + } + private class LineEvent implements Event { private final int address; @@ -190,22 +248,145 @@ public class DebugInfoBuilder this.localType = localType; } - public int getAddress() - { + public int getAddress() { return address; } - public void emit(DexFile dexFile, List debugInstructions) - { - int addressDelta = address-currentAddress; - - if (addressDelta > 0) { - debugInstructions.add(new AdvancePC(addressDelta)); - currentAddress = address; - } + public void emit(DexFile dexFile, List debugInstructions) { + emitAdvancePC(address, debugInstructions); debugInstructions.add(new StartLocal(dexFile, registerNum, new StringIdItem(dexFile, localName), new TypeIdItem(dexFile, localType))); } } + + private class StartLocalExtendedEvent implements Event + { + private final int address; + private final int registerNum; + private final String localName; + private final String localType; + private final String signature; + + public StartLocalExtendedEvent(int address, int registerNum, String localName, String localType, + String signature) { + this.address = address; + this.registerNum = registerNum; + this.localName = localName; + this.localType = localType; + this.signature = signature; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, List debugInstructions) { + emitAdvancePC(address, debugInstructions); + + debugInstructions.add(new StartLocalExtended(dexFile, registerNum, new StringIdItem(dexFile, localName), + new TypeIdItem(dexFile, localType), new StringIdItem(dexFile, signature))); + } + } + + private class EndLocalEvent implements Event + { + private final int address; + private final int registerNum; + + public EndLocalEvent(int address, int registerNum) { + this.address = address; + this.registerNum = registerNum; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, List debugInstructions) { + emitAdvancePC(address, debugInstructions); + + debugInstructions.add(new EndLocal(registerNum)); + } + } + + private class RestartLocalEvent implements Event + { + private final int address; + private final int registerNum; + + public RestartLocalEvent(int address, int registerNum) { + this.address = address; + this.registerNum = registerNum; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, List debugInstructions) { + emitAdvancePC(address, debugInstructions); + + debugInstructions.add(new RestartLocal(registerNum)); + } + } + + private class PrologueEvent implements Event + { + private final int address; + + public PrologueEvent(int address) { + this.address = address; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, List debugInstructions) { + emitAdvancePC(address, debugInstructions); + + debugInstructions.add(new SetPrologueEnd()); + } + } + + private class EpilogueEvent implements Event + { + private final int address; + + public EpilogueEvent(int address) { + this.address = address; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, List debugInstructions) { + emitAdvancePC(address, debugInstructions); + + debugInstructions.add(new SetEpilogueBegin()); + } + } + + private class SetFileEvent implements Event + { + private final int address; + private final String fileName; + + public SetFileEvent(int address, String fileName) { + this.address = address; + this.fileName = fileName; + } + + public int getAddress() { + return address; + } + + public void emit(DexFile dexFile, List debugInstructions) { + emitAdvancePC(address, debugInstructions); + + debugInstructions.add(new SetFile(dexFile, new StringIdItem(dexFile, fileName))); + } + } } diff --git a/src/test/resources/examples/HelloWorld2.smali b/src/test/resources/examples/HelloWorld2.smali index 5cadead5..5e0f301f 100644 --- a/src/test/resources/examples/HelloWorld2.smali +++ b/src/test/resources/examples/HelloWorld2.smali @@ -380,6 +380,8 @@ SparseSwitch: .local v0, testVarName Ljava/lang/String; + .prologue + @@ -388,10 +390,14 @@ SparseSwitch: tryStart: new-instance v1, Ljava/lang/Exception; + .local v1, testVarName2 Ljava/lang/String;, "some weird type" + .line 2 ;4 + .end local v0 + invoke-direct {v1}, java/lang/Exception/()V ;7 @@ -401,6 +407,8 @@ SparseSwitch: nop nop + .restart local v0 + .line 5 ;10 @@ -408,11 +416,14 @@ SparseSwitch: return-object v0 + .source "blahblah.java" .line 90 ;11 + .epilogue + .catch Ljava/lang/Exception; {tryStart: .. tryEnd:} handler: handler: