diff --git a/lldb/include/lldb/Core/Architecture.h b/lldb/include/lldb/Core/Architecture.h index b6fc1a20e1e69..ed64a895717a1 100644 --- a/lldb/include/lldb/Core/Architecture.h +++ b/lldb/include/lldb/Core/Architecture.h @@ -12,6 +12,7 @@ #include "lldb/Core/PluginInterface.h" #include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/MemoryTagManager.h" +#include "lldb/Target/RegisterContextUnwind.h" namespace lldb_private { @@ -129,6 +130,14 @@ class Architecture : public PluginInterface { RegisterContext ®_context) const { return false; } + + /// Return an UnwindPlan that allows architecture-defined rules for finding + /// saved registers, given a particular set of register values. + virtual lldb::UnwindPlanSP GetArchitectureUnwindPlan( + lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx, + std::shared_ptr current_unwindplan) { + return lldb::UnwindPlanSP(); + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h index b10a364823b83..52c28fd76da98 100644 --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -21,6 +21,7 @@ namespace lldb_private { class UnwindLLDB; +class ArchitectureArm; class RegisterContextUnwind : public lldb_private::RegisterContext { public: @@ -72,6 +73,25 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { // above asynchronous trap handlers (sigtramp) for instance. bool BehavesLikeZerothFrame() const override; +protected: + // Provide a location for where THIS function saved the CALLER's register + // value, or a frame "below" this one saved it. That is, this function doesn't + // modify the register, it may call a function that does & saved it to stack. + // + // The ConcreteRegisterLocation type may be set to eRegisterNotAvailable -- + // this will happen for a volatile register being queried mid-stack. Instead + // of floating frame 0's contents of that register up the stack (which may or + // may not be the value of that reg when the function was executing), we won't + // return any value. + // + // If a non-volatile register (a "preserved" register, a callee-preserved + // register) is requested mid-stack, and no frames "below" the requested stack + // have saved the register anywhere, it is safe to assume that frame 0's + // register value is the same. + lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister( + uint32_t lldb_regnum, + lldb_private::UnwindLLDB::ConcreteRegisterLocation ®loc); + private: enum FrameType { eNormalFrame, @@ -86,6 +106,8 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { // UnwindLLDB needs to pass around references to ConcreteRegisterLocations friend class UnwindLLDB; + // Architecture may need to retrieve caller register values from this frame + friend class ArchitectureArm; // Returns true if we have an unwind loop -- the same stack frame unwinding // multiple times. @@ -130,27 +152,6 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { void PropagateTrapHandlerFlagFromUnwindPlan( std::shared_ptr unwind_plan); - // Provide a location for where THIS function saved the CALLER's register - // value - // Or a frame "below" this one saved it, i.e. a function called by this one, - // preserved a register that this - // function didn't modify/use. - // - // The ConcreteRegisterLocation type may be set to eRegisterNotAvailable -- - // this will happen for a volatile register being queried mid-stack. Instead - // of floating frame 0's contents of that register up the stack (which may or - // may not be the value of that reg when the function was executing), we won't - // return any value. - // - // If a non-volatile register (a "preserved" register) is requested mid-stack - // and no frames "below" the requested - // stack have saved the register anywhere, it is safe to assume that frame 0's - // register values are still the same - // as the requesting frame's. - lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister( - uint32_t lldb_regnum, - lldb_private::UnwindLLDB::ConcreteRegisterLocation ®loc); - std::optional GetAbstractRegisterLocation(uint32_t lldb_regnum, lldb::RegisterKind &kind); @@ -202,6 +203,8 @@ class RegisterContextUnwind : public lldb_private::RegisterContext { std::shared_ptr GetFullUnwindPlanForFrame(); + lldb::UnwindPlanSP TryAdoptArchitectureUnwindPlan(); + void UnwindLogMsg(const char *fmt, ...) __attribute__((format(printf, 2, 3))); void UnwindLogMsgVerbose(const char *fmt, ...) diff --git a/lldb/include/lldb/Target/UnwindLLDB.h b/lldb/include/lldb/Target/UnwindLLDB.h index 88180b37fd93a..29b3ab9c90298 100644 --- a/lldb/include/lldb/Target/UnwindLLDB.h +++ b/lldb/include/lldb/Target/UnwindLLDB.h @@ -22,6 +22,7 @@ namespace lldb_private { class RegisterContextUnwind; +class ArchitectureArm; class UnwindLLDB : public lldb_private::Unwind { public: @@ -37,6 +38,7 @@ class UnwindLLDB : public lldb_private::Unwind { protected: friend class lldb_private::RegisterContextUnwind; + friend class lldb_private::ArchitectureArm; /// An UnwindPlan::Row::AbstractRegisterLocation, combined with the register /// context and memory for a specific stop point, is used to create a diff --git a/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp b/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp index 2bcb2c0de97ac..bb0c4ba3f1b57 100644 --- a/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp +++ b/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp @@ -1921,6 +1921,13 @@ UnwindPlanSP ABISysV_arm::CreateFunctionEntryUnwindPlan() { UnwindPlanSP ABISysV_arm::CreateDefaultUnwindPlan() { // TODO: Handle thumb + // If we had a Target argument, could at least check + // target.GetArchitecture().GetTriple().isArmMClass() + // which is always thumb. + // To handle thumb properly, we'd need to fetch the current + // CPSR state at unwind time to tell if the processor is + // in thumb mode in this stack frame. There's no way to + // express something like that in an UnwindPlan today. uint32_t fp_reg_num = dwarf_r11; uint32_t pc_reg_num = dwarf_pc; diff --git a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp index 81c72122cb7e5..721c4bcfe6342 100644 --- a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp +++ b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp @@ -9,10 +9,18 @@ #include "Plugins/Architecture/Arm/ArchitectureArm.h" #include "Plugins/Process/Utility/ARMDefines.h" #include "Plugins/Process/Utility/InstructionUtils.h" +#include "Utility/ARM_DWARF_Registers.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" +#include "lldb/Target/RegisterNumber.h" #include "lldb/Target/Thread.h" +#include "lldb/Target/UnwindLLDB.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" using namespace lldb_private; using namespace lldb; @@ -150,3 +158,181 @@ addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr, } return opcode_addr & ~(1ull); } + +// The ARM M-Profile Armv7-M Architecture Reference Manual, +// subsection "B1.5 Armv7-M exception model", see the parts +// describing "Exception entry behavior" and "Exception +// return behavior". +// When an exception happens on this processor, certain registers are +// saved below the stack pointer, the stack pointer is decremented, +// a special value is put in the link register to indicate the +// exception has been taken, and an exception handler function +// is invoked. +// +// Detect that special value in $lr, and if present, add +// unwind rules for the registers that were saved above this +// stack frame's CFA. Overwrite any register locations that +// the current_unwindplan has for these registers; they are +// not correct when we're invoked this way. +UnwindPlanSP ArchitectureArm::GetArchitectureUnwindPlan( + Thread &thread, RegisterContextUnwind *regctx, + std::shared_ptr current_unwindplan) { + + ProcessSP process_sp = thread.GetProcess(); + if (!process_sp) + return {}; + + const ArchSpec arch = process_sp->GetTarget().GetArchitecture(); + if (!arch.GetTriple().isArmMClass() || arch.GetAddressByteSize() != 4) + return {}; + + // Get the caller's LR value from regctx (the LR value + // at function entry to this function). + RegisterNumber ra_regnum(thread, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_RA); + uint32_t ra_regnum_lldb = ra_regnum.GetAsKind(eRegisterKindLLDB); + + if (ra_regnum_lldb == LLDB_INVALID_REGNUM) + return {}; + + UnwindLLDB::ConcreteRegisterLocation regloc = {}; + bool got_concrete_location = false; + if (regctx->SavedLocationForRegister(ra_regnum_lldb, regloc) == + UnwindLLDB::RegisterSearchResult::eRegisterFound) { + got_concrete_location = true; + } else { + RegisterNumber pc_regnum(thread, eRegisterKindGeneric, + LLDB_REGNUM_GENERIC_PC); + uint32_t pc_regnum_lldb = pc_regnum.GetAsKind(eRegisterKindLLDB); + if (regctx->SavedLocationForRegister(pc_regnum_lldb, regloc) == + UnwindLLDB::RegisterSearchResult::eRegisterFound) + got_concrete_location = true; + } + + if (!got_concrete_location) + return {}; + + addr_t callers_return_address = LLDB_INVALID_ADDRESS; + const RegisterInfo *reg_info = regctx->GetRegisterInfoAtIndex(ra_regnum_lldb); + if (reg_info) { + RegisterValue reg_value; + if (regctx->ReadRegisterValueFromRegisterLocation(regloc, reg_info, + reg_value)) { + callers_return_address = reg_value.GetAsUInt32(); + } + } + + if (callers_return_address == LLDB_INVALID_ADDRESS) + return {}; + + // ARMv7-M ARM says that the LR will be set to + // one of these values when an exception has taken + // place: + // if HaveFPExt() then + // if CurrentMode==Mode_Handler then + // LR = Ones(27):NOT(CONTROL.FPCA):'0001'; + // else + // LR = Ones(27):NOT(CONTROL.FPCA):'1':CONTROL.SPSEL:'01'; + // else + // if CurrentMode==Mode_Handler then + // LR = Ones(28):'0001'; + // else + // LR = Ones(29):CONTROL.SPSEL:'01'; + + // Top 27 bits are set for an exception return. + const uint32_t exception_return = -1U & ~0b11111U; + // Bit4 is 1 if only GPRs were saved. + const uint32_t gprs_only = 0b10000; + // Bit<1:0> are '01'. + const uint32_t lowbits = 0b01; + + if ((callers_return_address & exception_return) != exception_return) + return {}; + if ((callers_return_address & lowbits) != lowbits) + return {}; + + const bool fp_regs_saved = !(callers_return_address & gprs_only); + + const RegisterKind plan_regkind = current_unwindplan->GetRegisterKind(); + UnwindPlanSP new_plan = std::make_shared(plan_regkind); + new_plan->SetSourceName("Arm Cortex-M exception return UnwindPlan"); + new_plan->SetSourcedFromCompiler(eLazyBoolNo); + new_plan->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes); + new_plan->SetUnwindPlanForSignalTrap(eLazyBoolYes); + + int stored_regs_size = fp_regs_saved ? 0x68 : 0x20; + + uint32_t gpr_regs[] = {dwarf_r0, dwarf_r1, dwarf_r2, dwarf_r3, + dwarf_r12, dwarf_lr, dwarf_pc, dwarf_cpsr}; + const int gpr_reg_count = std::size(gpr_regs); + uint32_t fpr_regs[] = {dwarf_s0, dwarf_s1, dwarf_s2, dwarf_s3, + dwarf_s4, dwarf_s5, dwarf_s6, dwarf_s7, + dwarf_s8, dwarf_s9, dwarf_s10, dwarf_s11, + dwarf_s12, dwarf_s13, dwarf_s14, dwarf_s15}; + const int fpr_reg_count = std::size(fpr_regs); + + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); + std::vector saved_regs; + for (int i = 0; i < gpr_reg_count; i++) { + uint32_t regno = gpr_regs[i]; + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, gpr_regs[i], + plan_regkind, regno); + saved_regs.push_back(regno); + } + if (fp_regs_saved) { + for (int i = 0; i < fpr_reg_count; i++) { + uint32_t regno = fpr_regs[i]; + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, fpr_regs[i], + plan_regkind, regno); + saved_regs.push_back(regno); + } + } + + addr_t cfa; + if (!regctx->GetCFA(cfa)) + return {}; + + // The CPSR value saved to stack is actually (from Armv7-M ARM) + // "XPSR<31:10>:frameptralign:XPSR<8:0>" + // Bit 9 indicates that the stack pointer was aligned (to + // an 8-byte alignment) when the exception happened, and we must + // account for that when restoring the original stack pointer value. + Status error; + uint32_t callers_xPSR = + process_sp->ReadUnsignedIntegerFromMemory(cfa + 0x1c, 4, 0, error); + const bool align_stack = callers_xPSR & (1U << 9); + uint32_t callers_sp = cfa + stored_regs_size; + if (align_stack) + callers_sp |= 4; + + Log *log = GetLog(LLDBLog::Unwind); + LLDB_LOGF(log, + "ArchitectureArm::GetArchitectureUnwindPlan found caller return " + "addr of 0x%" PRIx64 ", for frame with CFA 0x%" PRIx64 + ", fp_regs_saved %d, stored_regs_size 0x%x, align stack %d", + callers_return_address, cfa, fp_regs_saved, stored_regs_size, + align_stack); + + uint32_t sp_regnum = dwarf_sp; + reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, dwarf_sp, + plan_regkind, sp_regnum); + + const int row_count = current_unwindplan->GetRowCount(); + for (int i = 0; i < row_count; i++) { + UnwindPlan::Row row = *current_unwindplan->GetRowAtIndex(i); + uint32_t offset = 0; + const size_t saved_reg_count = saved_regs.size(); + for (size_t j = 0; j < saved_reg_count; j++) { + // The locations could be set with + // SetRegisterLocationToIsConstant(regno, cfa+offset) + // expressing it in terms of CFA addr+offset - this UnwindPlan + // is only used once, with this specific CFA. I'm not sure + // which will be clearer for someone reading the unwind log. + row.SetRegisterLocationToAtCFAPlusOffset(saved_regs[j], offset, true); + offset += 4; + } + row.SetRegisterLocationToIsCFAPlusOffset(sp_regnum, callers_sp - cfa, true); + new_plan->AppendRow(row); + } + return new_plan; +} diff --git a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h index f579d6b625051..52277dc5dbae0 100644 --- a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h +++ b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h @@ -10,6 +10,7 @@ #define LLDB_SOURCE_PLUGINS_ARCHITECTURE_ARM_ARCHITECTUREARM_H #include "lldb/Core/Architecture.h" +#include "lldb/Target/Thread.h" namespace lldb_private { @@ -29,6 +30,10 @@ class ArchitectureArm : public Architecture { lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr, AddressClass addr_class) const override; + lldb::UnwindPlanSP GetArchitectureUnwindPlan( + lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx, + std::shared_ptr current_unwindplan) override; + private: static std::unique_ptr Create(const ArchSpec &arch); ArchitectureArm() = default; diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp index cb8ba05d461d4..69885aa53ca30 100644 --- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp @@ -12,6 +12,7 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Target.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "llvm/ADT/DenseSet.h" @@ -233,6 +234,40 @@ void ObjectFileJSON::CreateSections(SectionList &unified_section_list) { } } +bool ObjectFileJSON::SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) { + Log *log(GetLog(LLDBLog::DynamicLoader)); + if (!m_sections_up) + return true; + + addr_t slide = value; + if (!value_is_offset) { + addr_t lowest_addr = LLDB_INVALID_ADDRESS; + for (const SectionSP §ion_sp : *m_sections_up) { + addr_t section_load_addr = section_sp->GetFileAddress(); + lowest_addr = std::min(lowest_addr, section_load_addr); + } + if (lowest_addr == LLDB_INVALID_ADDRESS) + return false; + slide = value - lowest_addr; + } + + // Apply slide to each section's file address. + for (const SectionSP §ion_sp : *m_sections_up) { + addr_t section_load_addr = section_sp->GetFileAddress(); + if (section_load_addr != LLDB_INVALID_ADDRESS) { + LLDB_LOGF( + log, + "ObjectFileJSON::SetLoadAddress section %s to load addr 0x%" PRIx64, + section_sp->GetName().AsCString(), section_load_addr + slide); + target.SetSectionLoadAddress(section_sp, section_load_addr + slide, + /*warn_multiple=*/true); + } + } + + return true; +} + bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp, lldb::addr_t data_offset, lldb::addr_t data_length) { diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h index b72565f468862..029c8ff188934 100644 --- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h +++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h @@ -86,6 +86,9 @@ class ObjectFileJSON : public ObjectFile { Strata CalculateStrata() override { return eStrataUser; } + bool SetLoadAddress(Target &target, lldb::addr_t value, + bool value_is_offset) override; + static bool MagicBytesMatch(lldb::DataBufferSP data_sp, lldb::addr_t offset, lldb::addr_t length); diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index 6037c8d2514b8..a780b3f59aded 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -799,6 +799,23 @@ Status ProcessMachCore::DoGetMemoryRegionInfo(addr_t load_addr, region_info.SetMapped(MemoryRegionInfo::eNo); } return Status(); + } else { + // The corefile has no LC_SEGMENT at this virtual address, + // but see if there is a binary whose Section has been + // loaded at that address in the current Target. + Address addr; + if (GetTarget().ResolveLoadAddress(load_addr, addr)) { + SectionSP section_sp(addr.GetSection()); + if (section_sp) { + region_info.GetRange().SetRangeBase( + section_sp->GetLoadBaseAddress(&GetTarget())); + region_info.GetRange().SetByteSize(section_sp->GetByteSize()); + if (region_info.GetRange().Contains(load_addr)) { + region_info.SetLLDBPermissions(section_sp->GetPermissions()); + return Status(); + } + } + } } region_info.GetRange().SetRangeBase(load_addr); diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index bcf1297f2114f..787eb94be3b48 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -293,6 +293,9 @@ void RegisterContextUnwind::InitializeZerothFrame() { return; } + // Give the Architecture a chance to replace the UnwindPlan. + TryAdoptArchitectureUnwindPlan(); + UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64 " using %s UnwindPlan", (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), @@ -482,6 +485,9 @@ void RegisterContextUnwind::InitializeNonZerothFrame() { } } + // Give the Architecture a chance to replace the UnwindPlan. + TryAdoptArchitectureUnwindPlan(); + UnwindLogMsg("initialized frame cfa is 0x%" PRIx64 " afa is 0x%" PRIx64, (uint64_t)m_cfa, (uint64_t)m_afa); return; @@ -686,6 +692,9 @@ void RegisterContextUnwind::InitializeNonZerothFrame() { } } + // Give the Architecture a chance to replace the UnwindPlan. + TryAdoptArchitectureUnwindPlan(); + UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64 " afa is 0x%" PRIx64, (uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()), @@ -1717,6 +1726,41 @@ RegisterContextUnwind::SavedLocationForRegister( return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; } +UnwindPlanSP RegisterContextUnwind::TryAdoptArchitectureUnwindPlan() { + if (!m_full_unwind_plan_sp) + return {}; + ProcessSP process_sp = m_thread.GetProcess(); + if (!process_sp) + return {}; + + UnwindPlanSP arch_override_plan_sp; + if (Architecture *arch = process_sp->GetTarget().GetArchitecturePlugin()) + arch_override_plan_sp = + arch->GetArchitectureUnwindPlan(m_thread, this, m_full_unwind_plan_sp); + + if (arch_override_plan_sp) { + m_full_unwind_plan_sp = arch_override_plan_sp; + PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp); + m_registers.clear(); + if (GetLog(LLDBLog::Unwind)) { + UnwindLogMsg( + "Replacing Full Unwindplan with Architecture UnwindPlan, '%s'", + m_full_unwind_plan_sp->GetSourceName().AsCString()); + const UnwindPlan::Row *active_row = + m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset); + if (active_row) { + StreamString active_row_strm; + active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), + &m_thread, + m_start_pc.GetLoadAddress(&process_sp->GetTarget())); + UnwindLogMsg("%s", active_row_strm.GetData()); + } + } + } + + return {}; +} + // TryFallbackUnwindPlan() -- this method is a little tricky. // // When this is called, the frame above -- the caller frame, the "previous" diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/Makefile b/lldb/test/API/functionalities/unwind/cortex-m-exception/Makefile new file mode 100644 index 0000000000000..22f1051530f87 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/Makefile @@ -0,0 +1 @@ +include Makefile.rules diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py b/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py new file mode 100644 index 0000000000000..7647409e80447 --- /dev/null +++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py @@ -0,0 +1,50 @@ +""" +Test that we can backtrace up an ARM Cortex-M Exception return stack +""" + +import lldb +import json +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCortexMExceptionUnwind(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def test_no_fpu(self): + """Test that we can backtrace correctly through an ARM Cortex-M Exception return stack""" + + target = self.dbg.CreateTarget("") + exe = "binary.json" + with open(exe) as f: + exe_json = json.load(f) + exe_uuid = exe_json["uuid"] + + target.AddModule(exe, "", exe_uuid) + self.assertTrue(target.IsValid()) + + core = self.getBuildArtifact("core") + self.yaml2macho_core("armv7m-nofpu-exception.yaml", core, exe_uuid) + + process = target.LoadCore(core) + self.assertTrue(process.IsValid()) + + if self.TraceOn(): + self.runCmd("image list") + self.runCmd("target modules dump sections") + self.runCmd("target modules dump symtab") + self.runCmd("bt") + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid()) + + self.assertEqual(thread.GetNumFrames(), 6) + stackframe_names = [ + "exception_catcher", + "exception_catcher", + "exception_thrower", + "main", + ] + for i, name in enumerate(stackframe_names): + self.assertEqual(name, thread.GetFrameAtIndex(i).GetSymbol().GetName()) diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml b/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml new file mode 100644 index 0000000000000..9ce5ff49d9b6e --- /dev/null +++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml @@ -0,0 +1,64 @@ +cpu: armv7m +threads: + - regsets: + - flavor: gpr + registers: [{name: sp, value: 0x2000fe70}, {name: r7, value: 0x2000fe80}, + {name: pc, value: 0x0020392c}, {name: lr, value: 0x0020392d}] +memory-regions: + # stack memory fetched via + # (lldb) p/x $sp + # (lldb) x/128wx $sp + # % pbpaste | sed -e 's,.*: ,,' -e 's/ /, /g' -e 's/$/,/' + - addr: 0x2000fe70 + UInt32: [ + 0x0000002a, 0x20010e58, 0x00203923, 0x00000001, + 0x2000fe88, 0x00203911, 0x2000ffdc, 0xfffffff9, + 0x00000102, 0x00000002, 0x000003f0, 0x0000002a, + 0x20012620, 0x00203215, 0x00203366, 0x81000200, + 0x00203215, 0x200128b0, 0x0024928d, 0x2000fecc, + 0x002491ed, 0x20010e58, 0x20010e4c, 0x2000ffa0, + 0x200107a0, 0x0000003c, 0x200116e8, 0x200108b0, + 0x0020b895, 0x00000000, 0x0000e200, 0x2001227d, + 0x200121fd, 0x0000e000, 0x00000000, 0x200129a0, + 0x002035bf, 0x00000029, 0x000003d8, 0x20011120, + 0x200116e0, 0x40003800, 0x20011120, 0x00000000, + 0x00205169, 0x00203713, 0x00000000, 0x0022dcb9, + 0x40003800, 0x20011240, 0x00000000, 0xf7d71ecf, + 0xfc7676d6, 0x00000000, 0x968782d3, 0xe75afbbb, + 0x600d77c8, 0xc1c05886, 0x17f3e76d, 0xefc3054d, + 0x11940aaa, 0x00000000, 0x93bffabb, 0x6db85af0, + 0x00000000, 0x2001d76f, 0xcb35f653, 0x00000000, + 0x00000000, 0x079d5058, 0x00000000, 0x00000000, + 0xc5622949, 0x68682572, 0x00000075, 0x0000e500, + 0x20012c30, 0x00000000, 0xcdfcd8c2, 0x76efc90f, + 0x0024495f, 0x20012bf0, 0x0000e400, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0029089c, 0x0029089c, 0x00000000, 0x2000ffe4, + 0x00202a87, 0x2000ffec, 0x00200257, 0x2000fff4, + 0x00200211, 0x00000000, 0x00000000, 0x7badb3f6, + 0x20010794, 0x20010fac, 0x200109b0, 0x002887a4, + 0x00285688, 0x002854c8, 0x00288f74, 0x0028a618, + 0x0028a6f8, 0x00000000, 0x00000001, 0x00000000, + 0x00000000, 0x00000000, 0x002037dd, 0x00000000, + 0x00000002, 0x00000100, 0x00000000, 0x20010064, + 0x00000000, 0x00000000, 0x00000000, 0x200109c0, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 + ] + # exception_catcher() function bytes + # (lldb) dis + # binary`exception_catcher: + # 0x203910 <+0>: push {r3, r4, r5, r6, r7, lr} + # 0x203912 <+2>: add r7, sp, #0x10 + # ... + # (lldb) x/44bx 0x203910 + # % pbpaste | sed -e 's,.*: ,,' -e 's/ /, /g' -e 's/$/,/' + - addr: 0x203910 + UInt8: [ + 0xf8, 0xb5, 0x04, 0xaf, 0x06, 0x4c, 0x07, 0x49, + 0x74, 0xf0, 0x2e, 0xf8, 0x01, 0xac, 0x74, 0xf0, + 0x61, 0xf8, 0x05, 0x48, 0x76, 0xf0, 0xdf, 0xfe, + 0x74, 0xf0, 0x0b, 0xf9, 0xfe, 0xe7, 0x00, 0xbf, + 0x4c, 0x0e, 0x01, 0x20, 0x0d, 0x35, 0x20, 0x00, + 0x98, 0xae, 0x28, 0x00 + ] + diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json b/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json new file mode 100644 index 0000000000000..8fcd5307ff82a --- /dev/null +++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json @@ -0,0 +1,41 @@ +{ + "triple": "armv7m-apple", + "uuid": "2D157DBA-53C9-3AC7-B5A1-9D336EC831CB", + "type": "executable", + "sections": [ + { + "user_id": 100, + "name": "TEXT", + "type": "code", + "address": 2097664, + "size": 598872, + "file_offset": 0, + "file_size": 598872, + "alignment": 2, + "flags": 514, + "read": true, + "write": false, + "execute": true + } + ], + "symbols": [ + { + "name": "main", + "type": "code", + "size": 10, + "address": 2108030 + }, + { + "name": "exception_catcher", + "type": "code", + "size": 44, + "address": 2111760 + }, + { + "name": "exception_thrower", + "type": "code", + "size": 2652, + "address": 2108040 + } + ] +}