blob: a3ee859bb27ab56af91edfc7d881ac942a0a96f4 [file] [log] [blame]
/*
* 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.)
*
* TODO: it doesn't look like we're using the negative values anymore.
* Consider switching to only positive values.
*/
InstructionWidth* dexCreateInstrWidthTable(void)
{
#ifdef __ARM_ARCH_7A__
/* hack to work around mysterious problem on emulator */
LOGD("creating instr width table\n");
#endif
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: /* note data for e.g. switch-* encoded "inside" a 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:
case OP_IGET_VOLATILE:
case OP_IPUT_VOLATILE:
case OP_SGET_VOLATILE:
case OP_SPUT_VOLATILE:
case OP_IGET_OBJECT_VOLATILE:
case OP_IPUT_OBJECT_VOLATILE:
case OP_SGET_OBJECT_VOLATILE:
case OP_SPUT_OBJECT_VOLATILE:
case OP_IGET_WIDE_VOLATILE:
case OP_IPUT_WIDE_VOLATILE:
case OP_SGET_WIDE_VOLATILE:
case OP_SPUT_WIDE_VOLATILE:
case OP_THROW_VERIFICATION_ERROR:
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_EXECUTE_INLINE_RANGE:
case OP_INVOKE_DIRECT_EMPTY:
width = -3;
break;
/* these should never appear when scanning bytecode */
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_BREAKPOINT:
case OP_UNUSED_F1:
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_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_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:
flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke;
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 | kInstrUnconditional;
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;
/* verifier/optimizer-generated instructions */
case OP_THROW_VERIFICATION_ERROR:
flags = kInstrCanThrow;
break;
case OP_EXECUTE_INLINE:
case OP_EXECUTE_INLINE_RANGE:
flags = kInstrCanContinue | kInstrCanThrow;
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_IGET_VOLATILE:
case OP_IPUT_VOLATILE:
case OP_SGET_VOLATILE:
case OP_SPUT_VOLATILE:
case OP_IGET_OBJECT_VOLATILE:
case OP_IPUT_OBJECT_VOLATILE:
case OP_SGET_OBJECT_VOLATILE:
case OP_SPUT_OBJECT_VOLATILE:
case OP_IGET_WIDE_VOLATILE:
case OP_IPUT_WIDE_VOLATILE:
case OP_SGET_WIDE_VOLATILE:
case OP_SPUT_WIDE_VOLATILE:
flags = kInstrCanContinue | kInstrCanThrow;
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_INVOKE_DIRECT_EMPTY:
flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke;
break;
/* these should never appear when scanning code */
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_BREAKPOINT:
case OP_UNUSED_F1:
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_THROW_VERIFICATION_ERROR:
fmt = kFmt20bc;
break;
case OP_IGET_WIDE_VOLATILE:
case OP_IPUT_WIDE_VOLATILE:
case OP_SGET_WIDE_VOLATILE:
case OP_SPUT_WIDE_VOLATILE:
case OP_IGET_VOLATILE:
case OP_IPUT_VOLATILE:
case OP_SGET_VOLATILE:
case OP_SPUT_VOLATILE:
case OP_IGET_OBJECT_VOLATILE:
case OP_IPUT_OBJECT_VOLATILE:
case OP_SGET_OBJECT_VOLATILE:
case OP_SPUT_OBJECT_VOLATILE:
fmt = kFmt22c;
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:
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_EXECUTE_INLINE_RANGE:
fmt = kFmt3rinline;
break;
case OP_INVOKE_DIRECT_EMPTY:
fmt = kFmt35c;
break;
/* these should never appear when scanning code */
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_BREAKPOINT:
case OP_UNUSED_F1:
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 kFmt20bc: // op AA, thing@BBBB
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
case kFmt3rinline: // [opt] execute-inline/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.
*/
size_t dexGetInstrOrTableWidthAbs(const InstructionWidth* widths,
const u2* insns)
{
size_t 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;
}