diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java index d9ebe139..5a79b9e0 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java @@ -1069,6 +1069,9 @@ public class MethodAnalyzer { case INVOKE_DIRECT_EMPTY: analyzeInvokeDirectEmpty(analyzedInstruction); return true; + case INVOKE_OBJECT_INIT_RANGE: + analyzeInvokeObjectInitRange(analyzedInstruction); + return true; case IGET_QUICK: case IGET_WIDE_QUICK: case IGET_OBJECT_QUICK: @@ -1568,6 +1571,7 @@ public class MethodAnalyzer { case EXECUTE_INLINE: case EXECUTE_INLINE_RANGE: case INVOKE_DIRECT_EMPTY: + case INVOKE_OBJECT_INIT_RANGE: case IGET_QUICK: case IGET_WIDE_QUICK: case IGET_OBJECT_QUICK: @@ -3470,6 +3474,17 @@ public class MethodAnalyzer { analyzeInstruction(analyzedInstruction); } + private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction) { + Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction; + + Instruction3rc deodexedInstruction = new Instruction3rc(Opcode.INVOKE_DIRECT_RANGE, + (short)instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem()); + + analyzedInstruction.setDeodexedInstruction(deodexedInstruction); + + analyzeInstruction(analyzedInstruction); + } + private boolean analyzeIputIgetQuick(AnalyzedInstruction analyzedInstruction) { Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction; diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java index 4beace5b..c2abc621 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java @@ -117,7 +117,8 @@ public class Instruction3rc extends InstructionWithReference implements Register if (type.charAt(1) == 'J' || type.charAt(1) == 'D') { throw new RuntimeException("The type cannot be an array of longs or doubles"); } - } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value) { + } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value || + opcode == INVOKE_OBJECT_INIT_RANGE) { //check data for invoke-*/range opcodes MethodIdItem methodIdItem = (MethodIdItem) item; int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount(); diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java index 1c332e33..93bbdb7c 100644 --- a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java +++ b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java @@ -267,6 +267,7 @@ public enum Opcode EXECUTE_INLINE((short)0xee, "execute-inline", ReferenceType.none, Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), EXECUTE_INLINE_RANGE((short)0xef, "execute-inline/range", ReferenceType.none, Format.Format3rmi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), INVOKE_DIRECT_EMPTY((short)0xf0, "invoke-direct-empty", ReferenceType.method, Format.Format35s, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), + INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", ReferenceType.method, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT), IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER), IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", ReferenceType.none, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER), @@ -351,13 +352,16 @@ public enum Opcode opcodesByName = new HashMap(); for (Opcode opcode: Opcode.values()) { - if (((opcode.value >> 8) & 0xFF) == 0x00) { - opcodesByValue[opcode.value & 0xFF] = opcode; - } else { - assert ((opcode.value >> 8) & 0xFF) == 0xFF; - expandedOpcodesByValue[opcode.value & 0xFF] = opcode; + //INVOKE_DIRECT_EMPTY was changed to INVOKE_OBJECT_INIT_RANGE in ICS + if (opcode != INVOKE_DIRECT_EMPTY) { + if (((opcode.value >> 8) & 0xFF) == 0x00) { + opcodesByValue[opcode.value & 0xFF] = opcode; + } else { + assert ((opcode.value >> 8) & 0xFF) == 0xFF; + expandedOpcodesByValue[opcode.value & 0xFF] = opcode; + } + opcodesByName.put(opcode.name.hashCode(), opcode); } - opcodesByName.put(opcode.name.hashCode(), opcode); } } @@ -386,6 +390,18 @@ public enum Opcode } } + private static void addOpcodes(Opcode... toAdd) { + for (Opcode opcode: toAdd) { + if (((opcode.value >> 8) & 0xFF) == 0x00) { + opcodesByValue[opcode.value & 0xFF] = opcode; + } else { + assert ((opcode.value >> 8) & 0xFF) == 0xFF; + expandedOpcodesByValue[opcode.value & 0xFF] = opcode; + } + opcodesByName.put(opcode.name.hashCode(), opcode); + } + } + /** * This will add/remove/replace various opcodes in the value/name maps as needed, * based on the idiosyncrasies of that api level @@ -411,7 +427,9 @@ public enum Opcode SGET_JUMBO, SGET_WIDE_JUMBO, SGET_OBJECT_JUMBO, SGET_BOOLEAN_JUMBO, SGET_BYTE_JUMBO, SGET_CHAR_JUMBO, SGET_SHORT_JUMBO, SPUT_JUMBO, SPUT_WIDE_JUMBO, SPUT_OBJECT_JUMBO, SPUT_BOOLEAN_JUMBO, SPUT_BYTE_JUMBO, SPUT_CHAR_JUMBO, SPUT_SHORT_JUMBO, INVOKE_VIRTUAL_JUMBO, - INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO); + INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO, + INVOKE_OBJECT_INIT_RANGE); + addOpcodes(INVOKE_DIRECT_EMPTY); } } diff --git a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g index 445c5ea8..d1984b1d 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g @@ -680,6 +680,9 @@ INSTRUCTION_FORMAT3rc_METHOD | 'invoke-static/range' | 'invoke-interface/range'; +INSTRUCTION_FORMAT3rc_METHOD_ODEX + : 'invoke-object-init/range'; + INSTRUCTION_FORMAT3rc_TYPE : 'filled-new-array/range'; diff --git a/smali/src/main/antlr3/org/jf/smali/smaliParser.g b/smali/src/main/antlr3/org/jf/smali/smaliParser.g index 53b3bc92..3dc77e85 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliParser.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliParser.g @@ -827,6 +827,11 @@ instruction returns [int size] | //e.g. invoke-virtual/range {v25..v26}, java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; INSTRUCTION_FORMAT3rc_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA fully_qualified_method {$size = Format.Format3rc.size;} -> ^(I_STATEMENT_FORMAT3rc_METHOD[$start, "I_STATEMENT_FORMAT3rc_METHOD"] INSTRUCTION_FORMAT3rc_METHOD register_range fully_qualified_method) + | //e.g. invoke-object-init/range {p0}, Ljava/lang/Object;->()V + INSTRUCTION_FORMAT3rc_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method + { + throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rc_METHOD_ODEX.text); + } | //e.g. filled-new-array/range {v0..v6}, I INSTRUCTION_FORMAT3rc_TYPE OPEN_BRACE register_range CLOSE_BRACE COMMA nonvoid_type_descriptor {$size = Format.Format3rc.size;} -> ^(I_STATEMENT_FORMAT3rc_TYPE[$start, "I_STATEMENT_FORMAT3rc_TYPE"] INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor) diff --git a/smali/src/main/jflex/smaliLexer.flex b/smali/src/main/jflex/smaliLexer.flex index c374292b..ff72c3c1 100644 --- a/smali/src/main/jflex/smaliLexer.flex +++ b/smali/src/main/jflex/smaliLexer.flex @@ -550,6 +550,10 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor} return newToken(INSTRUCTION_FORMAT3rc_METHOD); } + "invoke-object-init/range" { + return newToken(INSTRUCTION_FORMAT3rc_METHOD_ODEX); + } + "filled-new-array/range" { return newToken(INSTRUCTION_FORMAT3rc_TYPE); } diff --git a/smali/src/test/resources/LexerTest/InstructionTest.smali b/smali/src/test/resources/LexerTest/InstructionTest.smali index 778e3c30..d6207936 100644 --- a/smali/src/test/resources/LexerTest/InstructionTest.smali +++ b/smali/src/test/resources/LexerTest/InstructionTest.smali @@ -210,6 +210,7 @@ invoke-static invoke-interface filled-new-array invoke-direct-empty +invoke-object-init/range throw-verification-error execute-inline invoke-virtual-quick diff --git a/smali/src/test/resources/LexerTest/InstructionTest.tokens b/smali/src/test/resources/LexerTest/InstructionTest.tokens index 3388fa4e..7b4860c3 100644 --- a/smali/src/test/resources/LexerTest/InstructionTest.tokens +++ b/smali/src/test/resources/LexerTest/InstructionTest.tokens @@ -210,6 +210,7 @@ INSTRUCTION_FORMAT35c_METHOD("invoke-static") INSTRUCTION_FORMAT35c_METHOD("invoke-interface") INSTRUCTION_FORMAT35c_TYPE("filled-new-array") INSTRUCTION_FORMAT35s_METHOD("invoke-direct-empty") +INSTRUCTION_FORMAT3rc_METHOD_ODEX("invoke-object-init/range") INSTRUCTION_FORMAT20bc("throw-verification-error") INSTRUCTION_FORMAT35mi_METHOD("execute-inline") INSTRUCTION_FORMAT35ms_METHOD("invoke-virtual-quick")