Index: source/API/SystemInitializerFull.cpp =================================================================== --- source/API/SystemInitializerFull.cpp +++ source/API/SystemInitializerFull.cpp @@ -50,6 +50,7 @@ #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" #include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h" #include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h" #include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h" @@ -326,6 +327,7 @@ UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); EmulateInstructionARM64::Initialize(); + EmulateInstructionPPC64::Initialize(); SymbolFileDWARFDebugMap::Initialize(); ItaniumABILanguageRuntime::Initialize(); AppleObjCRuntimeV2::Initialize(); @@ -451,6 +453,7 @@ UnwindAssembly_x86::Terminate(); UnwindAssemblyInstEmulation::Terminate(); EmulateInstructionARM64::Terminate(); + EmulateInstructionPPC64::Terminate(); SymbolFileDWARFDebugMap::Terminate(); ItaniumABILanguageRuntime::Terminate(); AppleObjCRuntimeV2::Terminate(); Index: source/Plugins/Instruction/CMakeLists.txt =================================================================== --- source/Plugins/Instruction/CMakeLists.txt +++ source/Plugins/Instruction/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(ARM64) add_subdirectory(MIPS) add_subdirectory(MIPS64) +add_subdirectory(PPC64) Index: source/Plugins/Instruction/PPC64/CMakeLists.txt =================================================================== --- /dev/null +++ source/Plugins/Instruction/PPC64/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginInstructionPPC64 PLUGIN + EmulateInstructionPPC64.cpp + + LINK_LIBS + lldbCore + lldbInterpreter + lldbSymbol + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ) Index: source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h =================================================================== --- /dev/null +++ source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h @@ -0,0 +1,97 @@ +//===-- EmulateInstructionPPC64.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef EmulateInstructionPPC64_h_ +#define EmulateInstructionPPC64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Utility/Log.h" + +namespace lldb_private { + +class EmulateInstructionPPC64 : public EmulateInstruction { +public: + EmulateInstructionPPC64(const ArchSpec &arch); + + static void Initialize(); + + static void Terminate(); + + static ConstString GetPluginNameStatic(); + + static const char *GetPluginDescriptionStatic(); + + static EmulateInstruction *CreateInstance(const ArchSpec &arch, + InstructionType inst_type); + + static bool + SupportsEmulatingInstructionsOfTypeStatic(InstructionType inst_type) { + switch (inst_type) { + case eInstructionTypeAny: + case eInstructionTypePrologueEpilogue: + return true; + + case eInstructionTypePCModifying: + case eInstructionTypeAll: + return false; + } + return false; + } + + ConstString GetPluginName() override; + + uint32_t GetPluginVersion() override { return 1; } + + bool SetTargetTriple(const ArchSpec &arch) override; + + bool SupportsEmulatingInstructionsOfType(InstructionType inst_type) override { + return SupportsEmulatingInstructionsOfTypeStatic(inst_type); + } + + bool ReadInstruction() override; + + bool EvaluateInstruction(uint32_t evaluate_options) override; + + bool TestEmulation(Stream *out_stream, ArchSpec &arch, + OptionValueDictionary *test_data) override { + return false; + } + + bool GetRegisterInfo(lldb::RegisterKind reg_kind, uint32_t reg_num, + RegisterInfo ®_info) override; + + bool CreateFunctionEntryUnwind(UnwindPlan &unwind_plan) override; + +private: + struct Opcode { + uint32_t mask; + uint32_t value; + bool (EmulateInstructionPPC64::*callback)(uint32_t opcode); + const char *name; + }; + + uint32_t m_fp = LLDB_INVALID_REGNUM; + + Opcode *GetOpcodeForInstruction(uint32_t opcode); + + bool EmulateMFSPR(uint32_t opcode); + bool EmulateLD(uint32_t opcode); + bool EmulateSTD(uint32_t opcode); + bool EmulateOR(uint32_t opcode); + bool EmulateADDI(uint32_t opcode); +}; + +} // namespace lldb_private + +#endif // EmulateInstructionPPC64_h_ Index: source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp =================================================================== --- /dev/null +++ source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp @@ -0,0 +1,406 @@ +//===-- EmulateInstructionPPC64.cpp ------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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; + +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().getArch() == llvm::Triple::ppc64 || + arch.GetTriple().getArch() == llvm::Triple::ppc64le) { + return new EmulateInstructionPPC64(arch); + } + } + + return nullptr; +} + +bool EmulateInstructionPPC64::SetTargetTriple(const ArchSpec &arch) { + if (arch.GetTriple().getArch() == llvm::Triple::ppc64) + return true; + else if (arch.GetTriple().getArch() == llvm::Triple::ppc64le) + return true; + + return false; +} + +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.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 (auto_advance_pc && (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; +} Index: tools/lldb-test/SystemInitializerTest.cpp =================================================================== --- tools/lldb-test/SystemInitializerTest.cpp +++ tools/lldb-test/SystemInitializerTest.cpp @@ -40,6 +40,7 @@ #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" #include "Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h" #include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h" +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" #include "Plugins/InstrumentationRuntime/ASan/ASanRuntime.h" #include "Plugins/InstrumentationRuntime/MainThreadChecker/MainThreadCheckerRuntime.h" #include "Plugins/InstrumentationRuntime/TSan/TSanRuntime.h" @@ -180,6 +181,7 @@ UnwindAssemblyInstEmulation::Initialize(); UnwindAssembly_x86::Initialize(); EmulateInstructionARM64::Initialize(); + EmulateInstructionPPC64::Initialize(); SymbolFileDWARFDebugMap::Initialize(); ItaniumABILanguageRuntime::Initialize(); AppleObjCRuntimeV2::Initialize(); @@ -283,6 +285,7 @@ UnwindAssembly_x86::Terminate(); UnwindAssemblyInstEmulation::Terminate(); EmulateInstructionARM64::Terminate(); + EmulateInstructionPPC64::Terminate(); SymbolFileDWARFDebugMap::Terminate(); ItaniumABILanguageRuntime::Terminate(); AppleObjCRuntimeV2::Terminate(); Index: unittests/UnwindAssembly/InstEmulation/CMakeLists.txt =================================================================== --- unittests/UnwindAssembly/InstEmulation/CMakeLists.txt +++ unittests/UnwindAssembly/InstEmulation/CMakeLists.txt @@ -1,13 +1,23 @@ if ("AArch64" IN_LIST LLVM_TARGETS_TO_BUILD) + list(APPEND INST_EMU_SRCS "TestArm64InstEmulation.cpp") + list(APPEND INST_EMU_LIBS "lldbPluginInstructionARM64") +endif() + +if ("PowerPC" IN_LIST LLVM_TARGETS_TO_BUILD) + list(APPEND INST_EMU_SRCS "TestPPC64InstEmulation.cpp") + list(APPEND INST_EMU_LIBS "lldbPluginInstructionPPC64") +endif() + +if (INST_EMU_SRCS) add_lldb_unittest(InstEmulationTests - TestArm64InstEmulation.cpp + ${INST_EMU_SRCS} LINK_LIBS lldbCore lldbSymbol lldbTarget lldbPluginUnwindAssemblyInstEmulation lldbPluginDisassemblerLLVM - lldbPluginInstructionARM64 + ${INST_EMU_LIBS} lldbPluginProcessUtility LINK_COMPONENTS Support Index: unittests/UnwindAssembly/InstEmulation/TestPPC64InstEmulation.cpp =================================================================== --- /dev/null +++ unittests/UnwindAssembly/InstEmulation/TestPPC64InstEmulation.cpp @@ -0,0 +1,259 @@ +//===-- TestPPC64InstEmulation.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include + +#include "Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/UnwindAssembly.h" +#include "lldb/Utility/ArchSpec.h" + +#include "Plugins/Disassembler/llvm/DisassemblerLLVMC.h" +#include "Plugins/Instruction/PPC64/EmulateInstructionPPC64.h" +#include "Plugins/Process/Utility/lldb-ppc64le-register-enums.h" +#include "llvm/Support/TargetSelect.h" + +using namespace lldb; +using namespace lldb_private; + +class TestPPC64InstEmulation : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + + // virtual void SetUp() override { } + // virtual void TearDown() override { } + +protected: +}; + +void TestPPC64InstEmulation::SetUpTestCase() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + DisassemblerLLVMC::Initialize(); + EmulateInstructionPPC64::Initialize(); +} + +void TestPPC64InstEmulation::TearDownTestCase() { + DisassemblerLLVMC::Terminate(); + EmulateInstructionPPC64::Terminate(); +} + +TEST_F(TestPPC64InstEmulation, TestSimpleFunction) { + ArchSpec arch("powerpc64le-linux-gnu"); + std::unique_ptr engine( + static_cast( + UnwindAssemblyInstEmulation::CreateInstance(arch))); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // prologue and epilogue of: + // int main() { + // int i = test(); + // return i; + // } + // + // compiled with clang -O0 -g + uint8_t data[] = { + // prologue + 0x02, 0x10, 0x40, 0x3c, // 0: lis r2, 4098 + 0x00, 0x7f, 0x42, 0x38, // 4: addi r2, r2, 32512 + 0xa6, 0x02, 0x08, 0x7c, // 8: mflr r0 + 0xf8, 0xff, 0xe1, 0xfb, // 12: std r31, -8(r1) + 0x10, 0x00, 0x01, 0xf8, // 16: std r0, 16(r1) + 0x91, 0xff, 0x21, 0xf8, // 20: stdu r1, -112(r1) + 0x78, 0x0b, 0x3f, 0x7c, // 24: mr r31, r1 + 0x00, 0x00, 0x60, 0x38, // 28: li r3, 0 + 0x64, 0x00, 0x7f, 0x90, // 32: stw r3, 100(r31) + + // epilogue + 0x70, 0x00, 0x21, 0x38, // 36: addi r1, r1, 112 + 0x10, 0x00, 0x01, 0xe8, // 40: ld r0, 16(r1) + 0xf8, 0xff, 0xe1, 0xeb, // 44: ld r31, -8(r1) + 0xa6, 0x03, 0x08, 0x7c, // 48: mtlr r0 + 0x20, 0x00, 0x80, 0x4e // 52: blr + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // 0: CFA=sp+0 + row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + // 1: CFA=sp+0 => fp=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // 2: CFA=sp+0 => fp=[CFA-8] lr=[CFA+16] + row_sp = unwind_plan.GetRowForFunctionOffset(20); + EXPECT_EQ(20ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(16, regloc.GetOffset()); + + // 3: CFA=sp+112 => fp=[CFA-8] lr=[CFA+16] + row_sp = unwind_plan.GetRowForFunctionOffset(24); + EXPECT_EQ(24ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(112, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(16, regloc.GetOffset()); + + // 4: CFA=r31+112 => fp=[CFA-8] lr=[CFA+16] + row_sp = unwind_plan.GetRowForFunctionOffset(28); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r31_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(112, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(16, regloc.GetOffset()); + + // 5: CFA=sp+0 => fp=[CFA-8] lr=[CFA+16] + row_sp = unwind_plan.GetRowForFunctionOffset(40); + EXPECT_EQ(40ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(16, regloc.GetOffset()); +} + +TEST_F(TestPPC64InstEmulation, TestMediumFunction) { + ArchSpec arch("powerpc64le-linux-gnu"); + std::unique_ptr engine( + static_cast( + UnwindAssemblyInstEmulation::CreateInstance(arch))); + ASSERT_NE(nullptr, engine); + + UnwindPlan::RowSP row_sp; + AddressRange sample_range; + UnwindPlan unwind_plan(eRegisterKindLLDB); + UnwindPlan::Row::RegisterLocation regloc; + + // prologue and epilogue of main() (call-func.c), + // with several calls and stack variables. + // + // compiled with clang -O0 -g + uint8_t data[] = { + // prologue + 0xa6, 0x02, 0x08, 0x7c, // 0: mflr r0 + 0xf8, 0xff, 0xe1, 0xfb, // 4: std r31, -8(r1) + 0x10, 0x00, 0x01, 0xf8, // 8: std r0, 16(r1) + 0x78, 0x0b, 0x3e, 0x7c, // 12: mr r30, r1 + 0xe0, 0x06, 0x20, 0x78, // 16: clrldi r0, r1, 59 + 0xa0, 0xfa, 0x00, 0x20, // 20: subfic r0, r0, -1376 + 0x6a, 0x01, 0x21, 0x7c, // 24: stdux r1, r1, r0 + 0x78, 0x0b, 0x3f, 0x7c, // 28: mr r31, r1 + + // epilogue + 0x00, 0x00, 0x21, 0xe8, // 32: ld r1, 0(r1) + 0x20, 0x00, 0x80, 0x4e // 36: blr + }; + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // 0: CFA=sp+0 + row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + // 1: CFA=sp+0 => fp=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(8); + EXPECT_EQ(8ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_r31_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // 2: CFA=sp+0 => fp=[CFA-8] lr=[CFA+16] + row_sp = unwind_plan.GetRowForFunctionOffset(12); + EXPECT_EQ(12ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_ppc64le, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(16, regloc.GetOffset()); + + // 3: CFA=r30 + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r30_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(32); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r30_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + // 4: CFA=sp+0 + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_EQ(36ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_r1_ppc64le); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); +}