diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g index d11805bb..3acddd66 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliLexer.g @@ -255,6 +255,11 @@ INSTRUCTION_FORMAT12x_PHRASE REGISTER_EMIT WS? ',' WS? REGISTER_EMIT; + +INSTRUCTION_FORMAT20t_PHRASE + : INSTRUCTION_FORMAT20t_EMIT + WS + (LABEL_EMIT | OFFSET_EMIT); INSTRUCTION_FORMAT21c_FIELD_PHRASE : INSTRUCTION_FORMAT21c_FIELD_EMIT @@ -682,6 +687,11 @@ fragment INSTRUCTION_FORMAT12x | 'mul-double/2addr' | 'div-double/2addr' | 'rem-double/2addr'; + +fragment INSTRUCTION_FORMAT20t_EMIT + : INSTRUCTION_FORMAT20t {emit($INSTRUCTION_FORMAT20t, INSTRUCTION_FORMAT20t);}; +fragment INSTRUCTION_FORMAT20t + : 'goto/16'; fragment INSTRUCTION_FORMAT21c_FIELD_EMIT : INSTRUCTION_FORMAT21c_FIELD {emit($INSTRUCTION_FORMAT21c_FIELD, INSTRUCTION_FORMAT21c_FIELD);}; diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g index 627d840f..a7bc2407 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g @@ -56,6 +56,7 @@ tokens { I_STATEMENT_FORMAT11n; I_STATEMENT_FORMAT11x; I_STATEMENT_FORMAT12x; + I_STATEMENT_FORMAT20t; I_STATEMENT_FORMAT21c_TYPE; I_STATEMENT_FORMAT21c_FIELD; I_STATEMENT_FORMAT22c_FIELD; @@ -131,7 +132,7 @@ label instruction returns [int size] : //e.g. goto endloop: //e.g. goto +3 - INSTRUCTION_FORMAT10t (LABEL | OFFSET){$size = Format10t.Format.getByteCount();} + INSTRUCTION_FORMAT10t (LABEL | OFFSET) {$size = Format10t.Format.getByteCount();} -> ^(I_STATEMENT_FORMAT10t[$start, "I_STATEMENT_FORMAT10t"] INSTRUCTION_FORMAT10t LABEL? OFFSET?) | //e.g. return INSTRUCTION_FORMAT10x {$size = Format10x.Format.getByteCount();} @@ -145,6 +146,9 @@ instruction returns [int size] | //e.g. move v1 v2 INSTRUCTION_FORMAT12x REGISTER REGISTER {$size = Format12x.Format.getByteCount();} -> ^(I_STATEMENT_FORMAT12x[$start, "I_STATEMENT_FORMAT12x"] INSTRUCTION_FORMAT12x REGISTER REGISTER) + | //e.g. goto/16 endloop: + INSTRUCTION_FORMAT20t (LABEL | OFFSET) {$size = Format20t.Format.getByteCount();} + -> ^(I_STATEMENT_FORMAT20t[$start, "I_STATEMENT_FORMAT20t"] INSTRUCTION_FORMAT20t LABEL? OFFSET?) | //e.g. sget_object v0 java/lang/System/out LJava/io/PrintStream; INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field {$size = Format21c.Format.getByteCount();} -> ^(I_STATEMENT_FORMAT21c_FIELD[$start, "I_STATEMENT_FORMAT21c_FIELD"] INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field) diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g index 079ee377..06290f97 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g @@ -373,6 +373,20 @@ instruction returns[Instruction instruction] $instruction = Format12x.Format.make(dexFile, opcode.value, regA, regB); } + | //e.g. goto/16 endloop: + ^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT20t.text); + + int addressOffset = $offset_or_label.offsetValue; + + if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { + //TODO: throw correct exception type + throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + } + + $instruction = Format20t.Format.make(dexFile, opcode.value, (byte)addressOffset); + } | //e.g. sget_object v0 java/lang/System/out LJava/io/PrintStream; ^(I_STATEMENT_FORMAT21c_FIELD INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field) { diff --git a/src/main/java/org/JesusFreke/dexlib/code/Format/Format20t.java b/src/main/java/org/JesusFreke/dexlib/code/Format/Format20t.java new file mode 100644 index 00000000..ef938cd9 --- /dev/null +++ b/src/main/java/org/JesusFreke/dexlib/code/Format/Format20t.java @@ -0,0 +1,69 @@ +/* + * [The "BSD licence"] + * Copyright (c) 2009 Ben Gruver + * 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.JesusFreke.dexlib.code.Format; + +import org.JesusFreke.dexlib.code.Instruction; +import org.JesusFreke.dexlib.code.Opcode; +import org.JesusFreke.dexlib.DexFile; + +public class Format20t extends Format +{ + public static final Format20t Format = new Format20t(); + + private Format20t() { + } + + public Instruction make(DexFile dexFile, byte opcode, short offA) { + byte[] bytes = new byte[4]; + + Opcode op = Opcode.getOpcodeByValue(opcode); + + checkOpcodeFormat(op); + + if (offA == 0) { + throw new RuntimeException("The offset cannot be 0. Use goto/32 instead."); + } + + bytes[0] = opcode; + bytes[2] = (byte)offA; + bytes[3] = (byte)(offA >> 8); + + return new Instruction(dexFile, bytes, null); + } + + public int getByteCount() + { + return 4; + } + + public String getFormatName() + { + return "20t"; + } +} diff --git a/src/test/resources/examples/HelloWorld2.smali b/src/test/resources/examples/HelloWorld2.smali index 7bce08f4..20f6edbf 100644 --- a/src/test/resources/examples/HelloWorld2.smali +++ b/src/test/resources/examples/HelloWorld2.smali @@ -18,8 +18,9 @@ ;0 ;-8 ;7 -;But this should! -;But this should! +;Format10t with a label +;Format10t with an offset +;Format20t with a label .method static constructor ()V ;test @@ -152,7 +153,7 @@ const-string v1, "This shouldn't be displayed!" SKIP: - const-string v1,"But this should!" + const-string v1,"Format10t with a label" invoke-virtual {v2, v1}, java/lang/String/concat(Ljava/lang/String;)Ljava/lang/String; move-result-object v2 @@ -166,7 +167,24 @@ const-string v1, "This shouldn't be displayed!" - const-string v1,"But this should!" + const-string v1,"Format10t with an offset" + + invoke-virtual {v2, v1}, java/lang/String/concat(Ljava/lang/String;)Ljava/lang/String; + move-result-object v2 + + invoke-virtual {v2, v3}, java/lang/String/concat(Ljava/lang/String;)Ljava/lang/String; + move-result-object v2 + + + + + ;test format20t with a label + goto/16 SKIP2: + + const-string v1, "This shouldn't be displayed!" + + SKIP2: + const-string v1,"Format20t with a label" invoke-virtual {v2, v1}, java/lang/String/concat(Ljava/lang/String;)Ljava/lang/String; move-result-object v2