/* * Copyright (C) 2008 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. */ /* * Dalvik instruction utility functions. */ #include "InstrUtils.h" #include <stdlib.h> /* * Generate a table that holds the width of all instructions. * * Standard instructions have positive values, optimizer instructions * have negative values, unimplemented instructions have a width of zero. * * I'm doing it with a giant switch statement because it's easier to * maintain and update than a static table with 256 unadorned integers, * and if we're missing a case gcc emits a "warning: enumeration value not * handled" message. * * (To save space in the binary we could generate a static table with a * command-line utility.) */ InstructionWidth* dexCreateInstrWidthTable(void) { InstructionWidth* instrWidth; int i; instrWidth = malloc(sizeof(InstructionWidth) * kNumDalvikInstructions); if (instrWidth == NULL) return NULL; for (i = 0; i < kNumDalvikInstructions; i++) { OpCode opc = (OpCode) i; int width = 0; switch (opc) { case OP_NOP: /* switch-statement data is a special case of NOP */ case OP_MOVE: case OP_MOVE_WIDE: case OP_MOVE_OBJECT: case OP_MOVE_RESULT: case OP_MOVE_RESULT_WIDE: case OP_MOVE_RESULT_OBJECT: case OP_MOVE_EXCEPTION: case OP_RETURN_VOID: case OP_RETURN: case OP_RETURN_WIDE: case OP_RETURN_OBJECT: case OP_CONST_4: case OP_MONITOR_ENTER: case OP_MONITOR_EXIT: case OP_ARRAY_LENGTH: case OP_THROW: case OP_GOTO: case OP_NEG_INT: case OP_NOT_INT: case OP_NEG_LONG: case OP_NOT_LONG: case OP_NEG_FLOAT: case OP_NEG_DOUBLE: case OP_INT_TO_LONG: case OP_INT_TO_FLOAT: case OP_INT_TO_DOUBLE: case OP_LONG_TO_INT: case OP_LONG_TO_FLOAT: case OP_LONG_TO_DOUBLE: case OP_FLOAT_TO_INT: case OP_FLOAT_TO_LONG: case OP_FLOAT_TO_DOUBLE: case OP_DOUBLE_TO_INT: case OP_DOUBLE_TO_LONG: case OP_DOUBLE_TO_FLOAT: case OP_INT_TO_BYTE: case OP_INT_TO_CHAR: case OP_INT_TO_SHORT: case OP_ADD_INT_2ADDR: case OP_SUB_INT_2ADDR: case OP_MUL_INT_2ADDR: case OP_DIV_INT_2ADDR: case OP_REM_INT_2ADDR: case OP_AND_INT_2ADDR: case OP_OR_INT_2ADDR: case OP_XOR_INT_2ADDR: case OP_SHL_INT_2ADDR: case OP_SHR_INT_2ADDR: case OP_USHR_INT_2ADDR: case OP_ADD_LONG_2ADDR: case OP_SUB_LONG_2ADDR: case OP_MUL_LONG_2ADDR: case OP_DIV_LONG_2ADDR: case OP_REM_LONG_2ADDR: case OP_AND_LONG_2ADDR: case OP_OR_LONG_2ADDR: case OP_XOR_LONG_2ADDR: case OP_SHL_LONG_2ADDR: case OP_SHR_LONG_2ADDR: case OP_USHR_LONG_2ADDR: case OP_ADD_FLOAT_2ADDR: case OP_SUB_FLOAT_2ADDR: case OP_MUL_FLOAT_2ADDR: case OP_DIV_FLOAT_2ADDR: case OP_REM_FLOAT_2ADDR: case OP_ADD_DOUBLE_2ADDR: case OP_SUB_DOUBLE_2ADDR: case OP_MUL_DOUBLE_2ADDR: case OP_DIV_DOUBLE_2ADDR: case OP_REM_DOUBLE_2ADDR: width = 1; break; case OP_MOVE_FROM16: case OP_MOVE_WIDE_FROM16: case OP_MOVE_OBJECT_FROM16: case OP_CONST_16: case OP_CONST_HIGH16: case OP_CONST_WIDE_16: case OP_CONST_WIDE_HIGH16: case OP_CONST_STRING: case OP_CONST_CLASS: case OP_CHECK_CAST: case OP_INSTANCE_OF: case OP_NEW_INSTANCE: case OP_NEW_ARRAY: case OP_CMPL_FLOAT: case OP_CMPG_FLOAT: case OP_CMPL_DOUBLE: case OP_CMPG_DOUBLE: case OP_CMP_LONG: case OP_GOTO_16: case OP_IF_EQ: case OP_IF_NE: case OP_IF_LT: case OP_IF_GE: case OP_IF_GT: case OP_IF_LE: case OP_IF_EQZ: case OP_IF_NEZ: case OP_IF_LTZ: case OP_IF_GEZ: case OP_IF_GTZ: case OP_IF_LEZ: case OP_AGET: case OP_AGET_WIDE: case OP_AGET_OBJECT: case OP_AGET_BOOLEAN: case OP_AGET_BYTE: case OP_AGET_CHAR: case OP_AGET_SHORT: case OP_APUT: case OP_APUT_WIDE: case OP_APUT_OBJECT: case OP_APUT_BOOLEAN: case OP_APUT_BYTE: case OP_APUT_CHAR: case OP_APUT_SHORT: case OP_IGET: case OP_IGET_WIDE: case OP_IGET_OBJECT: case OP_IGET_BOOLEAN: case OP_IGET_BYTE: case OP_IGET_CHAR: case OP_IGET_SHORT: case OP_IPUT: case OP_IPUT_WIDE: case OP_IPUT_OBJECT: case OP_IPUT_BOOLEAN: case OP_IPUT_BYTE: case OP_IPUT_CHAR: case OP_IPUT_SHORT: case OP_SGET: case OP_SGET_WIDE: case OP_SGET_OBJECT: case OP_SGET_BOOLEAN: case OP_SGET_BYTE: case OP_SGET_CHAR: case OP_SGET_SHORT: case OP_SPUT: case OP_SPUT_WIDE: case OP_SPUT_OBJECT: case OP_SPUT_BOOLEAN: case OP_SPUT_BYTE: case OP_SPUT_CHAR: case OP_SPUT_SHORT: case OP_ADD_INT: case OP_SUB_INT: case OP_MUL_INT: case OP_DIV_INT: case OP_REM_INT: case OP_AND_INT: case OP_OR_INT: case OP_XOR_INT: case OP_SHL_INT: case OP_SHR_INT: case OP_USHR_INT: case OP_ADD_LONG: case OP_SUB_LONG: case OP_MUL_LONG: case OP_DIV_LONG: case OP_REM_LONG: case OP_AND_LONG: case OP_OR_LONG: case OP_XOR_LONG: case OP_SHL_LONG: case OP_SHR_LONG: case OP_USHR_LONG: case OP_ADD_FLOAT: case OP_SUB_FLOAT: case OP_MUL_FLOAT: case OP_DIV_FLOAT: case OP_REM_FLOAT: case OP_ADD_DOUBLE: case OP_SUB_DOUBLE: case OP_MUL_DOUBLE: case OP_DIV_DOUBLE: case OP_REM_DOUBLE: case OP_ADD_INT_LIT16: case OP_RSUB_INT: case OP_MUL_INT_LIT16: case OP_DIV_INT_LIT16: case OP_REM_INT_LIT16: case OP_AND_INT_LIT16: case OP_OR_INT_LIT16: case OP_XOR_INT_LIT16: case OP_ADD_INT_LIT8: case OP_RSUB_INT_LIT8: case OP_MUL_INT_LIT8: case OP_DIV_INT_LIT8: case OP_REM_INT_LIT8: case OP_AND_INT_LIT8: case OP_OR_INT_LIT8: case OP_XOR_INT_LIT8: case OP_SHL_INT_LIT8: case OP_SHR_INT_LIT8: case OP_USHR_INT_LIT8: width = 2; break; case OP_MOVE_16: case OP_MOVE_WIDE_16: case OP_MOVE_OBJECT_16: case OP_CONST: case OP_CONST_WIDE_32: case OP_CONST_STRING_JUMBO: case OP_GOTO_32: case OP_FILLED_NEW_ARRAY: case OP_FILLED_NEW_ARRAY_RANGE: case OP_FILL_ARRAY_DATA: case OP_PACKED_SWITCH: case OP_SPARSE_SWITCH: case OP_INVOKE_VIRTUAL: case OP_INVOKE_SUPER: case OP_INVOKE_DIRECT: case OP_INVOKE_STATIC: case OP_INVOKE_INTERFACE: case OP_INVOKE_VIRTUAL_RANGE: case OP_INVOKE_SUPER_RANGE: case OP_INVOKE_DIRECT_RANGE: case OP_INVOKE_STATIC_RANGE: case OP_INVOKE_INTERFACE_RANGE: width = 3; break; case OP_CONST_WIDE: width = 5; break; /* * Optimized instructions. We return negative size values for these * to distinguish them. */ case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: case OP_IPUT_QUICK: case OP_IPUT_WIDE_QUICK: case OP_IPUT_OBJECT_QUICK: width = -2; break; case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: case OP_EXECUTE_INLINE: case OP_INVOKE_DIRECT_EMPTY: width = -3; break; /* these should never appear */ case OP_UNUSED_3E: case OP_UNUSED_3F: case OP_UNUSED_40: case OP_UNUSED_41: case OP_UNUSED_42: case OP_UNUSED_43: case OP_UNUSED_73: case OP_UNUSED_79: case OP_UNUSED_7A: case OP_UNUSED_E3: case OP_UNUSED_E4: case OP_UNUSED_E5: case OP_UNUSED_E6: case OP_UNUSED_E7: case OP_UNUSED_E8: case OP_UNUSED_E9: case OP_UNUSED_EA: case OP_UNUSED_EB: case OP_UNUSED_EC: case OP_UNUSED_ED: case OP_UNUSED_EF: case OP_UNUSED_F1: case OP_UNUSED_FC: case OP_UNUSED_FD: case OP_UNUSED_FE: case OP_UNUSED_FF: assert(width == 0); break; /* * DO NOT add a "default" clause here. Without it the compiler will * complain if an instruction is missing (which is desirable). */ } instrWidth[opc] = width; } return instrWidth; } /* * Generate a table that holds instruction flags. */ InstructionFlags* dexCreateInstrFlagsTable(void) { InstructionFlags* instrFlags; int i; instrFlags = malloc(sizeof(InstructionFlags) * kNumDalvikInstructions); if (instrFlags == NULL) return NULL; for (i = 0; i < kNumDalvikInstructions; i++) { OpCode opc = (OpCode) i; InstructionFlags flags = 0; switch (opc) { /* these don't affect the PC and can't cause an exception */ case OP_NOP: case OP_MOVE: case OP_MOVE_FROM16: case OP_MOVE_16: case OP_MOVE_WIDE: case OP_MOVE_WIDE_FROM16: case OP_MOVE_WIDE_16: case OP_MOVE_OBJECT: case OP_MOVE_OBJECT_FROM16: case OP_MOVE_OBJECT_16: case OP_MOVE_RESULT: case OP_MOVE_RESULT_WIDE: case OP_MOVE_RESULT_OBJECT: case OP_MOVE_EXCEPTION: case OP_CONST_4: case OP_CONST_16: case OP_CONST: case OP_CONST_HIGH16: case OP_CONST_WIDE_16: case OP_CONST_WIDE_32: case OP_CONST_WIDE: case OP_CONST_WIDE_HIGH16: case OP_FILL_ARRAY_DATA: case OP_CMPL_FLOAT: case OP_CMPG_FLOAT: case OP_CMPL_DOUBLE: case OP_CMPG_DOUBLE: case OP_CMP_LONG: case OP_NEG_INT: case OP_NOT_INT: case OP_NEG_LONG: case OP_NOT_LONG: case OP_NEG_FLOAT: case OP_NEG_DOUBLE: case OP_INT_TO_LONG: case OP_INT_TO_FLOAT: case OP_INT_TO_DOUBLE: case OP_LONG_TO_INT: case OP_LONG_TO_FLOAT: case OP_LONG_TO_DOUBLE: case OP_FLOAT_TO_INT: case OP_FLOAT_TO_LONG: case OP_FLOAT_TO_DOUBLE: case OP_DOUBLE_TO_INT: case OP_DOUBLE_TO_LONG: case OP_DOUBLE_TO_FLOAT: case OP_INT_TO_BYTE: case OP_INT_TO_CHAR: case OP_INT_TO_SHORT: case OP_ADD_INT: case OP_SUB_INT: case OP_MUL_INT: case OP_AND_INT: case OP_OR_INT: case OP_XOR_INT: case OP_SHL_INT: case OP_SHR_INT: case OP_USHR_INT: case OP_ADD_LONG: case OP_SUB_LONG: case OP_MUL_LONG: case OP_AND_LONG: case OP_OR_LONG: case OP_XOR_LONG: case OP_SHL_LONG: case OP_SHR_LONG: case OP_USHR_LONG: case OP_ADD_FLOAT: case OP_SUB_FLOAT: case OP_MUL_FLOAT: case OP_DIV_FLOAT: case OP_REM_FLOAT: case OP_ADD_DOUBLE: case OP_SUB_DOUBLE: case OP_MUL_DOUBLE: case OP_DIV_DOUBLE: // div by zero just returns NaN case OP_REM_DOUBLE: case OP_ADD_INT_2ADDR: case OP_SUB_INT_2ADDR: case OP_MUL_INT_2ADDR: case OP_AND_INT_2ADDR: case OP_OR_INT_2ADDR: case OP_XOR_INT_2ADDR: case OP_SHL_INT_2ADDR: case OP_SHR_INT_2ADDR: case OP_USHR_INT_2ADDR: case OP_ADD_LONG_2ADDR: case OP_SUB_LONG_2ADDR: case OP_MUL_LONG_2ADDR: case OP_AND_LONG_2ADDR: case OP_OR_LONG_2ADDR: case OP_XOR_LONG_2ADDR: case OP_SHL_LONG_2ADDR: case OP_SHR_LONG_2ADDR: case OP_USHR_LONG_2ADDR: case OP_ADD_FLOAT_2ADDR: case OP_SUB_FLOAT_2ADDR: case OP_MUL_FLOAT_2ADDR: case OP_DIV_FLOAT_2ADDR: case OP_REM_FLOAT_2ADDR: case OP_ADD_DOUBLE_2ADDR: case OP_SUB_DOUBLE_2ADDR: case OP_MUL_DOUBLE_2ADDR: case OP_DIV_DOUBLE_2ADDR: case OP_REM_DOUBLE_2ADDR: case OP_ADD_INT_LIT16: case OP_RSUB_INT: case OP_MUL_INT_LIT16: case OP_AND_INT_LIT16: case OP_OR_INT_LIT16: case OP_XOR_INT_LIT16: case OP_ADD_INT_LIT8: case OP_RSUB_INT_LIT8: case OP_MUL_INT_LIT8: case OP_AND_INT_LIT8: case OP_OR_INT_LIT8: case OP_XOR_INT_LIT8: case OP_SHL_INT_LIT8: case OP_SHR_INT_LIT8: case OP_USHR_INT_LIT8: flags = kInstrCanContinue; break; /* these don't affect the PC, but can cause exceptions */ case OP_CONST_STRING: case OP_CONST_STRING_JUMBO: case OP_CONST_CLASS: case OP_MONITOR_ENTER: case OP_MONITOR_EXIT: case OP_CHECK_CAST: case OP_INSTANCE_OF: case OP_ARRAY_LENGTH: case OP_NEW_INSTANCE: case OP_NEW_ARRAY: case OP_FILLED_NEW_ARRAY: case OP_FILLED_NEW_ARRAY_RANGE: case OP_AGET: case OP_AGET_BOOLEAN: case OP_AGET_BYTE: case OP_AGET_CHAR: case OP_AGET_SHORT: case OP_AGET_WIDE: case OP_AGET_OBJECT: case OP_APUT: case OP_APUT_BOOLEAN: case OP_APUT_BYTE: case OP_APUT_CHAR: case OP_APUT_SHORT: case OP_APUT_WIDE: case OP_APUT_OBJECT: case OP_IGET: case OP_IGET_BOOLEAN: case OP_IGET_BYTE: case OP_IGET_CHAR: case OP_IGET_SHORT: case OP_IGET_WIDE: case OP_IGET_OBJECT: case OP_IPUT: case OP_IPUT_BOOLEAN: case OP_IPUT_BYTE: case OP_IPUT_CHAR: case OP_IPUT_SHORT: case OP_IPUT_WIDE: case OP_IPUT_OBJECT: case OP_SGET: case OP_SGET_BOOLEAN: case OP_SGET_BYTE: case OP_SGET_CHAR: case OP_SGET_SHORT: case OP_SGET_WIDE: case OP_SGET_OBJECT: case OP_SPUT: case OP_SPUT_BOOLEAN: case OP_SPUT_BYTE: case OP_SPUT_CHAR: case OP_SPUT_SHORT: case OP_SPUT_WIDE: case OP_SPUT_OBJECT: case OP_INVOKE_VIRTUAL: case OP_INVOKE_VIRTUAL_RANGE: case OP_INVOKE_SUPER: case OP_INVOKE_SUPER_RANGE: case OP_INVOKE_DIRECT: case OP_INVOKE_DIRECT_RANGE: case OP_INVOKE_STATIC: case OP_INVOKE_STATIC_RANGE: case OP_INVOKE_INTERFACE: case OP_INVOKE_INTERFACE_RANGE: case OP_DIV_INT: case OP_REM_INT: case OP_DIV_LONG: case OP_REM_LONG: case OP_DIV_INT_2ADDR: case OP_REM_INT_2ADDR: case OP_DIV_LONG_2ADDR: case OP_REM_LONG_2ADDR: case OP_DIV_INT_LIT16: case OP_REM_INT_LIT16: case OP_DIV_INT_LIT8: case OP_REM_INT_LIT8: flags = kInstrCanContinue | kInstrCanThrow; break; case OP_RETURN_VOID: case OP_RETURN: case OP_RETURN_WIDE: case OP_RETURN_OBJECT: flags = kInstrCanReturn; break; case OP_THROW: flags = kInstrCanThrow; break; /* unconditional branches */ case OP_GOTO: case OP_GOTO_16: case OP_GOTO_32: flags = kInstrCanBranch; break; /* conditional branches */ case OP_IF_EQ: case OP_IF_NE: case OP_IF_LT: case OP_IF_GE: case OP_IF_GT: case OP_IF_LE: case OP_IF_EQZ: case OP_IF_NEZ: case OP_IF_LTZ: case OP_IF_GEZ: case OP_IF_GTZ: case OP_IF_LEZ: flags = kInstrCanBranch | kInstrCanContinue; break; /* switch statements; if value not in switch, it continues */ case OP_PACKED_SWITCH: case OP_SPARSE_SWITCH: flags = kInstrCanSwitch | kInstrCanContinue; break; /* optimizer-generated instructions */ case OP_EXECUTE_INLINE: flags = kInstrCanContinue; break; case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: case OP_IPUT_QUICK: case OP_IPUT_WIDE_QUICK: case OP_IPUT_OBJECT_QUICK: case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: case OP_INVOKE_DIRECT_EMPTY: flags = kInstrCanContinue | kInstrCanThrow; break; /* these should never appear */ case OP_UNUSED_3E: case OP_UNUSED_3F: case OP_UNUSED_40: case OP_UNUSED_41: case OP_UNUSED_42: case OP_UNUSED_43: case OP_UNUSED_73: case OP_UNUSED_79: case OP_UNUSED_7A: case OP_UNUSED_E3: case OP_UNUSED_E4: case OP_UNUSED_E5: case OP_UNUSED_E6: case OP_UNUSED_E7: case OP_UNUSED_E8: case OP_UNUSED_E9: case OP_UNUSED_EA: case OP_UNUSED_EB: case OP_UNUSED_EC: case OP_UNUSED_ED: case OP_UNUSED_EF: case OP_UNUSED_F1: case OP_UNUSED_FC: case OP_UNUSED_FD: case OP_UNUSED_FE: case OP_UNUSED_FF: break; /* * DO NOT add a "default" clause here. Without it the compiler will * complain if an instruction is missing (which is desirable). */ } instrFlags[opc] = flags; } return instrFlags; } /* * Allocate and populate a 256-element array with instruction formats. * Used in conjunction with dexDecodeInstruction. */ InstructionFormat* dexCreateInstrFormatTable(void) { InstructionFormat* instFmt; int i; instFmt = malloc(sizeof(InstructionFormat) * kNumDalvikInstructions); if (instFmt == NULL) return NULL; for (i = 0; i < kNumDalvikInstructions; i++) { OpCode opc = (OpCode) i; InstructionFormat fmt = kFmtUnknown; switch (opc) { case OP_GOTO: fmt = kFmt10t; break; case OP_NOP: case OP_RETURN_VOID: fmt = kFmt10x; break; case OP_CONST_4: fmt = kFmt11n; break; case OP_CONST_HIGH16: case OP_CONST_WIDE_HIGH16: fmt = kFmt21h; break; case OP_MOVE_RESULT: case OP_MOVE_RESULT_WIDE: case OP_MOVE_RESULT_OBJECT: case OP_MOVE_EXCEPTION: case OP_RETURN: case OP_RETURN_WIDE: case OP_RETURN_OBJECT: case OP_MONITOR_ENTER: case OP_MONITOR_EXIT: case OP_THROW: fmt = kFmt11x; break; case OP_MOVE: case OP_MOVE_WIDE: case OP_MOVE_OBJECT: case OP_ARRAY_LENGTH: case OP_NEG_INT: case OP_NOT_INT: case OP_NEG_LONG: case OP_NOT_LONG: case OP_NEG_FLOAT: case OP_NEG_DOUBLE: case OP_INT_TO_LONG: case OP_INT_TO_FLOAT: case OP_INT_TO_DOUBLE: case OP_LONG_TO_INT: case OP_LONG_TO_FLOAT: case OP_LONG_TO_DOUBLE: case OP_FLOAT_TO_INT: case OP_FLOAT_TO_LONG: case OP_FLOAT_TO_DOUBLE: case OP_DOUBLE_TO_INT: case OP_DOUBLE_TO_LONG: case OP_DOUBLE_TO_FLOAT: case OP_INT_TO_BYTE: case OP_INT_TO_CHAR: case OP_INT_TO_SHORT: case OP_ADD_INT_2ADDR: case OP_SUB_INT_2ADDR: case OP_MUL_INT_2ADDR: case OP_DIV_INT_2ADDR: case OP_REM_INT_2ADDR: case OP_AND_INT_2ADDR: case OP_OR_INT_2ADDR: case OP_XOR_INT_2ADDR: case OP_SHL_INT_2ADDR: case OP_SHR_INT_2ADDR: case OP_USHR_INT_2ADDR: case OP_ADD_LONG_2ADDR: case OP_SUB_LONG_2ADDR: case OP_MUL_LONG_2ADDR: case OP_DIV_LONG_2ADDR: case OP_REM_LONG_2ADDR: case OP_AND_LONG_2ADDR: case OP_OR_LONG_2ADDR: case OP_XOR_LONG_2ADDR: case OP_SHL_LONG_2ADDR: case OP_SHR_LONG_2ADDR: case OP_USHR_LONG_2ADDR: case OP_ADD_FLOAT_2ADDR: case OP_SUB_FLOAT_2ADDR: case OP_MUL_FLOAT_2ADDR: case OP_DIV_FLOAT_2ADDR: case OP_REM_FLOAT_2ADDR: case OP_ADD_DOUBLE_2ADDR: case OP_SUB_DOUBLE_2ADDR: case OP_MUL_DOUBLE_2ADDR: case OP_DIV_DOUBLE_2ADDR: case OP_REM_DOUBLE_2ADDR: fmt = kFmt12x; break; case OP_GOTO_16: fmt = kFmt20t; break; case OP_GOTO_32: fmt = kFmt30t; break; case OP_CONST_STRING: case OP_CONST_CLASS: case OP_CHECK_CAST: case OP_NEW_INSTANCE: case OP_SGET: case OP_SGET_WIDE: case OP_SGET_OBJECT: case OP_SGET_BOOLEAN: case OP_SGET_BYTE: case OP_SGET_CHAR: case OP_SGET_SHORT: case OP_SPUT: case OP_SPUT_WIDE: case OP_SPUT_OBJECT: case OP_SPUT_BOOLEAN: case OP_SPUT_BYTE: case OP_SPUT_CHAR: case OP_SPUT_SHORT: fmt = kFmt21c; break; case OP_CONST_16: case OP_CONST_WIDE_16: fmt = kFmt21s; break; case OP_IF_EQZ: case OP_IF_NEZ: case OP_IF_LTZ: case OP_IF_GEZ: case OP_IF_GTZ: case OP_IF_LEZ: fmt = kFmt21t; break; case OP_FILL_ARRAY_DATA: case OP_PACKED_SWITCH: case OP_SPARSE_SWITCH: fmt = kFmt31t; break; case OP_ADD_INT_LIT8: case OP_RSUB_INT_LIT8: case OP_MUL_INT_LIT8: case OP_DIV_INT_LIT8: case OP_REM_INT_LIT8: case OP_AND_INT_LIT8: case OP_OR_INT_LIT8: case OP_XOR_INT_LIT8: case OP_SHL_INT_LIT8: case OP_SHR_INT_LIT8: case OP_USHR_INT_LIT8: fmt = kFmt22b; break; case OP_INSTANCE_OF: case OP_NEW_ARRAY: case OP_IGET: case OP_IGET_WIDE: case OP_IGET_OBJECT: case OP_IGET_BOOLEAN: case OP_IGET_BYTE: case OP_IGET_CHAR: case OP_IGET_SHORT: case OP_IPUT: case OP_IPUT_WIDE: case OP_IPUT_OBJECT: case OP_IPUT_BOOLEAN: case OP_IPUT_BYTE: case OP_IPUT_CHAR: case OP_IPUT_SHORT: fmt = kFmt22c; break; case OP_ADD_INT_LIT16: case OP_RSUB_INT: case OP_MUL_INT_LIT16: case OP_DIV_INT_LIT16: case OP_REM_INT_LIT16: case OP_AND_INT_LIT16: case OP_OR_INT_LIT16: case OP_XOR_INT_LIT16: fmt = kFmt22s; break; case OP_IF_EQ: case OP_IF_NE: case OP_IF_LT: case OP_IF_GE: case OP_IF_GT: case OP_IF_LE: fmt = kFmt22t; break; case OP_MOVE_FROM16: case OP_MOVE_WIDE_FROM16: case OP_MOVE_OBJECT_FROM16: fmt = kFmt22x; break; case OP_CMPL_FLOAT: case OP_CMPG_FLOAT: case OP_CMPL_DOUBLE: case OP_CMPG_DOUBLE: case OP_CMP_LONG: case OP_AGET: case OP_AGET_WIDE: case OP_AGET_OBJECT: case OP_AGET_BOOLEAN: case OP_AGET_BYTE: case OP_AGET_CHAR: case OP_AGET_SHORT: case OP_APUT: case OP_APUT_WIDE: case OP_APUT_OBJECT: case OP_APUT_BOOLEAN: case OP_APUT_BYTE: case OP_APUT_CHAR: case OP_APUT_SHORT: case OP_ADD_INT: case OP_SUB_INT: case OP_MUL_INT: case OP_DIV_INT: case OP_REM_INT: case OP_AND_INT: case OP_OR_INT: case OP_XOR_INT: case OP_SHL_INT: case OP_SHR_INT: case OP_USHR_INT: case OP_ADD_LONG: case OP_SUB_LONG: case OP_MUL_LONG: case OP_DIV_LONG: case OP_REM_LONG: case OP_AND_LONG: case OP_OR_LONG: case OP_XOR_LONG: case OP_SHL_LONG: case OP_SHR_LONG: case OP_USHR_LONG: case OP_ADD_FLOAT: case OP_SUB_FLOAT: case OP_MUL_FLOAT: case OP_DIV_FLOAT: case OP_REM_FLOAT: case OP_ADD_DOUBLE: case OP_SUB_DOUBLE: case OP_MUL_DOUBLE: case OP_DIV_DOUBLE: case OP_REM_DOUBLE: fmt = kFmt23x; break; case OP_CONST: case OP_CONST_WIDE_32: fmt = kFmt31i; break; case OP_CONST_STRING_JUMBO: fmt = kFmt31c; break; case OP_MOVE_16: case OP_MOVE_WIDE_16: case OP_MOVE_OBJECT_16: fmt = kFmt32x; break; case OP_FILLED_NEW_ARRAY: case OP_INVOKE_VIRTUAL: case OP_INVOKE_SUPER: case OP_INVOKE_DIRECT: case OP_INVOKE_STATIC: case OP_INVOKE_INTERFACE: fmt = kFmt35c; break; case OP_FILLED_NEW_ARRAY_RANGE: case OP_INVOKE_VIRTUAL_RANGE: case OP_INVOKE_SUPER_RANGE: case OP_INVOKE_DIRECT_RANGE: case OP_INVOKE_STATIC_RANGE: case OP_INVOKE_INTERFACE_RANGE: fmt = kFmt3rc; break; case OP_CONST_WIDE: fmt = kFmt51l; break; /* * Optimized instructions. */ case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: case OP_IPUT_QUICK: case OP_IPUT_WIDE_QUICK: case OP_IPUT_OBJECT_QUICK: fmt = kFmt22cs; break; case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_SUPER_QUICK: fmt = kFmt35ms; break; case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK_RANGE: fmt = kFmt3rms; break; case OP_EXECUTE_INLINE: fmt = kFmt3inline; break; case OP_INVOKE_DIRECT_EMPTY: fmt = kFmt35c; break; /* these should never appear */ case OP_UNUSED_3E: case OP_UNUSED_3F: case OP_UNUSED_40: case OP_UNUSED_41: case OP_UNUSED_42: case OP_UNUSED_43: case OP_UNUSED_73: case OP_UNUSED_79: case OP_UNUSED_7A: case OP_UNUSED_E3: case OP_UNUSED_E4: case OP_UNUSED_E5: case OP_UNUSED_E6: case OP_UNUSED_E7: case OP_UNUSED_E8: case OP_UNUSED_E9: case OP_UNUSED_EA: case OP_UNUSED_EB: case OP_UNUSED_EC: case OP_UNUSED_ED: case OP_UNUSED_EF: case OP_UNUSED_F1: case OP_UNUSED_FC: case OP_UNUSED_FD: case OP_UNUSED_FE: case OP_UNUSED_FF: fmt = kFmtUnknown; break; /* * DO NOT add a "default" clause here. Without it the compiler will * complain if an instruction is missing (which is desirable). */ } instFmt[opc] = fmt; } return instFmt; } /* * Copied from InterpCore.h. Used for instruction decoding. */ #define FETCH(_offset) (insns[(_offset)]) #define INST_INST(_inst) ((_inst) & 0xff) #define INST_A(_inst) (((u2)(_inst) >> 8) & 0x0f) #define INST_B(_inst) ((u2)(_inst) >> 12) #define INST_AA(_inst) ((_inst) >> 8) /* * Decode the instruction pointed to by "insns". * * Fills out the pieces of "pDec" that are affected by the current * instruction. Does not touch anything else. */ void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns, DecodedInstruction* pDec) { u2 inst = *insns; pDec->opCode = (OpCode) INST_INST(inst); switch (dexGetInstrFormat(fmts, pDec->opCode)) { case kFmt10x: // op /* nothing to do; copy the AA bits out for the verifier */ pDec->vA = INST_AA(inst); break; case kFmt12x: // op vA, vB pDec->vA = INST_A(inst); pDec->vB = INST_B(inst); break; case kFmt11n: // op vA, #+B pDec->vA = INST_A(inst); pDec->vB = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value break; case kFmt11x: // op vAA pDec->vA = INST_AA(inst); break; case kFmt10t: // op +AA pDec->vA = (s1) INST_AA(inst); // sign-extend 8-bit value break; case kFmt20t: // op +AAAA pDec->vA = (s2) FETCH(1); // sign-extend 16-bit value break; case kFmt21c: // op vAA, thing@BBBB case kFmt22x: // op vAA, vBBBB pDec->vA = INST_AA(inst); pDec->vB = FETCH(1); break; case kFmt21s: // op vAA, #+BBBB case kFmt21t: // op vAA, +BBBB pDec->vA = INST_AA(inst); pDec->vB = (s2) FETCH(1); // sign-extend 16-bit value break; case kFmt21h: // op vAA, #+BBBB0000[00000000] pDec->vA = INST_AA(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. */ pDec->vB = FETCH(1); break; case kFmt23x: // op vAA, vBB, vCC pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) & 0xff; pDec->vC = FETCH(1) >> 8; break; case kFmt22b: // op vAA, vBB, #+CC pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) & 0xff; pDec->vC = (s1) (FETCH(1) >> 8); // sign-extend 8-bit value break; case kFmt22s: // op vA, vB, #+CCCC case kFmt22t: // op vA, vB, +CCCC pDec->vA = INST_A(inst); pDec->vB = INST_B(inst); pDec->vC = (s2) FETCH(1); // sign-extend 16-bit value break; case kFmt22c: // op vA, vB, thing@CCCC case kFmt22cs: // [opt] op vA, vB, field offset CCCC pDec->vA = INST_A(inst); pDec->vB = INST_B(inst); pDec->vC = FETCH(1); break; case kFmt30t: // op +AAAAAAAA pDec->vA = FETCH(1) | ((u4) FETCH(2) << 16); // signed 32-bit value break; case kFmt31t: // op vAA, +BBBBBBBB case kFmt31c: // op vAA, thing@BBBBBBBB pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16); // 32-bit value break; case kFmt32x: // op vAAAA, vBBBB pDec->vA = FETCH(1); pDec->vB = FETCH(2); break; case kFmt31i: // op vAA, #+BBBBBBBB pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16); break; case kFmt35c: // op vB, {vD..vG,vA}, thing@CCCC case kFmt35ms: // [opt] invoke-virtual+super { /* * The lettering changes that came about when we went from 4 args * to 5 made the "range" versions of the calls different from * the non-range versions. We have the choice between decoding * them the way the spec shows and having lots of conditionals * in the verifier, or mapping the values onto their original * registers and leaving the verifier intact. * * Current plan is to leave the verifier alone. We can fix it * later if it's architecturally unbearable. * * Bottom line: method constant is always in vB. */ u2 regList; int i, count; pDec->vA = INST_B(inst); pDec->vB = FETCH(1); regList = FETCH(2); if (pDec->vA > 5) { LOGW("Invalid arg count in 35c/35ms (%d)\n", pDec->vA); goto bail; } count = pDec->vA; if (count == 5) { /* 5th arg comes from A field in instruction */ pDec->arg[4] = INST_A(inst); count--; } for (i = 0; i < count; i++) { pDec->arg[i] = regList & 0x0f; regList >>= 4; } /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */ if (pDec->vA > 0) pDec->vC = pDec->arg[0]; } break; case kFmt3inline: // [opt] inline invoke { u2 regList; int i; pDec->vA = INST_B(inst); pDec->vB = FETCH(1); regList = FETCH(2); if (pDec->vA > 4) { LOGW("Invalid arg count in 3inline (%d)\n", pDec->vA); goto bail; } for (i = 0; i < (int) pDec->vA; i++) { pDec->arg[i] = regList & 0x0f; regList >>= 4; } /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */ if (pDec->vA > 0) pDec->vC = pDec->arg[0]; } break; case kFmt35fs: // [opt] invoke-interface assert(false); // TODO break; case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB case kFmt3rms: // [opt] invoke-virtual+super/range pDec->vA = INST_AA(inst); pDec->vB = FETCH(1); pDec->vC = FETCH(2); break; case kFmt3rfs: // [opt] invoke-interface/range assert(false); // TODO break; case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB pDec->vA = INST_AA(inst); pDec->vB_wide = FETCH(1); pDec->vB_wide |= (u8)FETCH(2) << 16; pDec->vB_wide |= (u8)FETCH(3) << 32; pDec->vB_wide |= (u8)FETCH(4) << 48; break; default: LOGW("Can't decode unexpected format %d (op=%d)\n", dexGetInstrFormat(fmts, pDec->opCode), pDec->opCode); assert(false); break; } bail: ; } /* * Return the width of the specified instruction, or 0 if not defined. Also * works for special OP_NOP entries, including switch statement data tables * and array data. */ int dexGetInstrOrTableWidthAbs(const InstructionWidth* widths, const u2* insns) { int width; if (*insns == kPackedSwitchSignature) { width = 4 + insns[1] * 2; } else if (*insns == kSparseSwitchSignature) { width = 2 + insns[1] * 4; } else if (*insns == kArrayDataSignature) { u2 elemWidth = insns[1]; u4 len = insns[2] | (((u4)insns[3]) << 16); width = 4 + (elemWidth * len + 1) / 2; } else { width = dexGetInstrWidthAbs(widths, INST_INST(insns[0])); } return width; }