diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h index fe8081f83c590..99d948f8b8a8c 100644 --- a/lldb/include/lldb/Symbol/UnwindPlan.h +++ b/lldb/include/lldb/Symbol/UnwindPlan.h @@ -409,7 +409,7 @@ class UnwindPlan { m_unspecified_registers_are_undefined = unspec_is_undef; } - bool GetUnspecifiedRegistersAreUndefined() { + bool GetUnspecifiedRegistersAreUndefined() const { return m_unspecified_registers_are_undefined; } diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 56e0dfef34444..97cfe98a00ccc 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -3786,6 +3786,11 @@ class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed { ABISP abi_sp = process->GetABI(); if (abi_sp) { if (UnwindPlanSP plan_sp = abi_sp->CreateDefaultUnwindPlan()) { + assert(((!plan_sp || plan_sp->GetRowCount() == 0 || + plan_sp->GetRowAtIndex(0) + ->GetUnspecifiedRegistersAreUndefined())) && + "Default UnwindPlan must set " + "UnspecifiedRegistersAreUndefined to true"); result.GetOutputStream().Printf("Arch default UnwindPlan:\n"); plan_sp->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); diff --git a/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp b/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp index d2a2cbe0643ac..0c4495be9a7ba 100644 --- a/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp +++ b/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp @@ -566,6 +566,7 @@ UnwindPlanSP ABISysV_loongarch::CreateDefaultUnwindPlan() { // have been spilled to stack already. row.SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, reg_size * -2, true); row.SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, reg_size * -1, true); + row.SetUnspecifiedRegistersAreUndefined(true); auto plan_sp = std::make_shared(eRegisterKindGeneric); plan_sp->AppendRow(std::move(row)); diff --git a/lldb/source/Plugins/ABI/MSP430/ABISysV_msp430.cpp b/lldb/source/Plugins/ABI/MSP430/ABISysV_msp430.cpp index af7ef03b94c4c..da2c01b4a2ad4 100644 --- a/lldb/source/Plugins/ABI/MSP430/ABISysV_msp430.cpp +++ b/lldb/source/Plugins/ABI/MSP430/ABISysV_msp430.cpp @@ -331,6 +331,7 @@ UnwindPlanSP ABISysV_msp430::CreateDefaultUnwindPlan() { row.SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -2, true); row.SetRegisterLocationToIsCFAPlusOffset(sp_reg_num, 0, true); row.SetRegisterLocationToUnspecified(fp_reg_num, true); + row.SetUnspecifiedRegistersAreUndefined(true); auto plan_sp = std::make_shared(eRegisterKindDWARF); plan_sp->AppendRow(std::move(row)); diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp index bf0e5a15ad790..f056b7958c063 100644 --- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp +++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp @@ -753,6 +753,7 @@ UnwindPlanSP ABISysV_riscv::CreateDefaultUnwindPlan() { // have been spilled to stack already. row.SetRegisterLocationToAtCFAPlusOffset(fp_reg_num, reg_size * -2, true); row.SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, reg_size * -1, true); + row.SetUnspecifiedRegistersAreUndefined(true); auto plan_sp = std::make_shared(eRegisterKindGeneric); plan_sp->AppendRow(std::move(row)); diff --git a/lldb/source/Symbol/FuncUnwinders.cpp b/lldb/source/Symbol/FuncUnwinders.cpp index fffc35d7f6177..9bd10ba64648b 100644 --- a/lldb/source/Symbol/FuncUnwinders.cpp +++ b/lldb/source/Symbol/FuncUnwinders.cpp @@ -459,8 +459,15 @@ FuncUnwinders::GetUnwindPlanArchitectureDefault(Thread &thread) { ProcessSP process_sp(thread.CalculateProcess()); if (process_sp) { - if (ABI *abi = process_sp->GetABI().get()) + if (ABI *abi = process_sp->GetABI().get()) { m_unwind_plan_arch_default_sp = abi->CreateDefaultUnwindPlan(); + assert(((!m_unwind_plan_arch_default_sp || + m_unwind_plan_arch_default_sp->GetRowCount() == 0 || + m_unwind_plan_arch_default_sp->GetRowAtIndex(0) + ->GetUnspecifiedRegistersAreUndefined())) && + "Default UnwindPlan must set " + "UnspecifiedRegistersAreUndefined to true"); + } } return m_unwind_plan_arch_default_sp; diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 139387db7a04f..0039b29034f4d 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -80,7 +80,7 @@ RegisterContextUnwind::RegisterContextUnwind(Thread &thread, m_fallback_unwind_plan_sp(), m_all_registers_available(false), m_frame_type(-1), m_cfa(LLDB_INVALID_ADDRESS), m_afa(LLDB_INVALID_ADDRESS), m_start_pc(), m_current_pc(), - m_current_offset(0), m_current_offset_backed_up_one(0), + m_current_offset(), m_current_offset_backed_up_one(), m_behaves_like_zeroth_frame(false), m_sym_ctx(sym_ctx), m_sym_ctx_valid(false), m_frame_number(frame_number), m_registers(), m_parent_unwind(unwind_lldb) { @@ -442,6 +442,12 @@ void RegisterContextUnwind::InitializeNonZerothFrame() { if (abi_sp) { m_fast_unwind_plan_sp.reset(); m_full_unwind_plan_sp = abi_sp->CreateDefaultUnwindPlan(); + assert(((!m_full_unwind_plan_sp || + m_full_unwind_plan_sp->GetRowCount() == 0 || + m_full_unwind_plan_sp->GetRowAtIndex(0) + ->GetUnspecifiedRegistersAreUndefined())) && + "Default UnwindPlan must set " + "UnspecifiedRegistersAreUndefined to true"); if (m_frame_type != eSkipFrame) // don't override eSkipFrame { m_frame_type = eNormalFrame; @@ -810,6 +816,12 @@ RegisterContextUnwind::GetFullUnwindPlanForFrame() { ABI *abi = process ? process->GetABI().get() : nullptr; if (abi) { arch_default_unwind_plan_sp = abi->CreateDefaultUnwindPlan(); + assert(((!arch_default_unwind_plan_sp || + arch_default_unwind_plan_sp->GetRowCount() == 0 || + arch_default_unwind_plan_sp->GetRowAtIndex(0) + ->GetUnspecifiedRegistersAreUndefined())) && + "Default UnwindPlan must set " + "UnspecifiedRegistersAreUndefined to true"); } else { UNWIND_LOG( log, "unable to get architectural default UnwindPlan from ABI plugin"); @@ -1386,6 +1398,15 @@ RegisterContextUnwind::GetAbstractRegisterLocation(uint32_t lldb_regnum, "could not convert lldb regnum {0} ({1}) into {2} " "RegisterKind reg numbering scheme", regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), kind); + if (active_row && active_row->GetUnspecifiedRegistersAreUndefined()) { + UNWIND_LOG( + log, + "marking register {0} ({1}) as Undefined (volatile) in this " + "stack frame because this row is UnspecifiedRegistersAreUndefined.", + regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); + unwindplan_regloc.SetUndefined(); + return unwindplan_regloc; + } return {}; } @@ -1481,6 +1502,15 @@ RegisterContextUnwind::GetAbstractRegisterLocation(uint32_t lldb_regnum, } } } + if (active_row && active_row->GetUnspecifiedRegistersAreUndefined()) { + UNWIND_LOG( + log, + "marking register {0} ({1}) as Undefined (volatile) in this " + "stack frame because this row is UnspecifiedRegistersAreUndefined.", + regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); + unwindplan_regloc.SetUndefined(); + return unwindplan_regloc; + } } ExecutionContext exe_ctx(m_thread.shared_from_this()); diff --git a/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py b/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py index 449da70fb08ca..b3afdbe1c5608 100644 --- a/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py +++ b/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py @@ -25,7 +25,7 @@ def test_riscv32_gpr_corefile_registers(self): self.assertEqual(process.GetNumThreads(), 2) thread = process.GetThreadAtIndex(0) - self.assertEqual(thread.GetNumFrames(), 1) + self.assertEqual(thread.GetNumFrames(), 2) frame = thread.GetFrameAtIndex(0) gpr_regs = frame.registers.GetValueAtIndex(0) @@ -75,10 +75,31 @@ def test_riscv32_gpr_corefile_registers(self): idx = 0 while idx < len(regnames): - val = idx | (idx << 8) | (idx << 16) | (idx << 24) - self.assertEqual(gpr_regs.GetChildAtIndex(idx).GetValueAsUnsigned(), val) + # fp needs to meet specific alignment rules, + # so hardcode that value. + if regnames[idx] == "fp": + self.assertEqual( + gpr_regs.GetChildAtIndex(idx).GetValueAsUnsigned(), 0x08080800 + ) + else: + val = idx | (idx << 8) | (idx << 16) | (idx << 24) + self.assertEqual( + gpr_regs.GetChildAtIndex(idx).GetValueAsUnsigned(), val + ) idx = idx + 1 + # We used an ArchDefaultUnwindPlan to get the second stack frame. + # Only pc and fp should be available in this stack frame; any other + # register being available is a bug. + frame = thread.GetFrameAtIndex(1) + gpr_regs = frame.registers.GetValueAtIndex(0) + + # Get pc + self.assertEqual(gpr_regs.GetChildAtIndex(32).GetValueAsUnsigned(3), 0x20201020) + # Should not be able to read a callee-preserved register, s6 + self.assertEqual(gpr_regs.GetChildAtIndex(22).GetValueAsUnsigned(3), 3) + + # Check that we can read registers for second thread. thread = process.GetThreadAtIndex(1) self.assertEqual(thread.GetNumFrames(), 1) diff --git a/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml b/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml index 81c725f1a4f0e..8163e5ab8b80f 100644 --- a/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml +++ b/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml @@ -9,7 +9,7 @@ threads: {name: sp, value: 0x02020202}, {name: gp, value: 0x03030303}, {name: tp, value: 0x04040404}, {name: t0, value: 0x05050505}, {name: t1, value: 0x06060606}, {name: t2, value: 0x07070707}, - {name: fp, value: 0x08080808}, {name: s1, value: 0x09090909}, + {name: fp, value: 0x08080800}, {name: s1, value: 0x09090909}, {name: a0, value: 0x0a0a0a0a}, {name: a1, value: 0x0b0b0b0b}, {name: a2, value: 0x0c0c0c0c}, {name: a3, value: 0x0d0d0d0d}, {name: a4, value: 0x0e0e0e0e}, {name: a5, value: 0x0f0f0f0f}, @@ -30,7 +30,7 @@ threads: {name: sp, value: 0x92020202}, {name: gp, value: 0x93030303}, {name: tp, value: 0x94040404}, {name: t0, value: 0x95050505}, {name: t1, value: 0x96060606}, {name: t2, value: 0x97070707}, - {name: fp, value: 0x98080808}, {name: s1, value: 0x99090909}, + {name: fp, value: 0x98080800}, {name: s1, value: 0x99090909}, {name: a0, value: 0x9a0a0a0a}, {name: a1, value: 0x9b0b0b0b}, {name: a2, value: 0x9c0c0c0c}, {name: a3, value: 0x9d0d0d0d}, {name: a4, value: 0x9e0e0e0e}, {name: a5, value: 0x9f0f0f0f}, @@ -44,3 +44,17 @@ threads: {name: t5, value: 0x9e1e1e1e}, {name: t6, value: 0x9f1f1f1f}, {name: pc, value: 0x90202020} ] + +memory-regions: + # Stack memory giving lldb a saved fp & pc + - addr: 0x080807f8 + UInt32: [ 0x08080700, 0x20201020, 0x00000000 ] + # Stack memory for the frame above, indicating end of stack + - addr: 0x080806f8 + UInt32: [ 0x00000000, 0x00000000, 0x00000000 ] + # Function address prologue + - addr: 0x20201020 + UInt16: [ 0xb882, 0x1000 ] + # Function address prologue + - addr: 0x20202020 + UInt16: [ 0xb882, 0x1000 ] diff --git a/lldb/tools/yaml2macho-core/CoreSpec.cpp b/lldb/tools/yaml2macho-core/CoreSpec.cpp index 22551a60b3a5e..cf3215fd06be3 100644 --- a/lldb/tools/yaml2macho-core/CoreSpec.cpp +++ b/lldb/tools/yaml2macho-core/CoreSpec.cpp @@ -51,12 +51,16 @@ template <> struct llvm::yaml::MappingTraits { static void mapping(IO &io, MemoryRegion &memory) { io.mapRequired("addr", memory.addr); io.mapOptional("UInt8", memory.bytes); + io.mapOptional("UInt16", memory.halfwords); io.mapOptional("UInt32", memory.words); io.mapOptional("UInt64", memory.doublewords); if (memory.bytes.size()) { memory.type = MemoryType::UInt8; memory.size = memory.bytes.size(); + } else if (memory.halfwords.size()) { + memory.type = MemoryType::UInt16; + memory.size = memory.halfwords.size() * 2; } else if (memory.words.size()) { memory.type = MemoryType::UInt32; memory.size = memory.words.size() * 4; diff --git a/lldb/tools/yaml2macho-core/CoreSpec.h b/lldb/tools/yaml2macho-core/CoreSpec.h index 5c27cc96bdaad..aadd39bd13c4d 100644 --- a/lldb/tools/yaml2macho-core/CoreSpec.h +++ b/lldb/tools/yaml2macho-core/CoreSpec.h @@ -37,7 +37,7 @@ struct Thread { std::vector regsets; }; -enum MemoryType { UInt8 = 0, UInt32, UInt64 }; +enum MemoryType { UInt8 = 0, UInt16, UInt32, UInt64 }; struct MemoryRegion { uint64_t addr; @@ -45,6 +45,7 @@ struct MemoryRegion { uint32_t size; // One of the following formats. std::vector bytes; + std::vector halfwords; std::vector words; std::vector doublewords; }; diff --git a/lldb/tools/yaml2macho-core/MemoryWriter.cpp b/lldb/tools/yaml2macho-core/MemoryWriter.cpp index 4ace2894b6be5..139e39e8ac9fe 100644 --- a/lldb/tools/yaml2macho-core/MemoryWriter.cpp +++ b/lldb/tools/yaml2macho-core/MemoryWriter.cpp @@ -46,6 +46,10 @@ void create_memory_bytes(const CoreSpec &spec, const MemoryRegion &memory, for (uint8_t byte : memory.bytes) buf.push_back(byte); + if (memory.type == MemoryType::UInt16) + for (uint16_t halfword : memory.halfwords) + add_uint16(buf, halfword); + if (memory.type == MemoryType::UInt32) for (uint32_t word : memory.words) add_uint32(buf, word); diff --git a/lldb/tools/yaml2macho-core/Utility.cpp b/lldb/tools/yaml2macho-core/Utility.cpp index 2414c2f039323..c56c8403b71c1 100644 --- a/lldb/tools/yaml2macho-core/Utility.cpp +++ b/lldb/tools/yaml2macho-core/Utility.cpp @@ -20,3 +20,9 @@ void add_uint32(std::vector &buf, uint32_t val) { for (int i = 0; i < 4; i++) buf.push_back(*p++); } + +void add_uint16(std::vector &buf, uint16_t val) { + uint8_t *p = reinterpret_cast(&val); + for (int i = 0; i < 2; i++) + buf.push_back(*p++); +} diff --git a/lldb/tools/yaml2macho-core/Utility.h b/lldb/tools/yaml2macho-core/Utility.h index ff05858c4dee2..e20fb7c8d5756 100644 --- a/lldb/tools/yaml2macho-core/Utility.h +++ b/lldb/tools/yaml2macho-core/Utility.h @@ -14,5 +14,6 @@ void add_uint64(std::vector &buf, uint64_t val); void add_uint32(std::vector &buf, uint32_t val); +void add_uint16(std::vector &buf, uint16_t val); #endif