You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
12 KiB
401 lines
12 KiB
//===-- EmulateInstructionPPC64.cpp ---------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "EmulateInstructionPPC64.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Symbol/UnwindPlan.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/Utility/ConstString.h"
|
|
|
|
#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h"
|
|
|
|
#define DECLARE_REGISTER_INFOS_PPC64LE_STRUCT
|
|
#include "Plugins/Process/Utility/RegisterInfos_ppc64le.h"
|
|
|
|
#include "Plugins/Process/Utility/InstructionUtils.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionPPC64, InstructionPPC64)
|
|
|
|
EmulateInstructionPPC64::EmulateInstructionPPC64(const ArchSpec &arch)
|
|
: EmulateInstruction(arch) {}
|
|
|
|
void EmulateInstructionPPC64::Initialize() {
|
|
PluginManager::RegisterPlugin(GetPluginNameStatic(),
|
|
GetPluginDescriptionStatic(), CreateInstance);
|
|
}
|
|
|
|
void EmulateInstructionPPC64::Terminate() {
|
|
PluginManager::UnregisterPlugin(CreateInstance);
|
|
}
|
|
|
|
ConstString EmulateInstructionPPC64::GetPluginNameStatic() {
|
|
ConstString g_plugin_name("lldb.emulate-instruction.ppc64");
|
|
return g_plugin_name;
|
|
}
|
|
|
|
ConstString EmulateInstructionPPC64::GetPluginName() {
|
|
static ConstString g_plugin_name("EmulateInstructionPPC64");
|
|
return g_plugin_name;
|
|
}
|
|
|
|
const char *EmulateInstructionPPC64::GetPluginDescriptionStatic() {
|
|
return "Emulate instructions for the PPC64 architecture.";
|
|
}
|
|
|
|
EmulateInstruction *
|
|
EmulateInstructionPPC64::CreateInstance(const ArchSpec &arch,
|
|
InstructionType inst_type) {
|
|
if (EmulateInstructionPPC64::SupportsEmulatingInstructionsOfTypeStatic(
|
|
inst_type))
|
|
if (arch.GetTriple().isPPC64())
|
|
return new EmulateInstructionPPC64(arch);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::SetTargetTriple(const ArchSpec &arch) {
|
|
return arch.GetTriple().isPPC64();
|
|
}
|
|
|
|
static bool LLDBTableGetRegisterInfo(uint32_t reg_num, RegisterInfo ®_info) {
|
|
if (reg_num >= llvm::array_lengthof(g_register_infos_ppc64le))
|
|
return false;
|
|
reg_info = g_register_infos_ppc64le[reg_num];
|
|
return true;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::GetRegisterInfo(RegisterKind reg_kind,
|
|
uint32_t reg_num,
|
|
RegisterInfo ®_info) {
|
|
if (reg_kind == eRegisterKindGeneric) {
|
|
switch (reg_num) {
|
|
case LLDB_REGNUM_GENERIC_PC:
|
|
reg_kind = eRegisterKindLLDB;
|
|
reg_num = gpr_pc_ppc64le;
|
|
break;
|
|
case LLDB_REGNUM_GENERIC_SP:
|
|
reg_kind = eRegisterKindLLDB;
|
|
reg_num = gpr_r1_ppc64le;
|
|
break;
|
|
case LLDB_REGNUM_GENERIC_RA:
|
|
reg_kind = eRegisterKindLLDB;
|
|
reg_num = gpr_lr_ppc64le;
|
|
break;
|
|
case LLDB_REGNUM_GENERIC_FLAGS:
|
|
reg_kind = eRegisterKindLLDB;
|
|
reg_num = gpr_cr_ppc64le;
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (reg_kind == eRegisterKindLLDB)
|
|
return LLDBTableGetRegisterInfo(reg_num, reg_info);
|
|
return false;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::ReadInstruction() {
|
|
bool success = false;
|
|
m_addr = ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
|
|
LLDB_INVALID_ADDRESS, &success);
|
|
if (success) {
|
|
Context ctx;
|
|
ctx.type = eContextReadOpcode;
|
|
ctx.SetNoArgs();
|
|
m_opcode.SetOpcode32(ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success),
|
|
GetByteOrder());
|
|
}
|
|
if (!success)
|
|
m_addr = LLDB_INVALID_ADDRESS;
|
|
return success;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::CreateFunctionEntryUnwind(
|
|
UnwindPlan &unwind_plan) {
|
|
unwind_plan.Clear();
|
|
unwind_plan.SetRegisterKind(eRegisterKindLLDB);
|
|
|
|
UnwindPlan::RowSP row(new UnwindPlan::Row);
|
|
|
|
// Our previous Call Frame Address is the stack pointer
|
|
row->GetCFAValue().SetIsRegisterPlusOffset(gpr_r1_ppc64le, 0);
|
|
|
|
unwind_plan.AppendRow(row);
|
|
unwind_plan.SetSourceName("EmulateInstructionPPC64");
|
|
unwind_plan.SetSourcedFromCompiler(eLazyBoolNo);
|
|
unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
|
|
unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo);
|
|
unwind_plan.SetReturnAddressRegister(gpr_lr_ppc64le);
|
|
return true;
|
|
}
|
|
|
|
EmulateInstructionPPC64::Opcode *
|
|
EmulateInstructionPPC64::GetOpcodeForInstruction(uint32_t opcode) {
|
|
static EmulateInstructionPPC64::Opcode g_opcodes[] = {
|
|
{0xfc0007ff, 0x7c0002a6, &EmulateInstructionPPC64::EmulateMFSPR,
|
|
"mfspr RT, SPR"},
|
|
{0xfc000003, 0xf8000000, &EmulateInstructionPPC64::EmulateSTD,
|
|
"std RS, DS(RA)"},
|
|
{0xfc000003, 0xf8000001, &EmulateInstructionPPC64::EmulateSTD,
|
|
"stdu RS, DS(RA)"},
|
|
{0xfc0007fe, 0x7c000378, &EmulateInstructionPPC64::EmulateOR,
|
|
"or RA, RS, RB"},
|
|
{0xfc000000, 0x38000000, &EmulateInstructionPPC64::EmulateADDI,
|
|
"addi RT, RA, SI"},
|
|
{0xfc000003, 0xe8000000, &EmulateInstructionPPC64::EmulateLD,
|
|
"ld RT, DS(RA)"}};
|
|
static const size_t k_num_ppc_opcodes = llvm::array_lengthof(g_opcodes);
|
|
|
|
for (size_t i = 0; i < k_num_ppc_opcodes; ++i) {
|
|
if ((g_opcodes[i].mask & opcode) == g_opcodes[i].value)
|
|
return &g_opcodes[i];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::EvaluateInstruction(uint32_t evaluate_options) {
|
|
const uint32_t opcode = m_opcode.GetOpcode32();
|
|
// LLDB_LOG(log, "PPC64::EvaluateInstruction: opcode={0:X+8}", opcode);
|
|
Opcode *opcode_data = GetOpcodeForInstruction(opcode);
|
|
if (!opcode_data)
|
|
return false;
|
|
|
|
// LLDB_LOG(log, "PPC64::EvaluateInstruction: {0}", opcode_data->name);
|
|
const bool auto_advance_pc =
|
|
evaluate_options & eEmulateInstructionOptionAutoAdvancePC;
|
|
|
|
bool success = false;
|
|
|
|
uint32_t orig_pc_value = 0;
|
|
if (auto_advance_pc) {
|
|
orig_pc_value =
|
|
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success);
|
|
if (!success)
|
|
return false;
|
|
}
|
|
|
|
// Call the Emulate... function.
|
|
success = (this->*opcode_data->callback)(opcode);
|
|
if (!success)
|
|
return false;
|
|
|
|
if (auto_advance_pc) {
|
|
uint32_t new_pc_value =
|
|
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_ppc64le, 0, &success);
|
|
if (!success)
|
|
return false;
|
|
|
|
if (new_pc_value == orig_pc_value) {
|
|
EmulateInstruction::Context context;
|
|
context.type = eContextAdvancePC;
|
|
context.SetNoArgs();
|
|
if (!WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_pc_ppc64le,
|
|
orig_pc_value + 4))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::EmulateMFSPR(uint32_t opcode) {
|
|
uint32_t rt = Bits32(opcode, 25, 21);
|
|
uint32_t spr = Bits32(opcode, 20, 11);
|
|
|
|
enum { SPR_LR = 0x100 };
|
|
|
|
// For now, we're only insterested in 'mfspr r0, lr'
|
|
if (rt != gpr_r0_ppc64le || spr != SPR_LR)
|
|
return false;
|
|
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
|
|
LLDB_LOG(log, "EmulateMFSPR: {0:X+8}: mfspr r0, lr", m_addr);
|
|
|
|
bool success;
|
|
uint64_t lr =
|
|
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success);
|
|
if (!success)
|
|
return false;
|
|
Context context;
|
|
context.type = eContextWriteRegisterRandomBits;
|
|
WriteRegisterUnsigned(context, eRegisterKindLLDB, gpr_r0_ppc64le, lr);
|
|
LLDB_LOG(log, "EmulateMFSPR: success!");
|
|
return true;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::EmulateLD(uint32_t opcode) {
|
|
uint32_t rt = Bits32(opcode, 25, 21);
|
|
uint32_t ra = Bits32(opcode, 20, 16);
|
|
uint32_t ds = Bits32(opcode, 15, 2);
|
|
|
|
int32_t ids = llvm::SignExtend32<16>(ds << 2);
|
|
|
|
// For now, tracking only loads from 0(r1) to r1 (0(r1) is the ABI defined
|
|
// location to save previous SP)
|
|
if (ra != gpr_r1_ppc64le || rt != gpr_r1_ppc64le || ids != 0)
|
|
return false;
|
|
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
|
|
LLDB_LOG(log, "EmulateLD: {0:X+8}: ld r{1}, {2}(r{3})", m_addr, rt, ids, ra);
|
|
|
|
RegisterInfo r1_info;
|
|
if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info))
|
|
return false;
|
|
|
|
// restore SP
|
|
Context ctx;
|
|
ctx.type = eContextRestoreStackPointer;
|
|
ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0);
|
|
|
|
WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, 0);
|
|
LLDB_LOG(log, "EmulateLD: success!");
|
|
return true;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::EmulateSTD(uint32_t opcode) {
|
|
uint32_t rs = Bits32(opcode, 25, 21);
|
|
uint32_t ra = Bits32(opcode, 20, 16);
|
|
uint32_t ds = Bits32(opcode, 15, 2);
|
|
uint32_t u = Bits32(opcode, 1, 0);
|
|
|
|
// For now, tracking only stores to r1
|
|
if (ra != gpr_r1_ppc64le)
|
|
return false;
|
|
// ... and only stores of SP, FP and LR (moved into r0 by a previous mfspr)
|
|
if (rs != gpr_r1_ppc64le && rs != gpr_r31_ppc64le && rs != gpr_r30_ppc64le &&
|
|
rs != gpr_r0_ppc64le)
|
|
return false;
|
|
|
|
bool success;
|
|
uint64_t rs_val = ReadRegisterUnsigned(eRegisterKindLLDB, rs, 0, &success);
|
|
if (!success)
|
|
return false;
|
|
|
|
int32_t ids = llvm::SignExtend32<16>(ds << 2);
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
|
|
LLDB_LOG(log, "EmulateSTD: {0:X+8}: std{1} r{2}, {3}(r{4})", m_addr,
|
|
u ? "u" : "", rs, ids, ra);
|
|
|
|
// Make sure that r0 is really holding LR value (this won't catch unlikely
|
|
// cases, such as r0 being overwritten after mfspr)
|
|
uint32_t rs_num = rs;
|
|
if (rs == gpr_r0_ppc64le) {
|
|
uint64_t lr =
|
|
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_lr_ppc64le, 0, &success);
|
|
if (!success || lr != rs_val)
|
|
return false;
|
|
rs_num = gpr_lr_ppc64le;
|
|
}
|
|
|
|
// set context
|
|
RegisterInfo rs_info;
|
|
if (!GetRegisterInfo(eRegisterKindLLDB, rs_num, rs_info))
|
|
return false;
|
|
RegisterInfo ra_info;
|
|
if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info))
|
|
return false;
|
|
|
|
Context ctx;
|
|
ctx.type = eContextPushRegisterOnStack;
|
|
ctx.SetRegisterToRegisterPlusOffset(rs_info, ra_info, ids);
|
|
|
|
// store
|
|
uint64_t ra_val = ReadRegisterUnsigned(eRegisterKindLLDB, ra, 0, &success);
|
|
if (!success)
|
|
return false;
|
|
|
|
lldb::addr_t addr = ra_val + ids;
|
|
WriteMemory(ctx, addr, &rs_val, sizeof(rs_val));
|
|
|
|
// update RA?
|
|
if (u) {
|
|
Context ctx;
|
|
// NOTE Currently, RA will always be equal to SP(r1)
|
|
ctx.type = eContextAdjustStackPointer;
|
|
WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, addr);
|
|
}
|
|
|
|
LLDB_LOG(log, "EmulateSTD: success!");
|
|
return true;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::EmulateOR(uint32_t opcode) {
|
|
uint32_t rs = Bits32(opcode, 25, 21);
|
|
uint32_t ra = Bits32(opcode, 20, 16);
|
|
uint32_t rb = Bits32(opcode, 15, 11);
|
|
|
|
// to be safe, process only the known 'mr r31/r30, r1' prologue instructions
|
|
if (m_fp != LLDB_INVALID_REGNUM || rs != rb ||
|
|
(ra != gpr_r30_ppc64le && ra != gpr_r31_ppc64le) || rb != gpr_r1_ppc64le)
|
|
return false;
|
|
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
|
|
LLDB_LOG(log, "EmulateOR: {0:X+8}: mr r{1}, r{2}", m_addr, ra, rb);
|
|
|
|
// set context
|
|
RegisterInfo ra_info;
|
|
if (!GetRegisterInfo(eRegisterKindLLDB, ra, ra_info))
|
|
return false;
|
|
|
|
Context ctx;
|
|
ctx.type = eContextSetFramePointer;
|
|
ctx.SetRegister(ra_info);
|
|
|
|
// move
|
|
bool success;
|
|
uint64_t rb_val = ReadRegisterUnsigned(eRegisterKindLLDB, rb, 0, &success);
|
|
if (!success)
|
|
return false;
|
|
WriteRegisterUnsigned(ctx, eRegisterKindLLDB, ra, rb_val);
|
|
m_fp = ra;
|
|
LLDB_LOG(log, "EmulateOR: success!");
|
|
return true;
|
|
}
|
|
|
|
bool EmulateInstructionPPC64::EmulateADDI(uint32_t opcode) {
|
|
uint32_t rt = Bits32(opcode, 25, 21);
|
|
uint32_t ra = Bits32(opcode, 20, 16);
|
|
uint32_t si = Bits32(opcode, 15, 0);
|
|
|
|
// handle stack adjustments only
|
|
// (this is a typical epilogue operation, with ra == r1. If it's
|
|
// something else, then we won't know the correct value of ra)
|
|
if (rt != gpr_r1_ppc64le || ra != gpr_r1_ppc64le)
|
|
return false;
|
|
|
|
int32_t si_val = llvm::SignExtend32<16>(si);
|
|
Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_UNWIND));
|
|
LLDB_LOG(log, "EmulateADDI: {0:X+8}: addi r1, r1, {1}", m_addr, si_val);
|
|
|
|
// set context
|
|
RegisterInfo r1_info;
|
|
if (!GetRegisterInfo(eRegisterKindLLDB, gpr_r1_ppc64le, r1_info))
|
|
return false;
|
|
|
|
Context ctx;
|
|
ctx.type = eContextRestoreStackPointer;
|
|
ctx.SetRegisterToRegisterPlusOffset(r1_info, r1_info, 0);
|
|
|
|
// adjust SP
|
|
bool success;
|
|
uint64_t r1 =
|
|
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_r1_ppc64le, 0, &success);
|
|
if (!success)
|
|
return false;
|
|
WriteRegisterUnsigned(ctx, eRegisterKindLLDB, gpr_r1_ppc64le, r1 + si_val);
|
|
LLDB_LOG(log, "EmulateADDI: success!");
|
|
return true;
|
|
}
|