//===-- 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 #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; }