blob: d042416fab8ccb2e8a01f7023a4e96901b6ada2a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* @author Alexander V. Astapchuk
*/
/**
* @file
* @brief Disassembler for JVMTI implementation.
*/
// FIXME: Highly platform specific, should be renamed to jvmti_dasm_x86.cpp
// or go to arch specific directory.
#if (defined _IA32_) || (defined _EM64T_)
#include "jvmti_dasm.h"
#include "dec_base.h"
static InstructionDisassembler::Register convertRegName2Register(RegName reg)
{
switch(reg) {
#ifdef _IA32_
case RegName_Null: return InstructionDisassembler::DISASM_REG_NONE;
case RegName_EAX: return InstructionDisassembler::DISASM_REG_EAX;
case RegName_EBX: return InstructionDisassembler::DISASM_REG_EBX;
case RegName_ECX: return InstructionDisassembler::DISASM_REG_ECX;
case RegName_EDX: return InstructionDisassembler::DISASM_REG_EDX;
case RegName_ESI: return InstructionDisassembler::DISASM_REG_ESI;
case RegName_EDI: return InstructionDisassembler::DISASM_REG_EDI;
case RegName_EBP: return InstructionDisassembler::DISASM_REG_EBP;
case RegName_ESP: return InstructionDisassembler::DISASM_REG_ESP;
#elif defined(_EM64T_)
case RegName_Null: return InstructionDisassembler::DISASM_REG_NONE;
case RegName_RAX: return InstructionDisassembler::DISASM_REG_RAX;
case RegName_RBX: return InstructionDisassembler::DISASM_REG_RBX;
case RegName_RCX: return InstructionDisassembler::DISASM_REG_RCX;
case RegName_RDX: return InstructionDisassembler::DISASM_REG_RDX;
case RegName_RSI: return InstructionDisassembler::DISASM_REG_RSI;
case RegName_RDI: return InstructionDisassembler::DISASM_REG_RDI;
case RegName_RSP: return InstructionDisassembler::DISASM_REG_RSP;
case RegName_RBP: return InstructionDisassembler::DISASM_REG_RBP;
case RegName_R8 : return InstructionDisassembler::DISASM_REG_R8;
case RegName_R9 : return InstructionDisassembler::DISASM_REG_R9;
case RegName_R10: return InstructionDisassembler::DISASM_REG_R10;
case RegName_R11: return InstructionDisassembler::DISASM_REG_R11;
case RegName_R12: return InstructionDisassembler::DISASM_REG_R12;
case RegName_R13: return InstructionDisassembler::DISASM_REG_R13;
case RegName_R14: return InstructionDisassembler::DISASM_REG_R14;
case RegName_R15: return InstructionDisassembler::DISASM_REG_R15;
#endif
default:
break;
}
// Some other registers (e.g. AL or XMM or whatever) - not
// supported currently
return InstructionDisassembler::DISASM_REG_NONE;
}
static void convertOperand2Opnd(
InstructionDisassembler::Opnd* pdst,
const EncoderBase::Operand& src)
{
if (src.is_imm()) {
pdst->kind = InstructionDisassembler::Kind_Imm;
pdst->imm = (int)src.imm();
}
else if (src.is_mem()) {
pdst->kind = InstructionDisassembler::Kind_Mem;
pdst->base = convertRegName2Register(src.base());
pdst->index = convertRegName2Register(src.index());
pdst->disp = src.disp();
pdst->scale = src.scale();
}
else {
assert(src.is_reg());
pdst->kind = InstructionDisassembler::Kind_Reg;
pdst->reg = convertRegName2Register(src.reg());
}
}
#ifdef _IPF_
const char* InstructionDisassembler::get_reg_value(
Register reg,
const Registers* pcontext) const
{
assert(0);
return NULL;
}
#elif defined _EM64T_
const char* InstructionDisassembler::get_reg_value(
Register reg,
const Registers* pcontext) const
{
switch(reg) {
case DISASM_REG_NONE: return NULL;
case DISASM_REG_RAX: return (const char*)pcontext->rax;
case DISASM_REG_RBX: return (const char*)pcontext->rbx;
case DISASM_REG_RCX: return (const char*)pcontext->rcx;
case DISASM_REG_RDX: return (const char*)pcontext->rdx;
case DISASM_REG_RSI: return (const char*)pcontext->rsi;
case DISASM_REG_RDI: return (const char*)pcontext->rdi;
case DISASM_REG_RBP: return (const char*)pcontext->rbp;
case DISASM_REG_RSP: return (const char*)pcontext->rsp;
case DISASM_REG_R8 : return (const char*)pcontext->r8 ;
case DISASM_REG_R9 : return (const char*)pcontext->r9 ;
case DISASM_REG_R10: return (const char*)pcontext->r10;
case DISASM_REG_R11: return (const char*)pcontext->r11;
case DISASM_REG_R12: return (const char*)pcontext->r12;
case DISASM_REG_R13: return (const char*)pcontext->r13;
case DISASM_REG_R14: return (const char*)pcontext->r14;
case DISASM_REG_R15: return (const char*)pcontext->r15;
default: assert(false);
}
return NULL;
}
#else // _IA32_
const char* InstructionDisassembler::get_reg_value(
Register reg,
const Registers* pcontext) const
{
switch(reg) {
case DISASM_REG_NONE: return NULL;
case DISASM_REG_EAX: return (const char*)pcontext->eax;
case DISASM_REG_EBX: return (const char*)pcontext->ebx;
case DISASM_REG_ECX: return (const char*)pcontext->ecx;
case DISASM_REG_EDX: return (const char*)pcontext->edx;
case DISASM_REG_ESI: return (const char*)pcontext->esi;
case DISASM_REG_EDI: return (const char*)pcontext->edi;
case DISASM_REG_EBP: return (const char*)pcontext->ebp;
case DISASM_REG_ESP: return (const char*)pcontext->esp;
default: assert(false);
}
return NULL;
}
#endif // _IA32_
void InstructionDisassembler::disasm(const NativeCodePtr addr,
InstructionDisassembler * pidi)
{
assert(pidi != NULL);
Inst inst;
pidi->m_len = DecoderBase::decode(addr, &inst);
if (pidi->m_len == 0) {
// Something wrong happened
pidi->m_type = OPCODEERROR;
return;
}
pidi->m_type = UNKNOWN;
pidi->m_argc = inst.argc;
for (unsigned i=0; i<inst.argc; i++) {
convertOperand2Opnd(&pidi->m_opnds[i], inst.operands[i]);
}
if (inst.mn == Mnemonic_CALL) {
assert(pidi->m_argc == 1);
if (inst.operands[0].is_imm()) {
pidi->m_type = RELATIVE_CALL;
pidi->m_target = (NativeCodePtr)((char*)addr + pidi->m_len + inst.operands[0].imm());
}
else {
pidi->m_type = INDIRECT_CALL;
}
}
else if (inst.mn == Mnemonic_JMP) {
assert(pidi->m_argc == 1);
if (inst.operands[0].is_imm()) {
pidi->m_type = RELATIVE_JUMP;
pidi->m_target = (NativeCodePtr)((char*)addr + pidi->m_len + inst.operands[0].imm());
}
else {
pidi->m_type = INDIRECT_JUMP;
}
}
else if (inst.mn == Mnemonic_RET) {
pidi->m_type = RET;
}
else if (is_jcc(inst.mn)) {
// relative Jcc is the only possible variant
assert(pidi->m_argc == 1);
assert(inst.odesc->opnds[0].kind == OpndKind_Imm);
pidi->m_cond_jump_type = (CondJumpType)(inst.mn-Mnemonic_Jcc);
assert(pidi->m_cond_jump_type < CondJumpType_Count);
pidi->m_target = (NativeCodePtr)((char*)addr + pidi->m_len + inst.operands[0].imm());
pidi->m_type = RELATIVE_COND_JUMP;
}
}
NativeCodePtr
InstructionDisassembler::get_target_address_from_context(const Registers* pcontext) const
{
switch(get_type()) {
case RELATIVE_JUMP:
case RELATIVE_COND_JUMP:
case RELATIVE_CALL:
return m_target;
case INDIRECT_JUMP:
case INDIRECT_CALL:
// Only CALL/JMP mem/reg expected - single argument
assert(m_argc == 1);
{
const Opnd& op = get_opnd(0);
if (op.kind == Kind_Reg) {
assert(op.reg != DISASM_REG_NONE);
return (NativeCodePtr)get_reg_value(op.reg, pcontext);
}
else if (op.kind == Kind_Mem) {
char* base = (char*)get_reg_value(op.base, pcontext);
char* index = (char*)get_reg_value(op.index, pcontext);
unsigned scale = op.scale;
int disp = op.disp;
char* targetAddrPtr = base + ((long)index)*scale + disp;
return (NativeCodePtr)*(void**)targetAddrPtr;
}
}
// 0th arg is neither memory, nor register, possibly immediate?
// can't happen for INDIRECT_xxx.
assert(false);
return NULL;
#ifdef _IA32_
case RET:
{
const char* sp_value = get_reg_value(DISASM_REG_ESP, pcontext);
const char* retAddr = *(char**)sp_value;
return (NativeCodePtr)retAddr;
}
#endif // _IA32_
default:
// This method should not be called for non-branch instructions.
assert(false);
}
return NULL;
}
#endif