Index: lldb/trunk/source/API/SystemInitializerFull.cpp =================================================================== --- lldb/trunk/source/API/SystemInitializerFull.cpp +++ lldb/trunk/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: lldb/trunk/source/Plugins/Instruction/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/Instruction/CMakeLists.txt +++ lldb/trunk/source/Plugins/Instruction/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(ARM64) add_subdirectory(MIPS) add_subdirectory(MIPS64) +add_subdirectory(PPC64) Index: lldb/trunk/source/Plugins/Instruction/PPC64/CMakeLists.txt =================================================================== --- lldb/trunk/source/Plugins/Instruction/PPC64/CMakeLists.txt +++ lldb/trunk/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: lldb/trunk/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h =================================================================== --- lldb/trunk/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.h +++ lldb/trunk/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: lldb/trunk/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp =================================================================== --- lldb/trunk/source/Plugins/Instruction/PPC64/EmulateInstructionPPC64.cpp +++ lldb/trunk/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: lldb/trunk/tools/lldb-test/SystemInitializerTest.cpp =================================================================== --- lldb/trunk/tools/lldb-test/SystemInitializerTest.cpp +++ lldb/trunk/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: lldb/trunk/unittests/UnwindAssembly/ARM64/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/UnwindAssembly/ARM64/CMakeLists.txt +++ lldb/trunk/unittests/UnwindAssembly/ARM64/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_unittest(Arm64InstEmulationTests + TestArm64InstEmulation.cpp + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + lldbPluginUnwindAssemblyInstEmulation + lldbPluginDisassemblerLLVM + lldbPluginInstructionARM64 + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ${LLVM_TARGETS_TO_BUILD}) Index: lldb/trunk/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp =================================================================== --- lldb/trunk/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp +++ lldb/trunk/unittests/UnwindAssembly/ARM64/TestArm64InstEmulation.cpp @@ -0,0 +1,672 @@ +//===-- TestArm64InstEmulation.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/ARM64/EmulateInstructionARM64.h" +#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" +#include "llvm/Support/TargetSelect.h" + +using namespace lldb; +using namespace lldb_private; + +class TestArm64InstEmulation : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + + // virtual void SetUp() override { } + // virtual void TearDown() override { } + +protected: +}; + +void TestArm64InstEmulation::SetUpTestCase() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + DisassemblerLLVMC::Initialize(); + EmulateInstructionARM64::Initialize(); +} + +void TestArm64InstEmulation::TearDownTestCase() { + DisassemblerLLVMC::Terminate(); + EmulateInstructionARM64::Terminate(); +} + +TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) { + ArchSpec arch("arm64-apple-ios10"); + 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; + + // 'int main() { }' compiled for arm64-apple-ios with clang + uint8_t data[] = { + 0xfd, 0x7b, 0xbf, 0xa9, // 0xa9bf7bfd : stp x29, x30, [sp, #-0x10]! + 0xfd, 0x03, 0x00, 0x91, // 0x910003fd : mov x29, sp + 0xff, 0x43, 0x00, 0xd1, // 0xd10043ff : sub sp, sp, #0x10 + + 0xbf, 0x03, 0x00, 0x91, // 0x910003bf : mov sp, x29 + 0xfd, 0x7b, 0xc1, 0xa8, // 0xa8c17bfd : ldp x29, x30, [sp], #16 + 0xc0, 0x03, 0x5f, 0xd6, // 0xd65f03c0 : ret + }; + + // UnwindPlan we expect: + + // row[0]: 0: CFA=sp +0 => + // row[1]: 4: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + // row[2]: 8: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] + // row[2]: 16: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + // row[3]: 20: CFA=sp +0 => fp= lr= + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // CFA=sp +0 + row_sp = unwind_plan.GetRowForFunctionOffset(0); + EXPECT_EQ(0ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(8); + EXPECT_EQ(8ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // CFA=sp +0 => fp= lr= + row_sp = unwind_plan.GetRowForFunctionOffset(20); + EXPECT_EQ(20ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(TestArm64InstEmulation, TestMediumDarwinFunction) { + ArchSpec arch("arm64-apple-ios10"); + 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; + + // disassembly of -[NSPlaceholderString initWithBytes:length:encoding:] + // from Foundation for iOS. + uint8_t data[] = { + 0xf6, 0x57, 0xbd, 0xa9, // 0: 0xa9bd57f6 stp x22, x21, [sp, #-48]! + 0xf4, 0x4f, 0x01, 0xa9, // 4: 0xa9014ff4 stp x20, x19, [sp, #16] + 0xfd, 0x7b, 0x02, 0xa9, // 8: 0xa9027bfd stp x29, x30, [sp, #32] + 0xfd, 0x83, 0x00, 0x91, // 12: 0x910083fd add x29, sp, #32 + 0xff, 0x43, 0x00, 0xd1, // 16: 0xd10043ff sub sp, sp, #16 + + // [... function body ...] + 0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop + + 0xbf, 0x83, 0x00, 0xd1, // 24: 0xd10083bf sub sp, x29, #32 + 0xfd, 0x7b, 0x42, 0xa9, // 28: 0xa9427bfd ldp x29, x30, [sp, #32] + 0xf4, 0x4f, 0x41, 0xa9, // 32: 0xa9414ff4 ldp x20, x19, [sp, #16] + 0xf6, 0x57, 0xc3, 0xa8, // 36: 0xa8c357f6 ldp x22, x21, [sp], #48 + 0x01, 0x16, 0x09, 0x14, // 40: 0x14091601 b 0x18f640524 ; symbol stub + // for: CFStringCreateWithBytes + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48] + // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + + // [... function body ...] + + // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp= + // lr= + // 36: CFA=sp+48 => x19= x20= x21=[CFA-40] x22=[CFA-48] fp= + // lr= + // 40: CFA=sp +0 => x19= x20= x21= x22= fp= + // lr= + + 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_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48] + row_sp = unwind_plan.GetRowForFunctionOffset(4); + EXPECT_EQ(4ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-40, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-48, regloc.GetOffset()); + + // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + row_sp = unwind_plan.GetRowForFunctionOffset(8); + EXPECT_EQ(8ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-24, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-32, regloc.GetOffset()); + + // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(12); + EXPECT_EQ(12ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-16, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-8, regloc.GetOffset()); + + // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(16); + EXPECT_EQ(16ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // fp=[CFA-16] lr=[CFA-8] + row_sp = unwind_plan.GetRowForFunctionOffset(28); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); + + // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp= + // lr= + row_sp = unwind_plan.GetRowForFunctionOffset(32); + EXPECT_EQ(32ull, row_sp->GetOffset()); + + // I'd prefer if these restored registers were cleared entirely instead of set + // to IsSame... + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + // 36: CFA=sp+48 => x19= x20= x21=[CFA-40] x22=[CFA-48] fp= + // lr= + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_EQ(36ull, row_sp->GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + // 40: CFA=sp +0 => x19= x20= x21= x22= fp= + // lr= + row_sp = unwind_plan.GetRowForFunctionOffset(40); + EXPECT_EQ(40ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); + EXPECT_TRUE(regloc.IsSame()); +} + +TEST_F(TestArm64InstEmulation, TestFramelessThreeEpilogueFunction) { + ArchSpec arch("arm64-apple-ios10"); + 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; + + // disassembly of JSC::ARM64LogicalImmediate::findBitRange<16u> + // from JavaScriptcore for iOS. + uint8_t data[] = { + 0x08, 0x3c, 0x0f, 0x53, // 0: 0x530f3c08 ubfx w8, w0, #15, #1 + 0x68, 0x00, 0x00, 0x39, // 4: 0x39000068 strb w8, [x3] + 0x08, 0x3c, 0x40, 0xd2, // 8: 0xd2403c08 eor x8, x0, #0xffff + 0x1f, 0x00, 0x71, 0xf2, // 12: 0xf271001f tst x0, #0x8000 + + // [...] + + 0x3f, 0x01, 0x0c, 0xeb, // 16: 0xeb0c013f cmp x9, x12 + 0x81, 0x00, 0x00, 0x54, // 20: 0x54000081 b.ne +34 + 0x5f, 0x00, 0x00, 0xb9, // 24: 0xb900005f str wzr, [x2] + 0xe0, 0x03, 0x00, 0x32, // 28: 0x320003e0 orr w0, wzr, #0x1 + 0xc0, 0x03, 0x5f, 0xd6, // 32: 0xd65f03c0 ret + 0x89, 0x01, 0x09, 0xca, // 36: 0xca090189 eor x9, x12, x9 + + // [...] + + 0x08, 0x05, 0x00, 0x11, // 40: 0x11000508 add w8, w8, #0x1 + 0x48, 0x00, 0x00, 0xb9, // 44: 0xb9000048 str w8, [x2] + 0xe0, 0x03, 0x00, 0x32, // 48: 0x320003e0 orr w0, wzr, #0x1 + 0xc0, 0x03, 0x5f, 0xd6, // 52: 0xd65f03c0 ret + 0x00, 0x00, 0x80, 0x52, // 56: 0x52800000 mov w0, #0x0 + 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret + + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // (possibly with additional rows at offsets 36 and 56 saying the same thing) + + 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_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(32); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x23_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x24_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x25_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x26_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); + EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); + + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(52); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(56); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(60); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); +} + +TEST_F(TestArm64InstEmulation, TestRegisterSavedTwice) { + ArchSpec arch("arm64-apple-ios10"); + 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; + + // disassembly of mach_msg_sever_once from libsystem_kernel.dylib for iOS. + uint8_t data[] = { + + 0xfc, 0x6f, 0xba, 0xa9, // 0: 0xa9ba6ffc stp x28, x27, [sp, #-0x60]! + 0xfa, 0x67, 0x01, 0xa9, // 4: 0xa90167fa stp x26, x25, [sp, #0x10] + 0xf8, 0x5f, 0x02, 0xa9, // 8: 0xa9025ff8 stp x24, x23, [sp, #0x20] + 0xf6, 0x57, 0x03, 0xa9, // 12: 0xa90357f6 stp x22, x21, [sp, #0x30] + 0xf4, 0x4f, 0x04, 0xa9, // 16: 0xa9044ff4 stp x20, x19, [sp, #0x40] + 0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50] + 0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50 + 0xff, 0xc3, 0x00, 0xd1, // 28: 0xd100c3ff sub sp, sp, #0x30 + + // mid-function, store x20 & x24 on the stack at a different location. + // this should not show up in the unwind plan; caller's values are not + // being saved to stack. + 0xf8, 0x53, 0x01, 0xa9, // 32: 0xa90153f8 stp x24, x20, [sp, #0x10] + + // mid-function, copy x20 and x19 off of the stack -- but not from + // their original locations. unwind plan should ignore this. + 0xf4, 0x4f, 0x41, 0xa9, // 36: 0xa9414ff4 ldp x20, x19, [sp, #0x10] + + // epilogue + 0xbf, 0x43, 0x01, 0xd1, // 40: 0xd10143bf sub sp, x29, #0x50 + 0xfd, 0x7b, 0x45, 0xa9, // 44: 0xa9457bfd ldp x29, x30, [sp, #0x50] + 0xf4, 0x4f, 0x44, 0xa9, // 48: 0xa9444ff4 ldp x20, x19, [sp, #0x40] + 0xf6, 0x57, 0x43, 0xa9, // 52: 0xa94357f6 ldp x22, x21, [sp, #0x30] + 0xf8, 0x5f, 0x42, 0xa9, // 56: 0xa9425ff8 ldp x24, x23, [sp, #0x20] + 0xfa, 0x67, 0x41, 0xa9, // 60: 0xa94167fa ldp x26, x25, [sp, #0x10] + 0xfc, 0x6f, 0xc6, 0xa8, // 64: 0xa8c66ffc ldp x28, x27, [sp], #0x60 + 0xc0, 0x03, 0x5f, 0xd6, // 68: 0xd65f03c0 ret + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // 4: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96] + // 8: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 12: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] + // x27=[CFA-88] x28=[CFA-96] + // 16: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64] + // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 20: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] + // 24: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] + // 28: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] + + // 44: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] + // 48: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] + // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] + // x28=[CFA-96] + // 52: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64] + // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 56: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] + // x27=[CFA-88] x28=[CFA-96] + // 60: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] + // 64: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96] + // 68: CFA=sp +0 => + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + row_sp = unwind_plan.GetRowForFunctionOffset(36); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-32, regloc.GetOffset()); + + row_sp = unwind_plan.GetRowForFunctionOffset(40); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-32, regloc.GetOffset()); +} + +TEST_F(TestArm64InstEmulation, TestRegisterDoubleSpills) { + ArchSpec arch("arm64-apple-ios10"); + 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; + + // this file built with clang for iOS arch arm64 optimization -Os + // #include + // double foo(double in) { + // double arr[32]; + // for (int i = 0; i < 32; i++) + // arr[i] = in + i; + // for (int i = 2; i < 30; i++) + // arr[i] = ((((arr[i - 1] * arr[i - 2] * 0.2) + (0.7 * arr[i])) / + // ((((arr[i] * 0.73) + 0.65) * (arr[i - 1] + 0.2)) - ((arr[i + 1] + (arr[i] + // * 0.32) + 0.52) / 0.3) + (0.531 * arr[i - 2]))) + ((arr[i - 1] + 5) / + // ((arr[i + 2] + 0.4) / arr[i])) + (arr[5] * (0.17 + arr[7] * arr[i])) + + // ((i > 5 ? (arr[i - 3]) : arr[i - 1]) * 0.263) + (((arr[i - 2] + arr[i - + // 1]) * 0.3252) + 3.56) - (arr[i + 1] * 0.852311)) * ((arr[i] * 85234.1345) + // + (77342.451324 / (arr[i - 2] + arr[i - 1] - 73425341.33455))) + (arr[i] + // * 875712013.55) - (arr[i - 1] * 0.5555) - ((arr[i] * (arr[i + 1] + + // 17342834.44) / 8688200123.555)) + (arr[i - 2] + 8888.888); + // return arr[16]; + //} + // int main(int argc, char **argv) { printf("%g\n", foo(argc)); } + + // so function foo() uses enough registers that it spills the callee-saved + // floating point registers. + uint8_t data[] = { + // prologue + 0xef, 0x3b, 0xba, 0x6d, // 0: 0x6dba3bef stp d15, d14, [sp, #-0x60]! + 0xed, 0x33, 0x01, 0x6d, // 4: 0x6d0133ed stp d13, d12, [sp, #0x10] + 0xeb, 0x2b, 0x02, 0x6d, // 8: 0x6d022beb stp d11, d10, [sp, #0x20] + 0xe9, 0x23, 0x03, 0x6d, // 12: 0x6d0323e9 stp d9, d8, [sp, #0x30] + 0xfc, 0x6f, 0x04, 0xa9, // 16: 0xa9046ffc stp x28, x27, [sp, #0x40] + 0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50] + 0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50 + 0xff, 0x43, 0x04, 0xd1, // 28: 0xd10443ff sub sp, sp, #0x110 + + // epilogue + 0xbf, 0x43, 0x01, 0xd1, // 32: 0xd10143bf sub sp, x29, #0x50 + 0xfd, 0x7b, 0x45, 0xa9, // 36: 0xa9457bfd ldp x29, x30, [sp, #0x50] + 0xfc, 0x6f, 0x44, 0xa9, // 40: 0xa9446ffc ldp x28, x27, [sp, #0x40] + 0xe9, 0x23, 0x43, 0x6d, // 44: 0x6d4323e9 ldp d9, d8, [sp, #0x30] + 0xeb, 0x2b, 0x42, 0x6d, // 48: 0x6d422beb ldp d11, d10, [sp, #0x20] + 0xed, 0x33, 0x41, 0x6d, // 52: 0x6d4133ed ldp d13, d12, [sp, #0x10] + 0xef, 0x3b, 0xc6, 0x6c, // 56: 0x6cc63bef ldp d15, d14, [sp], #0x60 + 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret + }; + + // UnwindPlan we expect: + // 0: CFA=sp +0 => + // 4: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96] + // 8: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 12: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] + // d14=[CFA-88] d15=[CFA-96] + // 16: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] + // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 20: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48] + // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] + // d15=[CFA-96] + // 24: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 36: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 40: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48] + // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] + // d15=[CFA-96] + // 44: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] + // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 48: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] + // d14=[CFA-88] d15=[CFA-96] + // 52: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + // 56: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96] + // 60: CFA=sp +0 => + + sample_range = AddressRange(0x1000, sizeof(data)); + + EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( + sample_range, data, sizeof(data), unwind_plan)); + + // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] + // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] + // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] + row_sp = unwind_plan.GetRowForFunctionOffset(28); + EXPECT_EQ(28ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-96, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-88, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-80, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-72, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-64, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-56, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-48, regloc.GetOffset()); + + EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)); + EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); + EXPECT_EQ(-40, regloc.GetOffset()); + + // 60: CFA=sp +0 => + row_sp = unwind_plan.GetRowForFunctionOffset(60); + EXPECT_EQ(60ull, row_sp->GetOffset()); + EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); + EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); + EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); + + if (row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); + if (row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)) + EXPECT_TRUE(regloc.IsSame()); +} Index: lldb/trunk/unittests/UnwindAssembly/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/UnwindAssembly/CMakeLists.txt +++ lldb/trunk/unittests/UnwindAssembly/CMakeLists.txt @@ -1,5 +1,11 @@ +if ("AArch64" IN_LIST LLVM_TARGETS_TO_BUILD) + add_subdirectory(ARM64) +endif() + +if ("PowerPC" IN_LIST LLVM_TARGETS_TO_BUILD) + add_subdirectory(PPC64) +endif() + if ("X86" IN_LIST LLVM_TARGETS_TO_BUILD) add_subdirectory(x86) endif() - -add_subdirectory(InstEmulation) Index: lldb/trunk/unittests/UnwindAssembly/InstEmulation/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/UnwindAssembly/InstEmulation/CMakeLists.txt +++ lldb/trunk/unittests/UnwindAssembly/InstEmulation/CMakeLists.txt @@ -1,15 +0,0 @@ -if ("AArch64" IN_LIST LLVM_TARGETS_TO_BUILD) - add_lldb_unittest(InstEmulationTests - TestArm64InstEmulation.cpp - LINK_LIBS - lldbCore - lldbSymbol - lldbTarget - lldbPluginUnwindAssemblyInstEmulation - lldbPluginDisassemblerLLVM - lldbPluginInstructionARM64 - lldbPluginProcessUtility - LINK_COMPONENTS - Support - ${LLVM_TARGETS_TO_BUILD}) -endif() Index: lldb/trunk/unittests/UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp =================================================================== --- lldb/trunk/unittests/UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp +++ lldb/trunk/unittests/UnwindAssembly/InstEmulation/TestArm64InstEmulation.cpp @@ -1,672 +0,0 @@ -//===-- TestArm64InstEmulation.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/ARM64/EmulateInstructionARM64.h" -#include "Plugins/Process/Utility/lldb-arm64-register-enums.h" -#include "llvm/Support/TargetSelect.h" - -using namespace lldb; -using namespace lldb_private; - -class TestArm64InstEmulation : public testing::Test { -public: - static void SetUpTestCase(); - static void TearDownTestCase(); - - // virtual void SetUp() override { } - // virtual void TearDown() override { } - -protected: -}; - -void TestArm64InstEmulation::SetUpTestCase() { - llvm::InitializeAllTargets(); - llvm::InitializeAllAsmPrinters(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllDisassemblers(); - DisassemblerLLVMC::Initialize(); - EmulateInstructionARM64::Initialize(); -} - -void TestArm64InstEmulation::TearDownTestCase() { - DisassemblerLLVMC::Terminate(); - EmulateInstructionARM64::Terminate(); -} - -TEST_F(TestArm64InstEmulation, TestSimpleDarwinFunction) { - ArchSpec arch("arm64-apple-ios10"); - 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; - - // 'int main() { }' compiled for arm64-apple-ios with clang - uint8_t data[] = { - 0xfd, 0x7b, 0xbf, 0xa9, // 0xa9bf7bfd : stp x29, x30, [sp, #-0x10]! - 0xfd, 0x03, 0x00, 0x91, // 0x910003fd : mov x29, sp - 0xff, 0x43, 0x00, 0xd1, // 0xd10043ff : sub sp, sp, #0x10 - - 0xbf, 0x03, 0x00, 0x91, // 0x910003bf : mov sp, x29 - 0xfd, 0x7b, 0xc1, 0xa8, // 0xa8c17bfd : ldp x29, x30, [sp], #16 - 0xc0, 0x03, 0x5f, 0xd6, // 0xd65f03c0 : ret - }; - - // UnwindPlan we expect: - - // row[0]: 0: CFA=sp +0 => - // row[1]: 4: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] - // row[2]: 8: CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] - // row[2]: 16: CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] - // row[3]: 20: CFA=sp +0 => fp= lr= - - sample_range = AddressRange(0x1000, sizeof(data)); - - EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( - sample_range, data, sizeof(data), unwind_plan)); - - // CFA=sp +0 - row_sp = unwind_plan.GetRowForFunctionOffset(0); - EXPECT_EQ(0ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] - row_sp = unwind_plan.GetRowForFunctionOffset(4); - EXPECT_EQ(4ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-16, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-8, regloc.GetOffset()); - - // CFA=fp+16 => fp=[CFA-16] lr=[CFA-8] - row_sp = unwind_plan.GetRowForFunctionOffset(8); - EXPECT_EQ(8ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-16, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-8, regloc.GetOffset()); - - // CFA=sp+16 => fp=[CFA-16] lr=[CFA-8] - row_sp = unwind_plan.GetRowForFunctionOffset(16); - EXPECT_EQ(16ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-16, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-8, regloc.GetOffset()); - - // CFA=sp +0 => fp= lr= - row_sp = unwind_plan.GetRowForFunctionOffset(20); - EXPECT_EQ(20ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); -} - -TEST_F(TestArm64InstEmulation, TestMediumDarwinFunction) { - ArchSpec arch("arm64-apple-ios10"); - 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; - - // disassembly of -[NSPlaceholderString initWithBytes:length:encoding:] - // from Foundation for iOS. - uint8_t data[] = { - 0xf6, 0x57, 0xbd, 0xa9, // 0: 0xa9bd57f6 stp x22, x21, [sp, #-48]! - 0xf4, 0x4f, 0x01, 0xa9, // 4: 0xa9014ff4 stp x20, x19, [sp, #16] - 0xfd, 0x7b, 0x02, 0xa9, // 8: 0xa9027bfd stp x29, x30, [sp, #32] - 0xfd, 0x83, 0x00, 0x91, // 12: 0x910083fd add x29, sp, #32 - 0xff, 0x43, 0x00, 0xd1, // 16: 0xd10043ff sub sp, sp, #16 - - // [... function body ...] - 0x1f, 0x20, 0x03, 0xd5, // 20: 0xd503201f nop - - 0xbf, 0x83, 0x00, 0xd1, // 24: 0xd10083bf sub sp, x29, #32 - 0xfd, 0x7b, 0x42, 0xa9, // 28: 0xa9427bfd ldp x29, x30, [sp, #32] - 0xf4, 0x4f, 0x41, 0xa9, // 32: 0xa9414ff4 ldp x20, x19, [sp, #16] - 0xf6, 0x57, 0xc3, 0xa8, // 36: 0xa8c357f6 ldp x22, x21, [sp], #48 - 0x01, 0x16, 0x09, 0x14, // 40: 0x14091601 b 0x18f640524 ; symbol stub - // for: CFStringCreateWithBytes - }; - - // UnwindPlan we expect: - // 0: CFA=sp +0 => - // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48] - // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // fp=[CFA-16] lr=[CFA-8] - // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // fp=[CFA-16] lr=[CFA-8] - - // [... function body ...] - - // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // fp=[CFA-16] lr=[CFA-8] - // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp= - // lr= - // 36: CFA=sp+48 => x19= x20= x21=[CFA-40] x22=[CFA-48] fp= - // lr= - // 40: CFA=sp +0 => x19= x20= x21= x22= fp= - // lr= - - 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_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - // 4: CFA=sp+48 => x21=[CFA-40] x22=[CFA-48] - row_sp = unwind_plan.GetRowForFunctionOffset(4); - EXPECT_EQ(4ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-40, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-48, regloc.GetOffset()); - - // 8: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - row_sp = unwind_plan.GetRowForFunctionOffset(8); - EXPECT_EQ(8ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-24, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-32, regloc.GetOffset()); - - // 12: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // fp=[CFA-16] lr=[CFA-8] - row_sp = unwind_plan.GetRowForFunctionOffset(12); - EXPECT_EQ(12ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-16, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-8, regloc.GetOffset()); - - // 16: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // fp=[CFA-16] lr=[CFA-8] - row_sp = unwind_plan.GetRowForFunctionOffset(16); - EXPECT_EQ(16ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); - - // 28: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // fp=[CFA-16] lr=[CFA-8] - row_sp = unwind_plan.GetRowForFunctionOffset(28); - EXPECT_EQ(28ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(48, row_sp->GetCFAValue().GetOffset()); - - // 32: CFA=sp+48 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] fp= - // lr= - row_sp = unwind_plan.GetRowForFunctionOffset(32); - EXPECT_EQ(32ull, row_sp->GetOffset()); - - // I'd prefer if these restored registers were cleared entirely instead of set - // to IsSame... - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); - EXPECT_TRUE(regloc.IsSame()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); - EXPECT_TRUE(regloc.IsSame()); - - // 36: CFA=sp+48 => x19= x20= x21=[CFA-40] x22=[CFA-48] fp= - // lr= - row_sp = unwind_plan.GetRowForFunctionOffset(36); - EXPECT_EQ(36ull, row_sp->GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); - EXPECT_TRUE(regloc.IsSame()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); - EXPECT_TRUE(regloc.IsSame()); - - // 40: CFA=sp +0 => x19= x20= x21= x22= fp= - // lr= - row_sp = unwind_plan.GetRowForFunctionOffset(40); - EXPECT_EQ(40ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); - EXPECT_TRUE(regloc.IsSame()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); - EXPECT_TRUE(regloc.IsSame()); -} - -TEST_F(TestArm64InstEmulation, TestFramelessThreeEpilogueFunction) { - ArchSpec arch("arm64-apple-ios10"); - 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; - - // disassembly of JSC::ARM64LogicalImmediate::findBitRange<16u> - // from JavaScriptcore for iOS. - uint8_t data[] = { - 0x08, 0x3c, 0x0f, 0x53, // 0: 0x530f3c08 ubfx w8, w0, #15, #1 - 0x68, 0x00, 0x00, 0x39, // 4: 0x39000068 strb w8, [x3] - 0x08, 0x3c, 0x40, 0xd2, // 8: 0xd2403c08 eor x8, x0, #0xffff - 0x1f, 0x00, 0x71, 0xf2, // 12: 0xf271001f tst x0, #0x8000 - - // [...] - - 0x3f, 0x01, 0x0c, 0xeb, // 16: 0xeb0c013f cmp x9, x12 - 0x81, 0x00, 0x00, 0x54, // 20: 0x54000081 b.ne +34 - 0x5f, 0x00, 0x00, 0xb9, // 24: 0xb900005f str wzr, [x2] - 0xe0, 0x03, 0x00, 0x32, // 28: 0x320003e0 orr w0, wzr, #0x1 - 0xc0, 0x03, 0x5f, 0xd6, // 32: 0xd65f03c0 ret - 0x89, 0x01, 0x09, 0xca, // 36: 0xca090189 eor x9, x12, x9 - - // [...] - - 0x08, 0x05, 0x00, 0x11, // 40: 0x11000508 add w8, w8, #0x1 - 0x48, 0x00, 0x00, 0xb9, // 44: 0xb9000048 str w8, [x2] - 0xe0, 0x03, 0x00, 0x32, // 48: 0x320003e0 orr w0, wzr, #0x1 - 0xc0, 0x03, 0x5f, 0xd6, // 52: 0xd65f03c0 ret - 0x00, 0x00, 0x80, 0x52, // 56: 0x52800000 mov w0, #0x0 - 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret - - }; - - // UnwindPlan we expect: - // 0: CFA=sp +0 => - // (possibly with additional rows at offsets 36 and 56 saying the same thing) - - 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_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - row_sp = unwind_plan.GetRowForFunctionOffset(32); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x19_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x21_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x22_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x23_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x24_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x25_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x26_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_fp_arm64, regloc)); - EXPECT_FALSE(row_sp->GetRegisterInfo(gpr_lr_arm64, regloc)); - - row_sp = unwind_plan.GetRowForFunctionOffset(36); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - row_sp = unwind_plan.GetRowForFunctionOffset(52); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - row_sp = unwind_plan.GetRowForFunctionOffset(56); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - row_sp = unwind_plan.GetRowForFunctionOffset(60); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); -} - -TEST_F(TestArm64InstEmulation, TestRegisterSavedTwice) { - ArchSpec arch("arm64-apple-ios10"); - 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; - - // disassembly of mach_msg_sever_once from libsystem_kernel.dylib for iOS. - uint8_t data[] = { - - 0xfc, 0x6f, 0xba, 0xa9, // 0: 0xa9ba6ffc stp x28, x27, [sp, #-0x60]! - 0xfa, 0x67, 0x01, 0xa9, // 4: 0xa90167fa stp x26, x25, [sp, #0x10] - 0xf8, 0x5f, 0x02, 0xa9, // 8: 0xa9025ff8 stp x24, x23, [sp, #0x20] - 0xf6, 0x57, 0x03, 0xa9, // 12: 0xa90357f6 stp x22, x21, [sp, #0x30] - 0xf4, 0x4f, 0x04, 0xa9, // 16: 0xa9044ff4 stp x20, x19, [sp, #0x40] - 0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50] - 0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50 - 0xff, 0xc3, 0x00, 0xd1, // 28: 0xd100c3ff sub sp, sp, #0x30 - - // mid-function, store x20 & x24 on the stack at a different location. - // this should not show up in the unwind plan; caller's values are not - // being saved to stack. - 0xf8, 0x53, 0x01, 0xa9, // 32: 0xa90153f8 stp x24, x20, [sp, #0x10] - - // mid-function, copy x20 and x19 off of the stack -- but not from - // their original locations. unwind plan should ignore this. - 0xf4, 0x4f, 0x41, 0xa9, // 36: 0xa9414ff4 ldp x20, x19, [sp, #0x10] - - // epilogue - 0xbf, 0x43, 0x01, 0xd1, // 40: 0xd10143bf sub sp, x29, #0x50 - 0xfd, 0x7b, 0x45, 0xa9, // 44: 0xa9457bfd ldp x29, x30, [sp, #0x50] - 0xf4, 0x4f, 0x44, 0xa9, // 48: 0xa9444ff4 ldp x20, x19, [sp, #0x40] - 0xf6, 0x57, 0x43, 0xa9, // 52: 0xa94357f6 ldp x22, x21, [sp, #0x30] - 0xf8, 0x5f, 0x42, 0xa9, // 56: 0xa9425ff8 ldp x24, x23, [sp, #0x20] - 0xfa, 0x67, 0x41, 0xa9, // 60: 0xa94167fa ldp x26, x25, [sp, #0x10] - 0xfc, 0x6f, 0xc6, 0xa8, // 64: 0xa8c66ffc ldp x28, x27, [sp], #0x60 - 0xc0, 0x03, 0x5f, 0xd6, // 68: 0xd65f03c0 ret - }; - - // UnwindPlan we expect: - // 0: CFA=sp +0 => - // 4: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96] - // 8: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] - // 12: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] - // x27=[CFA-88] x28=[CFA-96] - // 16: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64] - // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] - // 20: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] - // x28=[CFA-96] - // 24: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] - // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] - // 28: CFA=fp+16 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] - // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] - - // 44: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] - // x28=[CFA-96] fp=[CFA-16] lr=[CFA-8] - // 48: CFA=sp+96 => x19=[CFA-24] x20=[CFA-32] x21=[CFA-40] x22=[CFA-48] - // x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] - // x28=[CFA-96] - // 52: CFA=sp+96 => x21=[CFA-40] x22=[CFA-48] x23=[CFA-56] x24=[CFA-64] - // x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] - // 56: CFA=sp+96 => x23=[CFA-56] x24=[CFA-64] x25=[CFA-72] x26=[CFA-80] - // x27=[CFA-88] x28=[CFA-96] - // 60: CFA=sp+96 => x25=[CFA-72] x26=[CFA-80] x27=[CFA-88] x28=[CFA-96] - // 64: CFA=sp+96 => x27=[CFA-88] x28=[CFA-96] - // 68: CFA=sp +0 => - - sample_range = AddressRange(0x1000, sizeof(data)); - - EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( - sample_range, data, sizeof(data), unwind_plan)); - - row_sp = unwind_plan.GetRowForFunctionOffset(36); - EXPECT_EQ(28ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-32, regloc.GetOffset()); - - row_sp = unwind_plan.GetRowForFunctionOffset(40); - EXPECT_EQ(28ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(gpr_x20_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-32, regloc.GetOffset()); -} - -TEST_F(TestArm64InstEmulation, TestRegisterDoubleSpills) { - ArchSpec arch("arm64-apple-ios10"); - 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; - - // this file built with clang for iOS arch arm64 optimization -Os - // #include - // double foo(double in) { - // double arr[32]; - // for (int i = 0; i < 32; i++) - // arr[i] = in + i; - // for (int i = 2; i < 30; i++) - // arr[i] = ((((arr[i - 1] * arr[i - 2] * 0.2) + (0.7 * arr[i])) / - // ((((arr[i] * 0.73) + 0.65) * (arr[i - 1] + 0.2)) - ((arr[i + 1] + (arr[i] - // * 0.32) + 0.52) / 0.3) + (0.531 * arr[i - 2]))) + ((arr[i - 1] + 5) / - // ((arr[i + 2] + 0.4) / arr[i])) + (arr[5] * (0.17 + arr[7] * arr[i])) + - // ((i > 5 ? (arr[i - 3]) : arr[i - 1]) * 0.263) + (((arr[i - 2] + arr[i - - // 1]) * 0.3252) + 3.56) - (arr[i + 1] * 0.852311)) * ((arr[i] * 85234.1345) - // + (77342.451324 / (arr[i - 2] + arr[i - 1] - 73425341.33455))) + (arr[i] - // * 875712013.55) - (arr[i - 1] * 0.5555) - ((arr[i] * (arr[i + 1] + - // 17342834.44) / 8688200123.555)) + (arr[i - 2] + 8888.888); - // return arr[16]; - //} - // int main(int argc, char **argv) { printf("%g\n", foo(argc)); } - - // so function foo() uses enough registers that it spills the callee-saved - // floating point registers. - uint8_t data[] = { - // prologue - 0xef, 0x3b, 0xba, 0x6d, // 0: 0x6dba3bef stp d15, d14, [sp, #-0x60]! - 0xed, 0x33, 0x01, 0x6d, // 4: 0x6d0133ed stp d13, d12, [sp, #0x10] - 0xeb, 0x2b, 0x02, 0x6d, // 8: 0x6d022beb stp d11, d10, [sp, #0x20] - 0xe9, 0x23, 0x03, 0x6d, // 12: 0x6d0323e9 stp d9, d8, [sp, #0x30] - 0xfc, 0x6f, 0x04, 0xa9, // 16: 0xa9046ffc stp x28, x27, [sp, #0x40] - 0xfd, 0x7b, 0x05, 0xa9, // 20: 0xa9057bfd stp x29, x30, [sp, #0x50] - 0xfd, 0x43, 0x01, 0x91, // 24: 0x910143fd add x29, sp, #0x50 - 0xff, 0x43, 0x04, 0xd1, // 28: 0xd10443ff sub sp, sp, #0x110 - - // epilogue - 0xbf, 0x43, 0x01, 0xd1, // 32: 0xd10143bf sub sp, x29, #0x50 - 0xfd, 0x7b, 0x45, 0xa9, // 36: 0xa9457bfd ldp x29, x30, [sp, #0x50] - 0xfc, 0x6f, 0x44, 0xa9, // 40: 0xa9446ffc ldp x28, x27, [sp, #0x40] - 0xe9, 0x23, 0x43, 0x6d, // 44: 0x6d4323e9 ldp d9, d8, [sp, #0x30] - 0xeb, 0x2b, 0x42, 0x6d, // 48: 0x6d422beb ldp d11, d10, [sp, #0x20] - 0xed, 0x33, 0x41, 0x6d, // 52: 0x6d4133ed ldp d13, d12, [sp, #0x10] - 0xef, 0x3b, 0xc6, 0x6c, // 56: 0x6cc63bef ldp d15, d14, [sp], #0x60 - 0xc0, 0x03, 0x5f, 0xd6, // 60: 0xd65f03c0 ret - }; - - // UnwindPlan we expect: - // 0: CFA=sp +0 => - // 4: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96] - // 8: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - // 12: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] - // d14=[CFA-88] d15=[CFA-96] - // 16: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] - // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - // 20: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48] - // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] - // d15=[CFA-96] - // 24: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] - // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] - // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] - // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] - // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - // 36: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] - // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] - // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - // 40: CFA=sp+96 => x27=[CFA-24] x28=[CFA-32] d8=[CFA-40] d9=[CFA-48] - // d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] - // d15=[CFA-96] - // 44: CFA=sp+96 => d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] - // d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - // 48: CFA=sp+96 => d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] d13=[CFA-80] - // d14=[CFA-88] d15=[CFA-96] - // 52: CFA=sp+96 => d12=[CFA-72] d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - // 56: CFA=sp+96 => d14=[CFA-88] d15=[CFA-96] - // 60: CFA=sp +0 => - - sample_range = AddressRange(0x1000, sizeof(data)); - - EXPECT_TRUE(engine->GetNonCallSiteUnwindPlanFromAssembly( - sample_range, data, sizeof(data), unwind_plan)); - - // 28: CFA=fp+16 => x27=[CFA-24] x28=[CFA-32] fp=[CFA-16] lr=[CFA-8] - // d8=[CFA-40] d9=[CFA-48] d10=[CFA-56] d11=[CFA-64] d12=[CFA-72] - // d13=[CFA-80] d14=[CFA-88] d15=[CFA-96] - row_sp = unwind_plan.GetRowForFunctionOffset(28); - EXPECT_EQ(28ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_fp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(16, row_sp->GetCFAValue().GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-96, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-88, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-80, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-72, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-64, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-56, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-48, regloc.GetOffset()); - - EXPECT_TRUE(row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)); - EXPECT_TRUE(regloc.IsAtCFAPlusOffset()); - EXPECT_EQ(-40, regloc.GetOffset()); - - // 60: CFA=sp +0 => - row_sp = unwind_plan.GetRowForFunctionOffset(60); - EXPECT_EQ(60ull, row_sp->GetOffset()); - EXPECT_TRUE(row_sp->GetCFAValue().GetRegisterNumber() == gpr_sp_arm64); - EXPECT_TRUE(row_sp->GetCFAValue().IsRegisterPlusOffset() == true); - EXPECT_EQ(0, row_sp->GetCFAValue().GetOffset()); - - if (row_sp->GetRegisterInfo(fpu_d8_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(fpu_d9_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(fpu_d10_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(fpu_d11_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(fpu_d12_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(fpu_d13_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(fpu_d14_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(fpu_d15_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(gpr_x27_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); - if (row_sp->GetRegisterInfo(gpr_x28_arm64, regloc)) - EXPECT_TRUE(regloc.IsSame()); -} Index: lldb/trunk/unittests/UnwindAssembly/PPC64/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/UnwindAssembly/PPC64/CMakeLists.txt +++ lldb/trunk/unittests/UnwindAssembly/PPC64/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_unittest(PPC64InstEmulationTests + TestPPC64InstEmulation.cpp + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + lldbPluginUnwindAssemblyInstEmulation + lldbPluginDisassemblerLLVM + lldbPluginInstructionPPC64 + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ${LLVM_TARGETS_TO_BUILD}) Index: lldb/trunk/unittests/UnwindAssembly/PPC64/TestPPC64InstEmulation.cpp =================================================================== --- lldb/trunk/unittests/UnwindAssembly/PPC64/TestPPC64InstEmulation.cpp +++ lldb/trunk/unittests/UnwindAssembly/PPC64/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()); +}