| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "slicer/dex_bytecode.h" |
| #include "slicer/common.h" |
| |
| #include <assert.h> |
| #include <array> |
| |
| namespace dex { |
| |
| Opcode OpcodeFromBytecode(u2 bytecode) { |
| Opcode opcode = Opcode(bytecode & 0xff); |
| SLICER_CHECK(opcode != OP_UNUSED_FF); |
| return opcode; |
| } |
| |
| // Table that maps each opcode to the index type implied by that opcode |
| static constexpr std::array<InstructionIndexType, kNumPackedOpcodes> |
| gInstructionIndexTypeTable = { |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexStringRef, |
| kIndexStringRef, kIndexTypeRef, kIndexNone, |
| kIndexNone, kIndexTypeRef, kIndexTypeRef, |
| kIndexNone, kIndexTypeRef, kIndexTypeRef, |
| kIndexTypeRef, kIndexTypeRef, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexUnknown, |
| kIndexUnknown, kIndexUnknown, kIndexUnknown, |
| kIndexUnknown, kIndexUnknown, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexMethodRef, |
| kIndexMethodRef, kIndexMethodRef, kIndexMethodRef, |
| kIndexMethodRef, kIndexUnknown, kIndexMethodRef, |
| kIndexMethodRef, kIndexMethodRef, kIndexMethodRef, |
| kIndexMethodRef, kIndexUnknown, kIndexUnknown, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexNone, |
| kIndexNone, kIndexNone, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexFieldRef, kIndexFieldRef, kIndexUnknown, |
| kIndexVaries, kIndexInlineMethod, kIndexInlineMethod, |
| kIndexMethodRef, kIndexNone, kIndexFieldOffset, |
| kIndexFieldOffset, kIndexFieldOffset, kIndexFieldOffset, |
| kIndexFieldOffset, kIndexFieldOffset, kIndexVtableOffset, |
| kIndexVtableOffset, kIndexVtableOffset, kIndexVtableOffset, |
| kIndexFieldRef, kIndexFieldRef, kIndexFieldRef, |
| kIndexUnknown, |
| }; |
| |
| InstructionIndexType GetIndexTypeFromOpcode(Opcode opcode) { |
| return gInstructionIndexTypeTable[opcode]; |
| } |
| |
| // Table that maps each opcode to the full width of instructions that |
| // use that opcode, in (16-bit) code units. Unimplemented opcodes as |
| // well as the "breakpoint" opcode have a width of zero. |
| static constexpr std::array<u1, kNumPackedOpcodes> gInstructionWidthTable = { |
| 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 2, 3, |
| 5, 2, 2, 3, 2, 1, 1, 2, 2, 1, 2, 2, 3, 3, 3, 1, 1, 2, 3, 3, 3, 2, 2, 2, |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, |
| 3, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, |
| 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 3, 3, |
| 3, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 0, |
| }; |
| |
| size_t GetWidthFromOpcode(Opcode opcode) { |
| return gInstructionWidthTable[opcode]; |
| } |
| |
| size_t GetWidthFromBytecode(const u2* bytecode) { |
| size_t width = 0; |
| if (*bytecode == kPackedSwitchSignature) { |
| width = 4 + bytecode[1] * 2; |
| } else if (*bytecode == kSparseSwitchSignature) { |
| width = 2 + bytecode[1] * 4; |
| } else if (*bytecode == kArrayDataSignature) { |
| u2 elemWidth = bytecode[1]; |
| u4 len = bytecode[2] | (((u4)bytecode[3]) << 16); |
| // The plus 1 is to round up for odd size and width. |
| width = 4 + (elemWidth * len + 1) / 2; |
| } else { |
| width = GetWidthFromOpcode(OpcodeFromBytecode(bytecode[0])); |
| } |
| return width; |
| } |
| |
| // Table that maps each opcode to the instruction flags |
| static constexpr std::array<OpcodeFlags, kNumPackedOpcodes> gOpcodeFlagsTable = { |
| /* NOP */ kInstrCanContinue, |
| /* MOVE */ kInstrCanContinue, |
| /* MOVE_FROM16 */ kInstrCanContinue, |
| /* MOVE_16 */ kInstrCanContinue, |
| /* MOVE_WIDE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* MOVE_WIDE_FROM16 */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* MOVE_WIDE_16 */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* MOVE_OBJECT */ kInstrCanContinue, |
| /* MOVE_OBJECT_FROM16 */ kInstrCanContinue, |
| /* MOVE_OBJECT_16 */ kInstrCanContinue, |
| /* MOVE_RESULT */ kInstrCanContinue, |
| /* MOVE_RESULT_WIDE */ kInstrCanContinue|kInstrWideRegA, |
| /* MOVE_RESULT_OBJECT */ kInstrCanContinue, |
| /* MOVE_EXCEPTION */ kInstrCanContinue, |
| /* RETURN_VOID */ kInstrCanReturn, |
| /* RETURN */ kInstrCanReturn, |
| /* RETURN_WIDE */ kInstrCanReturn|kInstrWideRegA, |
| /* RETURN_OBJECT */ kInstrCanReturn, |
| /* CONST_4 */ kInstrCanContinue, |
| /* CONST_16 */ kInstrCanContinue, |
| /* CONST */ kInstrCanContinue, |
| /* CONST_HIGH16 */ kInstrCanContinue, |
| /* CONST_WIDE_16 */ kInstrCanContinue|kInstrWideRegA, |
| /* CONST_WIDE_32 */ kInstrCanContinue|kInstrWideRegA, |
| /* CONST_WIDE */ kInstrCanContinue|kInstrWideRegA, |
| /* CONST_WIDE_HIGH16 */ kInstrCanContinue|kInstrWideRegA, |
| /* CONST_STRING */ kInstrCanContinue|kInstrCanThrow, |
| /* CONST_STRING_JUMBO */ kInstrCanContinue|kInstrCanThrow, |
| /* CONST_CLASS */ kInstrCanContinue|kInstrCanThrow, |
| /* MONITOR_ENTER */ kInstrCanContinue|kInstrCanThrow, |
| /* MONITOR_EXIT */ kInstrCanContinue|kInstrCanThrow, |
| /* SLICER_CHECK_CAST */ kInstrCanContinue|kInstrCanThrow, |
| /* INSTANCE_OF */ kInstrCanContinue|kInstrCanThrow, |
| /* ARRAY_LENGTH */ kInstrCanContinue|kInstrCanThrow, |
| /* NEW_INSTANCE */ kInstrCanContinue|kInstrCanThrow, |
| /* NEW_ARRAY */ kInstrCanContinue|kInstrCanThrow, |
| /* FILLED_NEW_ARRAY */ kInstrCanContinue|kInstrCanThrow, |
| /* FILLED_NEW_ARRAY_RANGE */ kInstrCanContinue|kInstrCanThrow, |
| /* FILL_ARRAY_DATA */ kInstrCanContinue, |
| /* THROW */ kInstrCanThrow, |
| /* GOTO */ kInstrCanBranch, |
| /* GOTO_16 */ kInstrCanBranch, |
| /* GOTO_32 */ kInstrCanBranch, |
| /* PACKED_SWITCH */ kInstrCanContinue|kInstrCanSwitch, |
| /* SPARSE_SWITCH */ kInstrCanContinue|kInstrCanSwitch, |
| /* CMPL_FLOAT */ kInstrCanContinue, |
| /* CMPG_FLOAT */ kInstrCanContinue, |
| /* CMPL_DOUBLE */ kInstrCanContinue|kInstrWideRegB|kInstrWideRegC, |
| /* CMPG_DOUBLE */ kInstrCanContinue|kInstrWideRegB|kInstrWideRegC, |
| /* CMP_LONG */ kInstrCanContinue|kInstrWideRegB|kInstrWideRegC, |
| /* IF_EQ */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_NE */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_LT */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_GE */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_GT */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_LE */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_EQZ */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_NEZ */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_LTZ */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_GEZ */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_GTZ */ kInstrCanContinue|kInstrCanBranch, |
| /* IF_LEZ */ kInstrCanContinue|kInstrCanBranch, |
| /* UNUSED_3E */ 0, |
| /* UNUSED_3F */ 0, |
| /* UNUSED_40 */ 0, |
| /* UNUSED_41 */ 0, |
| /* UNUSED_42 */ 0, |
| /* UNUSED_43 */ 0, |
| /* AGET */ kInstrCanContinue|kInstrCanThrow, |
| /* AGET_WIDE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* AGET_OBJECT */ kInstrCanContinue|kInstrCanThrow, |
| /* AGET_BOOLEAN */ kInstrCanContinue|kInstrCanThrow, |
| /* AGET_BYTE */ kInstrCanContinue|kInstrCanThrow, |
| /* AGET_CHAR */ kInstrCanContinue|kInstrCanThrow, |
| /* AGET_SHORT */ kInstrCanContinue|kInstrCanThrow, |
| /* APUT */ kInstrCanContinue|kInstrCanThrow, |
| /* APUT_WIDE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* APUT_OBJECT */ kInstrCanContinue|kInstrCanThrow, |
| /* APUT_BOOLEAN */ kInstrCanContinue|kInstrCanThrow, |
| /* APUT_BYTE */ kInstrCanContinue|kInstrCanThrow, |
| /* APUT_CHAR */ kInstrCanContinue|kInstrCanThrow, |
| /* APUT_SHORT */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_WIDE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* IGET_OBJECT */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_BOOLEAN */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_BYTE */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_CHAR */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_SHORT */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_WIDE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* IPUT_OBJECT */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_BOOLEAN */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_BYTE */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_CHAR */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_SHORT */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET_WIDE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* SGET_OBJECT */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET_BOOLEAN */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET_BYTE */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET_CHAR */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET_SHORT */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT_WIDE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* SPUT_OBJECT */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT_BOOLEAN */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT_BYTE */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT_CHAR */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT_SHORT */ kInstrCanContinue|kInstrCanThrow, |
| /* INVOKE_VIRTUAL */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_SUPER */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_DIRECT */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_STATIC */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_INTERFACE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* UNUSED_73 */ 0, |
| /* INVOKE_VIRTUAL_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_SUPER_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_DIRECT_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_STATIC_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_INTERFACE_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* UNUSED_79 */ 0, |
| /* UNUSED_7A */ 0, |
| /* NEG_INT */ kInstrCanContinue, |
| /* NOT_INT */ kInstrCanContinue, |
| /* NEG_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* NOT_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* NEG_FLOAT */ kInstrCanContinue, |
| /* NEG_DOUBLE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* INT_TO_LONG */ kInstrCanContinue|kInstrWideRegA, |
| /* INT_TO_FLOAT */ kInstrCanContinue, |
| /* INT_TO_DOUBLE */ kInstrCanContinue|kInstrWideRegA, |
| /* LONG_TO_INT */ kInstrCanContinue|kInstrWideRegB, |
| /* LONG_TO_FLOAT */ kInstrCanContinue|kInstrWideRegB, |
| /* LONG_TO_DOUBLE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* FLOAT_TO_INT */ kInstrCanContinue, |
| /* FLOAT_TO_LONG */ kInstrCanContinue|kInstrWideRegA, |
| /* FLOAT_TO_DOUBLE */ kInstrCanContinue|kInstrWideRegA, |
| /* DOUBLE_TO_INT */ kInstrCanContinue|kInstrWideRegB, |
| /* DOUBLE_TO_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* DOUBLE_TO_FLOAT */ kInstrCanContinue|kInstrWideRegB, |
| /* INT_TO_BYTE */ kInstrCanContinue, |
| /* INT_TO_CHAR */ kInstrCanContinue, |
| /* INT_TO_SHORT */ kInstrCanContinue, |
| /* ADD_INT */ kInstrCanContinue, |
| /* SUB_INT */ kInstrCanContinue, |
| /* MUL_INT */ kInstrCanContinue, |
| /* DIV_INT */ kInstrCanContinue|kInstrCanThrow, |
| /* REM_INT */ kInstrCanContinue|kInstrCanThrow, |
| /* AND_INT */ kInstrCanContinue, |
| /* OR_INT */ kInstrCanContinue, |
| /* XOR_INT */ kInstrCanContinue, |
| /* SHL_INT */ kInstrCanContinue, |
| /* SHR_INT */ kInstrCanContinue, |
| /* USHR_INT */ kInstrCanContinue, |
| /* ADD_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* SUB_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* MUL_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* DIV_LONG */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* REM_LONG */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* AND_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* OR_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* XOR_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* SHL_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* SHR_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* USHR_LONG */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* ADD_FLOAT */ kInstrCanContinue, |
| /* SUB_FLOAT */ kInstrCanContinue, |
| /* MUL_FLOAT */ kInstrCanContinue, |
| /* DIV_FLOAT */ kInstrCanContinue, |
| /* REM_FLOAT */ kInstrCanContinue, |
| /* ADD_DOUBLE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* SUB_DOUBLE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* MUL_DOUBLE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* DIV_DOUBLE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* REM_DOUBLE */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB|kInstrWideRegC, |
| /* ADD_INT_2ADDR */ kInstrCanContinue, |
| /* SUB_INT_2ADDR */ kInstrCanContinue, |
| /* MUL_INT_2ADDR */ kInstrCanContinue, |
| /* DIV_INT_2ADDR */ kInstrCanContinue|kInstrCanThrow, |
| /* REM_INT_2ADDR */ kInstrCanContinue|kInstrCanThrow, |
| /* AND_INT_2ADDR */ kInstrCanContinue, |
| /* OR_INT_2ADDR */ kInstrCanContinue, |
| /* XOR_INT_2ADDR */ kInstrCanContinue, |
| /* SHL_INT_2ADDR */ kInstrCanContinue, |
| /* SHR_INT_2ADDR */ kInstrCanContinue, |
| /* USHR_INT_2ADDR */ kInstrCanContinue, |
| /* ADD_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* SUB_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* MUL_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* DIV_LONG_2ADDR */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA|kInstrWideRegB, |
| /* REM_LONG_2ADDR */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA|kInstrWideRegB, |
| /* AND_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* OR_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* XOR_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* SHL_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA, |
| /* SHR_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA, |
| /* USHR_LONG_2ADDR */ kInstrCanContinue|kInstrWideRegA, |
| /* ADD_FLOAT_2ADDR */ kInstrCanContinue, |
| /* SUB_FLOAT_2ADDR */ kInstrCanContinue, |
| /* MUL_FLOAT_2ADDR */ kInstrCanContinue, |
| /* DIV_FLOAT_2ADDR */ kInstrCanContinue, |
| /* REM_FLOAT_2ADDR */ kInstrCanContinue, |
| /* ADD_DOUBLE_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* SUB_DOUBLE_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* MUL_DOUBLE_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* DIV_DOUBLE_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* REM_DOUBLE_2ADDR */ kInstrCanContinue|kInstrWideRegA|kInstrWideRegB, |
| /* ADD_INT_LIT16 */ kInstrCanContinue, |
| /* RSUB_INT */ kInstrCanContinue, |
| /* MUL_INT_LIT16 */ kInstrCanContinue, |
| /* DIV_INT_LIT16 */ kInstrCanContinue|kInstrCanThrow, |
| /* REM_INT_LIT16 */ kInstrCanContinue|kInstrCanThrow, |
| /* AND_INT_LIT16 */ kInstrCanContinue, |
| /* OR_INT_LIT16 */ kInstrCanContinue, |
| /* XOR_INT_LIT16 */ kInstrCanContinue, |
| /* ADD_INT_LIT8 */ kInstrCanContinue, |
| /* RSUB_INT_LIT8 */ kInstrCanContinue, |
| /* MUL_INT_LIT8 */ kInstrCanContinue, |
| /* DIV_INT_LIT8 */ kInstrCanContinue|kInstrCanThrow, |
| /* REM_INT_LIT8 */ kInstrCanContinue|kInstrCanThrow, |
| /* AND_INT_LIT8 */ kInstrCanContinue, |
| /* OR_INT_LIT8 */ kInstrCanContinue, |
| /* XOR_INT_LIT8 */ kInstrCanContinue, |
| /* SHL_INT_LIT8 */ kInstrCanContinue, |
| /* SHR_INT_LIT8 */ kInstrCanContinue, |
| /* USHR_INT_LIT8 */ kInstrCanContinue, |
| /* IGET_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_OBJECT_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_WIDE_VOLATILE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* IPUT_WIDE_VOLATILE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* SGET_WIDE_VOLATILE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* SPUT_WIDE_VOLATILE */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* BREAKPOINT */ 0, |
| /* THROW_VERIFICATION_ERROR */ kInstrCanThrow, |
| /* EXECUTE_INLINE */ kInstrCanContinue|kInstrCanThrow, |
| /* EXECUTE_INLINE_RANGE */ kInstrCanContinue|kInstrCanThrow, |
| /* INVOKE_OBJECT_INIT_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* RETURN_VOID_BARRIER */ kInstrCanReturn, |
| /* IGET_QUICK */ kInstrCanContinue|kInstrCanThrow, |
| /* IGET_WIDE_QUICK */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* IGET_OBJECT_QUICK */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_QUICK */ kInstrCanContinue|kInstrCanThrow, |
| /* IPUT_WIDE_QUICK */ kInstrCanContinue|kInstrCanThrow|kInstrWideRegA, |
| /* IPUT_OBJECT_QUICK */ kInstrCanContinue|kInstrCanThrow, |
| /* INVOKE_VIRTUAL_QUICK */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_VIRTUAL_QUICK_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_SUPER_QUICK */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* INVOKE_SUPER_QUICK_RANGE */ kInstrCanContinue|kInstrCanThrow|kInstrInvoke, |
| /* IPUT_OBJECT_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* SGET_OBJECT_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* SPUT_OBJECT_VOLATILE */ kInstrCanContinue|kInstrCanThrow, |
| /* UNUSED_FF */ 0, |
| }; |
| |
| // Table that maps each opcode to the instruction format |
| static constexpr std::array<InstructionFormat, kNumPackedOpcodes> gInstructionFormatTable = { |
| kFmt10x, kFmt12x, kFmt22x, kFmt32x, kFmt12x, kFmt22x, kFmt32x, |
| kFmt12x, kFmt22x, kFmt32x, kFmt11x, kFmt11x, kFmt11x, kFmt11x, |
| kFmt10x, kFmt11x, kFmt11x, kFmt11x, kFmt11n, kFmt21s, kFmt31i, |
| kFmt21h, kFmt21s, kFmt31i, kFmt51l, kFmt21h, kFmt21c, kFmt31c, |
| kFmt21c, kFmt11x, kFmt11x, kFmt21c, kFmt22c, kFmt12x, kFmt21c, |
| kFmt22c, kFmt35c, kFmt3rc, kFmt31t, kFmt11x, kFmt10t, kFmt20t, |
| kFmt30t, kFmt31t, kFmt31t, kFmt23x, kFmt23x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt22t, kFmt22t, kFmt22t, kFmt22t, kFmt22t, kFmt22t, |
| kFmt21t, kFmt21t, kFmt21t, kFmt21t, kFmt21t, kFmt21t, kFmt00x, |
| kFmt00x, kFmt00x, kFmt00x, kFmt00x, kFmt00x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt22c, kFmt22c, |
| kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c, |
| kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt21c, kFmt21c, |
| kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c, |
| kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt35c, kFmt35c, |
| kFmt35c, kFmt35c, kFmt35c, kFmt00x, kFmt3rc, kFmt3rc, kFmt3rc, |
| kFmt3rc, kFmt3rc, kFmt00x, kFmt00x, kFmt12x, kFmt12x, kFmt12x, |
| kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, |
| kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, |
| kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt23x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, |
| kFmt23x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, |
| kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, |
| kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, |
| kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, |
| kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt22s, kFmt22s, |
| kFmt22s, kFmt22s, kFmt22s, kFmt22s, kFmt22s, kFmt22s, kFmt22b, |
| kFmt22b, kFmt22b, kFmt22b, kFmt22b, kFmt22b, kFmt22b, kFmt22b, |
| kFmt22b, kFmt22b, kFmt22b, kFmt22c, kFmt22c, kFmt21c, kFmt21c, |
| kFmt22c, kFmt22c, kFmt22c, kFmt21c, kFmt21c, kFmt00x, kFmt20bc, |
| kFmt35mi, kFmt3rmi, kFmt35c, kFmt10x, kFmt22cs, kFmt22cs, kFmt22cs, |
| kFmt22cs, kFmt22cs, kFmt22cs, kFmt35ms, kFmt3rms, kFmt35ms, kFmt3rms, |
| kFmt22c, kFmt21c, kFmt21c, kFmt00x, |
| }; |
| |
| InstructionFormat GetFormatFromOpcode(Opcode opcode) { |
| return gInstructionFormatTable[opcode]; |
| } |
| |
| OpcodeFlags GetFlagsFromOpcode(Opcode opcode) { |
| return gOpcodeFlagsTable[opcode]; |
| } |
| |
| // Dalvik opcode names. |
| static constexpr std::array<const char*, kNumPackedOpcodes> gOpcodeNames = { |
| "nop", |
| "move", |
| "move/from16", |
| "move/16", |
| "move-wide", |
| "move-wide/from16", |
| "move-wide/16", |
| "move-object", |
| "move-object/from16", |
| "move-object/16", |
| "move-result", |
| "move-result-wide", |
| "move-result-object", |
| "move-exception", |
| "return-void", |
| "return", |
| "return-wide", |
| "return-object", |
| "const/4", |
| "const/16", |
| "const", |
| "const/high16", |
| "const-wide/16", |
| "const-wide/32", |
| "const-wide", |
| "const-wide/high16", |
| "const-string", |
| "const-string/jumbo", |
| "const-class", |
| "monitor-enter", |
| "monitor-exit", |
| "check-cast", |
| "instance-of", |
| "array-length", |
| "new-instance", |
| "new-array", |
| "filled-new-array", |
| "filled-new-array/range", |
| "fill-array-data", |
| "throw", |
| "goto", |
| "goto/16", |
| "goto/32", |
| "packed-switch", |
| "sparse-switch", |
| "cmpl-float", |
| "cmpg-float", |
| "cmpl-double", |
| "cmpg-double", |
| "cmp-long", |
| "if-eq", |
| "if-ne", |
| "if-lt", |
| "if-ge", |
| "if-gt", |
| "if-le", |
| "if-eqz", |
| "if-nez", |
| "if-ltz", |
| "if-gez", |
| "if-gtz", |
| "if-lez", |
| "unused-3e", |
| "unused-3f", |
| "unused-40", |
| "unused-41", |
| "unused-42", |
| "unused-43", |
| "aget", |
| "aget-wide", |
| "aget-object", |
| "aget-boolean", |
| "aget-byte", |
| "aget-char", |
| "aget-short", |
| "aput", |
| "aput-wide", |
| "aput-object", |
| "aput-boolean", |
| "aput-byte", |
| "aput-char", |
| "aput-short", |
| "iget", |
| "iget-wide", |
| "iget-object", |
| "iget-boolean", |
| "iget-byte", |
| "iget-char", |
| "iget-short", |
| "iput", |
| "iput-wide", |
| "iput-object", |
| "iput-boolean", |
| "iput-byte", |
| "iput-char", |
| "iput-short", |
| "sget", |
| "sget-wide", |
| "sget-object", |
| "sget-boolean", |
| "sget-byte", |
| "sget-char", |
| "sget-short", |
| "sput", |
| "sput-wide", |
| "sput-object", |
| "sput-boolean", |
| "sput-byte", |
| "sput-char", |
| "sput-short", |
| "invoke-virtual", |
| "invoke-super", |
| "invoke-direct", |
| "invoke-static", |
| "invoke-interface", |
| "unused-73", |
| "invoke-virtual/range", |
| "invoke-super/range", |
| "invoke-direct/range", |
| "invoke-static/range", |
| "invoke-interface/range", |
| "unused-79", |
| "unused-7a", |
| "neg-int", |
| "not-int", |
| "neg-long", |
| "not-long", |
| "neg-float", |
| "neg-double", |
| "int-to-long", |
| "int-to-float", |
| "int-to-double", |
| "long-to-int", |
| "long-to-float", |
| "long-to-double", |
| "float-to-int", |
| "float-to-long", |
| "float-to-double", |
| "double-to-int", |
| "double-to-long", |
| "double-to-float", |
| "int-to-byte", |
| "int-to-char", |
| "int-to-short", |
| "add-int", |
| "sub-int", |
| "mul-int", |
| "div-int", |
| "rem-int", |
| "and-int", |
| "or-int", |
| "xor-int", |
| "shl-int", |
| "shr-int", |
| "ushr-int", |
| "add-long", |
| "sub-long", |
| "mul-long", |
| "div-long", |
| "rem-long", |
| "and-long", |
| "or-long", |
| "xor-long", |
| "shl-long", |
| "shr-long", |
| "ushr-long", |
| "add-float", |
| "sub-float", |
| "mul-float", |
| "div-float", |
| "rem-float", |
| "add-double", |
| "sub-double", |
| "mul-double", |
| "div-double", |
| "rem-double", |
| "add-int/2addr", |
| "sub-int/2addr", |
| "mul-int/2addr", |
| "div-int/2addr", |
| "rem-int/2addr", |
| "and-int/2addr", |
| "or-int/2addr", |
| "xor-int/2addr", |
| "shl-int/2addr", |
| "shr-int/2addr", |
| "ushr-int/2addr", |
| "add-long/2addr", |
| "sub-long/2addr", |
| "mul-long/2addr", |
| "div-long/2addr", |
| "rem-long/2addr", |
| "and-long/2addr", |
| "or-long/2addr", |
| "xor-long/2addr", |
| "shl-long/2addr", |
| "shr-long/2addr", |
| "ushr-long/2addr", |
| "add-float/2addr", |
| "sub-float/2addr", |
| "mul-float/2addr", |
| "div-float/2addr", |
| "rem-float/2addr", |
| "add-double/2addr", |
| "sub-double/2addr", |
| "mul-double/2addr", |
| "div-double/2addr", |
| "rem-double/2addr", |
| "add-int/lit16", |
| "rsub-int", |
| "mul-int/lit16", |
| "div-int/lit16", |
| "rem-int/lit16", |
| "and-int/lit16", |
| "or-int/lit16", |
| "xor-int/lit16", |
| "add-int/lit8", |
| "rsub-int/lit8", |
| "mul-int/lit8", |
| "div-int/lit8", |
| "rem-int/lit8", |
| "and-int/lit8", |
| "or-int/lit8", |
| "xor-int/lit8", |
| "shl-int/lit8", |
| "shr-int/lit8", |
| "ushr-int/lit8", |
| "+iget-volatile", |
| "+iput-volatile", |
| "+sget-volatile", |
| "+sput-volatile", |
| "+iget-object-volatile", |
| "+iget-wide-volatile", |
| "+iput-wide-volatile", |
| "+sget-wide-volatile", |
| "+sput-wide-volatile", |
| "^breakpoint", |
| "^throw-verification-error", |
| "+execute-inline", |
| "+execute-inline/range", |
| "+invoke-object-init/range", |
| "+return-void-barrier", |
| "+iget-quick", |
| "+iget-wide-quick", |
| "+iget-object-quick", |
| "+iput-quick", |
| "+iput-wide-quick", |
| "+iput-object-quick", |
| "+invoke-virtual-quick", |
| "+invoke-virtual-quick/range", |
| "+invoke-super-quick", |
| "+invoke-super-quick/range", |
| "+iput-object-volatile", |
| "+sget-object-volatile", |
| "+sput-object-volatile", |
| "unused-ff", |
| }; |
| |
| const char* GetOpcodeName(Opcode opcode) { return gOpcodeNames[opcode]; } |
| |
| // Helpers for DecodeInstruction() |
| static u4 InstA(u2 inst) { return (inst >> 8) & 0x0f; } |
| static u4 InstB(u2 inst) { return inst >> 12; } |
| static u4 InstAA(u2 inst) { return inst >> 8; } |
| |
| // Helper for DecodeInstruction() |
| static u4 FetchU4(const u2* ptr) { |
| return ptr[0] | (u4(ptr[1]) << 16); |
| } |
| |
| // Helper for DecodeInstruction() |
| static u8 FetchU8(const u2* ptr) { |
| return FetchU4(ptr) | (u8(FetchU4(ptr + 2)) << 32); |
| } |
| |
| // Decode a Dalvik bytecode and extract the individual fields |
| Instruction DecodeInstruction(const u2* bytecode) { |
| u2 inst = bytecode[0]; |
| Opcode opcode = OpcodeFromBytecode(inst); |
| InstructionFormat format = GetFormatFromOpcode(opcode); |
| |
| Instruction dec = {}; |
| dec.opcode = opcode; |
| |
| switch (format) { |
| case kFmt10x: // op |
| break; |
| case kFmt12x: // op vA, vB |
| dec.vA = InstA(inst); |
| dec.vB = InstB(inst); |
| break; |
| case kFmt11n: // op vA, #+B |
| dec.vA = InstA(inst); |
| dec.vB = s4(InstB(inst) << 28) >> 28; // sign extend 4-bit value |
| break; |
| case kFmt11x: // op vAA |
| dec.vA = InstAA(inst); |
| break; |
| case kFmt10t: // op +AA |
| dec.vA = s1(InstAA(inst)); // sign-extend 8-bit value |
| break; |
| case kFmt20t: // op +AAAA |
| dec.vA = s2(bytecode[1]); // sign-extend 16-bit value |
| break; |
| case kFmt20bc: // [opt] op AA, thing@BBBB |
| case kFmt21c: // op vAA, thing@BBBB |
| case kFmt22x: // op vAA, vBBBB |
| dec.vA = InstAA(inst); |
| dec.vB = bytecode[1]; |
| break; |
| case kFmt21s: // op vAA, #+BBBB |
| case kFmt21t: // op vAA, +BBBB |
| dec.vA = InstAA(inst); |
| dec.vB = s2(bytecode[1]); // sign-extend 16-bit value |
| break; |
| case kFmt21h: // op vAA, #+BBBB0000[00000000] |
| dec.vA = InstAA(inst); |
| // The value should be treated as right-zero-extended, but we don't |
| // actually do that here. Among other things, we don't know if it's |
| // the top bits of a 32- or 64-bit value. |
| dec.vB = bytecode[1]; |
| break; |
| case kFmt23x: // op vAA, vBB, vCC |
| dec.vA = InstAA(inst); |
| dec.vB = bytecode[1] & 0xff; |
| dec.vC = bytecode[1] >> 8; |
| break; |
| case kFmt22b: // op vAA, vBB, #+CC |
| dec.vA = InstAA(inst); |
| dec.vB = bytecode[1] & 0xff; |
| dec.vC = s1(bytecode[1] >> 8); // sign-extend 8-bit value |
| break; |
| case kFmt22s: // op vA, vB, #+CCCC |
| case kFmt22t: // op vA, vB, +CCCC |
| dec.vA = InstA(inst); |
| dec.vB = InstB(inst); |
| dec.vC = s2(bytecode[1]); // sign-extend 16-bit value |
| break; |
| case kFmt22c: // op vA, vB, thing@CCCC |
| case kFmt22cs: // [opt] op vA, vB, field offset CCCC |
| dec.vA = InstA(inst); |
| dec.vB = InstB(inst); |
| dec.vC = bytecode[1]; |
| break; |
| case kFmt30t: // op +AAAAAAAA |
| dec.vA = FetchU4(bytecode + 1); |
| break; |
| case kFmt31t: // op vAA, +BBBBBBBB |
| case kFmt31c: // op vAA, string@BBBBBBBB |
| dec.vA = InstAA(inst); |
| dec.vB = FetchU4(bytecode + 1); |
| break; |
| case kFmt32x: // op vAAAA, vBBBB |
| dec.vA = bytecode[1]; |
| dec.vB = bytecode[2]; |
| break; |
| case kFmt31i: // op vAA, #+BBBBBBBB |
| dec.vA = InstAA(inst); |
| dec.vB = FetchU4(bytecode + 1); |
| break; |
| case kFmt35c: // op {vC, vD, vE, vF, vG}, thing@BBBB |
| case kFmt35ms: // [opt] invoke-virtual+super |
| case kFmt35mi: // [opt] inline invoke |
| { |
| dec.vA = InstB(inst); // This is labeled A in the spec. |
| dec.vB = bytecode[1]; |
| |
| u2 regList = bytecode[2]; |
| |
| // Copy the argument registers into the arg[] array, and |
| // also copy the first argument (if any) into vC. (The |
| // Instruction structure doesn't have separate |
| // fields for {vD, vE, vF, vG}, so there's no need to make |
| // copies of those.) Note that cases 5..2 fall through. |
| switch (dec.vA) { |
| case 5: |
| // A fifth arg is verboten for inline invokes |
| SLICER_CHECK(format != kFmt35mi); |
| |
| // Per note at the top of this format decoder, the |
| // fifth argument comes from the A field in the |
| // instruction, but it's labeled G in the spec. |
| dec.arg[4] = InstA(inst); |
| // fallthrough |
| case 4: |
| dec.arg[3] = (regList >> 12) & 0x0f; |
| // fallthrough |
| case 3: |
| dec.arg[2] = (regList >> 8) & 0x0f; |
| // fallthrough |
| case 2: |
| dec.arg[1] = (regList >> 4) & 0x0f; |
| // fallthrough |
| case 1: |
| dec.vC = dec.arg[0] = regList & 0x0f; |
| // fallthrough |
| case 0: |
| // Valid, but no need to do anything |
| break; |
| default: |
| SLICER_CHECK(!"Invalid arg count in 35c/35ms/35mi"); |
| break; |
| } |
| } break; |
| case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB |
| case kFmt3rms: // [opt] invoke-virtual+super/range |
| case kFmt3rmi: // [opt] execute-inline/range |
| dec.vA = InstAA(inst); |
| dec.vB = bytecode[1]; |
| dec.vC = bytecode[2]; |
| break; |
| case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB |
| dec.vA = InstAA(inst); |
| dec.vB_wide = FetchU8(bytecode + 1); |
| break; |
| default: |
| SLICER_FATAL("Can't decode unexpected format 0x%02x", format); |
| } |
| |
| return dec; |
| } |
| |
| } // namespace dex |