https://github.com/dsandersllvm updated https://github.com/llvm/llvm-project/pull/150732
>From bd27929645928939f319dc915da1a4636e4d317d Mon Sep 17 00:00:00 2001 From: Daniel Sanders <daniel_l_sand...@apple.com> Date: Wed, 28 Aug 2024 19:02:21 -0700 Subject: [PATCH 1/6] [lldb] Implement DW_CFA_val_offset and DW_CFA_val_offset_sf The test for this is artificial as I'm not aware of any upstream targets that use DW_CFA_val_offset RegisterContextUnwind::ReadFrameAddress now reports how it's attempting to obtain the CFA unless all success/failure cases emit logs that clearly identify the method it was attempting. Previously several of the existing failure paths emit no message or a message that's indistinguishable from those on other paths. --- lldb/include/lldb/Symbol/UnwindPlan.h | 20 +++ lldb/include/lldb/Target/UnwindLLDB.h | 8 ++ lldb/source/Symbol/DWARFCallFrameInfo.cpp | 28 +++- lldb/source/Symbol/UnwindPlan.cpp | 17 +++ lldb/source/Target/RegisterContextUnwind.cpp | 54 +++++++- .../Symbol/TestDWARFCallFrameInfo.cpp | 124 ++++++++++++++++++ 6 files changed, 248 insertions(+), 3 deletions(-) diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h index fe8081f83c590..9587f1312aa2e 100644 --- a/lldb/include/lldb/Symbol/UnwindPlan.h +++ b/lldb/include/lldb/Symbol/UnwindPlan.h @@ -67,6 +67,7 @@ class UnwindPlan { atAFAPlusOffset, // reg = deref(AFA + offset) isAFAPlusOffset, // reg = AFA + offset inOtherRegister, // reg = other reg + isOtherRegisterPlusOffset, // reg = other reg + offset atDWARFExpression, // reg = deref(eval(dwarf_expr)) isDWARFExpression, // reg = eval(dwarf_expr) isConstant // reg = constant @@ -102,6 +103,10 @@ class UnwindPlan { bool IsInOtherRegister() const { return m_type == inOtherRegister; } + bool IsOtherRegisterPlusOffset() const { + return m_type == isOtherRegisterPlusOffset; + } + bool IsAtDWARFExpression() const { return m_type == atDWARFExpression; } bool IsDWARFExpression() const { return m_type == isDWARFExpression; } @@ -140,9 +145,17 @@ class UnwindPlan { m_location.reg_num = reg_num; } + void SetIsRegisterPlusOffset(uint32_t reg_num, int32_t offset = 0) { + m_type = isOtherRegisterPlusOffset; + m_location.reg_plus_offset.reg_num = reg_num; + m_location.reg_plus_offset.offset = offset; + } + uint32_t GetRegisterNumber() const { if (m_type == inOtherRegister) return m_location.reg_num; + if (m_type == isOtherRegisterPlusOffset) + return m_location.reg_plus_offset.reg_num; return LLDB_INVALID_REGNUM; } @@ -156,6 +169,8 @@ class UnwindPlan { case atAFAPlusOffset: case isAFAPlusOffset: return m_location.offset; + case inOtherRegister: + return m_location.reg_plus_offset.offset; default: return 0; } @@ -204,6 +219,11 @@ class UnwindPlan { } expr; // For m_type == isConstant uint64_t constant_value; + // For m_type == inOtherRegisterPlusOffset + struct { + uint32_t reg_num; + int32_t offset; + } reg_plus_offset; } m_location; }; diff --git a/lldb/include/lldb/Target/UnwindLLDB.h b/lldb/include/lldb/Target/UnwindLLDB.h index f2f65e67a7640..88180b37fd93a 100644 --- a/lldb/include/lldb/Target/UnwindLLDB.h +++ b/lldb/include/lldb/Target/UnwindLLDB.h @@ -49,6 +49,9 @@ class UnwindLLDB : public lldb_private::Unwind { // target mem (target_memory_location) eRegisterInRegister, // register is available in a (possible other) // register (register_number) + eRegisterIsRegisterPlusOffset, // register is available in a (possible + // other) register (register_number) with + // an offset applied eRegisterSavedAtHostMemoryLocation, // register is saved at a word in // lldb's address space eRegisterValueInferred, // register val was computed (and is in @@ -64,6 +67,11 @@ class UnwindLLDB : public lldb_private::Unwind { void *host_memory_location; uint64_t inferred_value; // eRegisterValueInferred - e.g. stack pointer == // cfa + offset + struct { + uint32_t + register_number; // in eRegisterKindLLDB register numbering system + uint64_t offset; + } reg_plus_offset; } location; }; diff --git a/lldb/source/Symbol/DWARFCallFrameInfo.cpp b/lldb/source/Symbol/DWARFCallFrameInfo.cpp index a2d748adad64a..2f8f9e9182fb2 100644 --- a/lldb/source/Symbol/DWARFCallFrameInfo.cpp +++ b/lldb/source/Symbol/DWARFCallFrameInfo.cpp @@ -766,8 +766,32 @@ DWARFCallFrameInfo::ParseFDE(dw_offset_t dwarf_offset, break; } - case DW_CFA_val_offset: // 0x14 - case DW_CFA_val_offset_sf: // 0x15 + case DW_CFA_val_offset: { // 0x14 + // takes two unsigned LEB128 operands representing a register number + // and a factored offset. The required action is to change the rule + // for the register indicated by the register number to be a + // val_offset(N) rule where the value of N is factored_offset* + // data_alignment_factor + uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + int32_t op_offset = + (int32_t)m_cfi_data.GetULEB128(&offset) * data_align; + reg_location.SetIsCFAPlusOffset(op_offset); + row.SetRegisterInfo(reg_num, reg_location); + break; + } + case DW_CFA_val_offset_sf: { // 0x15 + // takes two operands: an unsigned LEB128 value representing a + // register number and a signed LEB128 factored offset. This + // instruction is identical to DW_CFA_val_offset except that the + // second operand is signed and factored. The resulting offset is + // factored_offset* data_alignment_factor. + uint32_t reg_num = (uint32_t)m_cfi_data.GetULEB128(&offset); + int32_t op_offset = + (int32_t)m_cfi_data.GetSLEB128(&offset) * data_align; + reg_location.SetIsCFAPlusOffset(op_offset); + row.SetRegisterInfo(reg_num, reg_location); + break; + } default: break; } diff --git a/lldb/source/Symbol/UnwindPlan.cpp b/lldb/source/Symbol/UnwindPlan.cpp index 9245e52732061..93587568a7e64 100644 --- a/lldb/source/Symbol/UnwindPlan.cpp +++ b/lldb/source/Symbol/UnwindPlan.cpp @@ -42,6 +42,12 @@ bool UnwindPlan::Row::AbstractRegisterLocation::operator==( case inOtherRegister: return m_location.reg_num == rhs.m_location.reg_num; + case isOtherRegisterPlusOffset: + return m_location.reg_plus_offset.reg_num == + rhs.m_location.reg_plus_offset.reg_num && + m_location.reg_plus_offset.offset == + rhs.m_location.reg_plus_offset.offset; + case atDWARFExpression: case isDWARFExpression: if (m_location.expr.length == rhs.m_location.expr.length) @@ -145,6 +151,17 @@ void UnwindPlan::Row::AbstractRegisterLocation::Dump( s.Printf("=reg(%u)", m_location.reg_num); } break; + case isOtherRegisterPlusOffset: { + const RegisterInfo *other_reg_info = nullptr; + if (unwind_plan) + other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num); + if (other_reg_info) + s.Printf("=%s", other_reg_info->name); + else + s.Printf("=reg(%u)+%u", m_location.reg_plus_offset.reg_num, + m_location.reg_plus_offset.offset); + } break; + case atDWARFExpression: case isDWARFExpression: { s.PutChar('='); diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 880300d0637fb..80345e6d3588c 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1118,6 +1118,27 @@ bool RegisterContextUnwind::ReadRegisterValueFromRegisterLocation( success = GetNextFrame()->ReadRegister(other_reg_info, value); } } break; + case UnwindLLDB::ConcreteRegisterLocation::eRegisterIsRegisterPlusOffset: { + auto regnum = regloc.location.reg_plus_offset.register_number; + const RegisterInfo *other_reg_info = + GetRegisterInfoAtIndex(regloc.location.reg_plus_offset.register_number); + + if (!other_reg_info) + return false; + + if (IsFrameZero()) { + success = + m_thread.GetRegisterContext()->ReadRegister(other_reg_info, value); + } else { + success = GetNextFrame()->ReadRegister(other_reg_info, value); + } + if (success) { + UnwindLogMsg("read (%d)'s location", regnum); + value = value.GetAsUInt64(~0ull, &success) + + regloc.location.reg_plus_offset.offset; + UnwindLogMsg("success %s", success ? "yes" : "no"); + } + } break; case UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred: success = value.SetUInt(regloc.location.inferred_value, reg_info->byte_size); @@ -1164,6 +1185,7 @@ bool RegisterContextUnwind::WriteRegisterValueToRegisterLocation( success = GetNextFrame()->WriteRegister(other_reg_info, value); } } break; + case UnwindLLDB::ConcreteRegisterLocation::eRegisterIsRegisterPlusOffset: case UnwindLLDB::ConcreteRegisterLocation::eRegisterValueInferred: case UnwindLLDB::ConcreteRegisterLocation::eRegisterNotSaved: break; @@ -1633,6 +1655,30 @@ RegisterContextUnwind::SavedLocationForRegister( return UnwindLLDB::RegisterSearchResult::eRegisterFound; } + if (abs_regloc->IsOtherRegisterPlusOffset()) { + uint32_t unwindplan_regnum = abs_regloc->GetRegisterNumber(); + int unwindplan_offset = abs_regloc->GetOffset(); + RegisterNumber row_regnum(m_thread, abs_regkind, unwindplan_regnum); + if (row_regnum.GetAsKind(eRegisterKindLLDB) == LLDB_INVALID_REGNUM) { + UnwindLogMsg("could not supply caller's %s (%d) location - was saved in " + "another reg+offset but couldn't convert that regnum", + regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); + return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; + } + regloc.type = + UnwindLLDB::ConcreteRegisterLocation::eRegisterIsRegisterPlusOffset; + regloc.location.reg_plus_offset.register_number = + row_regnum.GetAsKind(eRegisterKindLLDB); + regloc.location.reg_plus_offset.offset = unwindplan_offset; + m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; + UnwindLogMsg("supplying caller's register %s (%d), " + "from register %s (%d) plus offset %u", + regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), + row_regnum.GetName(), row_regnum.GetAsKind(eRegisterKindLLDB), + unwindplan_offset); + return UnwindLLDB::RegisterSearchResult::eRegisterFound; + } + if (abs_regloc->IsDWARFExpression() || abs_regloc->IsAtDWARFExpression()) { DataExtractor dwarfdata(abs_regloc->GetDWARFExpressionBytes(), abs_regloc->GetDWARFExpressionLength(), @@ -1959,6 +2005,7 @@ bool RegisterContextUnwind::ReadFrameAddress( switch (fa.GetValueType()) { case UnwindPlan::Row::FAValue::isRegisterDereferenced: { + UnwindLogMsg("CFA value via dereferencing reg"); RegisterNumber cfa_reg(m_thread, row_register_kind, fa.GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { @@ -1991,6 +2038,7 @@ bool RegisterContextUnwind::ReadFrameAddress( break; } case UnwindPlan::Row::FAValue::isRegisterPlusOffset: { + UnwindLogMsg("CFA value via register plus offset"); RegisterNumber cfa_reg(m_thread, row_register_kind, fa.GetRegisterNumber()); if (ReadGPRValue(cfa_reg, cfa_reg_contents)) { @@ -2012,10 +2060,13 @@ bool RegisterContextUnwind::ReadFrameAddress( address, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents, fa.GetOffset()); return true; - } + } else + UnwindLogMsg("unable to read CFA register %s (%d)", cfa_reg.GetName(), + cfa_reg.GetAsKind(eRegisterKindLLDB)); break; } case UnwindPlan::Row::FAValue::isDWARFExpression: { + UnwindLogMsg("CFA value via DWARF expression"); ExecutionContext exe_ctx(m_thread.shared_from_this()); Process *process = exe_ctx.GetProcessPtr(); DataExtractor dwarfdata(fa.GetDWARFExpressionBytes(), @@ -2042,6 +2093,7 @@ bool RegisterContextUnwind::ReadFrameAddress( break; } case UnwindPlan::Row::FAValue::isRaSearch: { + UnwindLogMsg("CFA value via heuristic search"); Process &process = *m_thread.GetProcess(); lldb::addr_t return_address_hint = GetReturnAddressHint(fa.GetOffset()); if (return_address_hint == LLDB_INVALID_ADDRESS) diff --git a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp index c1dcab02227da..a75a4acd304b2 100644 --- a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp +++ b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp @@ -39,6 +39,7 @@ class DWARFCallFrameInfoTest : public testing::Test { protected: void TestBasic(DWARFCallFrameInfo::Type type, llvm::StringRef symbol); + void TestValOffset(DWARFCallFrameInfo::Type type, llvm::StringRef symbol); }; namespace lldb_private { @@ -256,3 +257,126 @@ TEST_F(DWARFCallFrameInfoTest, Basic_dwarf4) { TEST_F(DWARFCallFrameInfoTest, Basic_eh) { TestBasic(DWARFCallFrameInfo::EH, "eh_frame"); } + +static UnwindPlan::Row GetValOffsetExpectedRow0() { + UnwindPlan::Row row; + row.SetOffset(0); + row.GetCFAValue().SetIsRegisterPlusOffset(dwarf_rsp_x86_64, 16); + row.SetRegisterLocationToAtCFAPlusOffset(dwarf_rip_x86_64, -8, false); + row.SetRegisterLocationToIsCFAPlusOffset(dwarf_rbp_x86_64, -16, false); + return row; +} + +void DWARFCallFrameInfoTest::TestValOffset(DWARFCallFrameInfo::Type type, + llvm::StringRef symbol) { + // This test is artificial as X86 does not use DW_CFA_val_offset but this + // test verifies that we can successfully interpret them if they do occur. + // Note the distinction between RBP and RIP in this part of the DWARF dump: + // 0x0: CFA=RSP+16: RBP=CFA-16, RIP=[CFA-8] + // Whereas RIP is stored in the memory CFA-8 points at, RBP is reconstructed + // from the CFA without any memory access. + auto ExpectedFile = TestFile::fromYaml(R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + SectionHeaderStringTable: .strtab +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x4 + Content: 0F1F00 + - Name: .debug_frame + Type: SHT_PROGBITS + AddressAlign: 0x8 +#00000000 00000014 ffffffff CIE +# Format: DWARF32 +# Version: 4 +# Augmentation: "" +# Address size: 8 +# Segment desc size: 0 +# Code alignment factor: 1 +# Data alignment factor: -8 +# Return address column: 16 +# +# DW_CFA_def_cfa: RSP +8 +# DW_CFA_offset: RIP -8 +# DW_CFA_nop: +# DW_CFA_nop: +# DW_CFA_nop: +# DW_CFA_nop: +# +# CFA=RSP+8: RIP=[CFA-8] +# +#00000018 0000001c 00000000 FDE cie=00000000 pc=00000000...00000003 +# Format: DWARF32 +# DW_CFA_def_cfa_offset: +16 +# DW_CFA_val_offset: RBP -16 +# DW_CFA_nop: +# DW_CFA_nop: +# DW_CFA_nop: +# +# 0x0: CFA=RSP+16: RBP=CFA-16, RIP=[CFA-8] + Content: 14000000FFFFFFFF040008000178100C07089001000000001C00000000000000000000000000000003000000000000000E10140602000000 + - Name: .rela.debug_frame + Type: SHT_RELA + Flags: [ SHF_INFO_LINK ] + Link: .symtab + AddressAlign: 0x8 + Info: .debug_frame + Relocations: + - Offset: 0x1C + Symbol: .debug_frame + Type: R_X86_64_32 + - Offset: 0x20 + Symbol: .text + Type: R_X86_64_64 + - Type: SectionHeaderTable + Sections: + - Name: .strtab + - Name: .text + - Name: .debug_frame + - Name: .rela.debug_frame + - Name: .symtab +Symbols: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: debug_frame3 + Section: .text + - Name: .debug_frame + Type: STT_SECTION + Section: .debug_frame +... +)"); + ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded()); + + auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec()); + SectionList *list = module_sp->GetSectionList(); + ASSERT_NE(nullptr, list); + + auto section_sp = list->FindSectionByType(type == DWARFCallFrameInfo::EH + ? eSectionTypeEHFrame + : eSectionTypeDWARFDebugFrame, + false); + ASSERT_NE(nullptr, section_sp); + + DWARFCallFrameInfo cfi(*module_sp->GetObjectFile(), section_sp, type); + + const Symbol *sym = module_sp->FindFirstSymbolWithNameAndType( + ConstString(symbol), eSymbolTypeAny); + ASSERT_NE(nullptr, sym); + + std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress()); + ASSERT_TRUE(plan_up); + ASSERT_EQ(1, plan_up->GetRowCount()); + EXPECT_THAT(plan_up->GetRowAtIndex(0), testing::Pointee(GetValOffsetExpectedRow0())); +} + +TEST_F(DWARFCallFrameInfoTest, ValOffset_dwarf3) { + TestValOffset(DWARFCallFrameInfo::DWARF, "debug_frame3"); +} + >From c9e9bbfbfca5a2221bf5bb0be32789a705241a50 Mon Sep 17 00:00:00 2001 From: Daniel Sanders <daniel_l_sand...@apple.com> Date: Mon, 28 Jul 2025 09:06:34 -0700 Subject: [PATCH 2/6] fixup: formatting --- lldb/include/lldb/Symbol/UnwindPlan.h | 26 +++++++++---------- .../Symbol/TestDWARFCallFrameInfo.cpp | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h index 9587f1312aa2e..7c562e6686023 100644 --- a/lldb/include/lldb/Symbol/UnwindPlan.h +++ b/lldb/include/lldb/Symbol/UnwindPlan.h @@ -57,20 +57,20 @@ class UnwindPlan { class AbstractRegisterLocation { public: enum RestoreType { - unspecified, // not specified, we may be able to assume this - // is the same register. gcc doesn't specify all - // initial values so we really don't know... - undefined, // reg is not available, e.g. volatile reg - same, // reg is unchanged - atCFAPlusOffset, // reg = deref(CFA + offset) - isCFAPlusOffset, // reg = CFA + offset - atAFAPlusOffset, // reg = deref(AFA + offset) - isAFAPlusOffset, // reg = AFA + offset - inOtherRegister, // reg = other reg + unspecified, // not specified, we may be able to assume this + // is the same register. gcc doesn't specify all + // initial values so we really don't know... + undefined, // reg is not available, e.g. volatile reg + same, // reg is unchanged + atCFAPlusOffset, // reg = deref(CFA + offset) + isCFAPlusOffset, // reg = CFA + offset + atAFAPlusOffset, // reg = deref(AFA + offset) + isAFAPlusOffset, // reg = AFA + offset + inOtherRegister, // reg = other reg isOtherRegisterPlusOffset, // reg = other reg + offset - atDWARFExpression, // reg = deref(eval(dwarf_expr)) - isDWARFExpression, // reg = eval(dwarf_expr) - isConstant // reg = constant + atDWARFExpression, // reg = deref(eval(dwarf_expr)) + isDWARFExpression, // reg = eval(dwarf_expr) + isConstant // reg = constant }; AbstractRegisterLocation() : m_location() {} diff --git a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp index a75a4acd304b2..e113b8ca99341 100644 --- a/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp +++ b/lldb/unittests/Symbol/TestDWARFCallFrameInfo.cpp @@ -373,10 +373,10 @@ void DWARFCallFrameInfoTest::TestValOffset(DWARFCallFrameInfo::Type type, std::unique_ptr<UnwindPlan> plan_up = cfi.GetUnwindPlan(sym->GetAddress()); ASSERT_TRUE(plan_up); ASSERT_EQ(1, plan_up->GetRowCount()); - EXPECT_THAT(plan_up->GetRowAtIndex(0), testing::Pointee(GetValOffsetExpectedRow0())); + EXPECT_THAT(plan_up->GetRowAtIndex(0), + testing::Pointee(GetValOffsetExpectedRow0())); } TEST_F(DWARFCallFrameInfoTest, ValOffset_dwarf3) { TestValOffset(DWARFCallFrameInfo::DWARF, "debug_frame3"); } - >From 7a0329fe6cc02f1ea057dccd09336b18968474b2 Mon Sep 17 00:00:00 2001 From: Daniel Sanders <daniel_l_sand...@apple.com> Date: Mon, 28 Jul 2025 10:25:29 -0700 Subject: [PATCH 3/6] fixup: Remove unused code --- lldb/include/lldb/Symbol/UnwindPlan.h | 20 ---------------- lldb/source/Symbol/UnwindPlan.cpp | 17 -------------- lldb/source/Target/RegisterContextUnwind.cpp | 24 -------------------- 3 files changed, 61 deletions(-) diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h index 7c562e6686023..a38d36a69302d 100644 --- a/lldb/include/lldb/Symbol/UnwindPlan.h +++ b/lldb/include/lldb/Symbol/UnwindPlan.h @@ -67,7 +67,6 @@ class UnwindPlan { atAFAPlusOffset, // reg = deref(AFA + offset) isAFAPlusOffset, // reg = AFA + offset inOtherRegister, // reg = other reg - isOtherRegisterPlusOffset, // reg = other reg + offset atDWARFExpression, // reg = deref(eval(dwarf_expr)) isDWARFExpression, // reg = eval(dwarf_expr) isConstant // reg = constant @@ -103,10 +102,6 @@ class UnwindPlan { bool IsInOtherRegister() const { return m_type == inOtherRegister; } - bool IsOtherRegisterPlusOffset() const { - return m_type == isOtherRegisterPlusOffset; - } - bool IsAtDWARFExpression() const { return m_type == atDWARFExpression; } bool IsDWARFExpression() const { return m_type == isDWARFExpression; } @@ -145,17 +140,9 @@ class UnwindPlan { m_location.reg_num = reg_num; } - void SetIsRegisterPlusOffset(uint32_t reg_num, int32_t offset = 0) { - m_type = isOtherRegisterPlusOffset; - m_location.reg_plus_offset.reg_num = reg_num; - m_location.reg_plus_offset.offset = offset; - } - uint32_t GetRegisterNumber() const { if (m_type == inOtherRegister) return m_location.reg_num; - if (m_type == isOtherRegisterPlusOffset) - return m_location.reg_plus_offset.reg_num; return LLDB_INVALID_REGNUM; } @@ -169,8 +156,6 @@ class UnwindPlan { case atAFAPlusOffset: case isAFAPlusOffset: return m_location.offset; - case inOtherRegister: - return m_location.reg_plus_offset.offset; default: return 0; } @@ -219,11 +204,6 @@ class UnwindPlan { } expr; // For m_type == isConstant uint64_t constant_value; - // For m_type == inOtherRegisterPlusOffset - struct { - uint32_t reg_num; - int32_t offset; - } reg_plus_offset; } m_location; }; diff --git a/lldb/source/Symbol/UnwindPlan.cpp b/lldb/source/Symbol/UnwindPlan.cpp index 93587568a7e64..9245e52732061 100644 --- a/lldb/source/Symbol/UnwindPlan.cpp +++ b/lldb/source/Symbol/UnwindPlan.cpp @@ -42,12 +42,6 @@ bool UnwindPlan::Row::AbstractRegisterLocation::operator==( case inOtherRegister: return m_location.reg_num == rhs.m_location.reg_num; - case isOtherRegisterPlusOffset: - return m_location.reg_plus_offset.reg_num == - rhs.m_location.reg_plus_offset.reg_num && - m_location.reg_plus_offset.offset == - rhs.m_location.reg_plus_offset.offset; - case atDWARFExpression: case isDWARFExpression: if (m_location.expr.length == rhs.m_location.expr.length) @@ -151,17 +145,6 @@ void UnwindPlan::Row::AbstractRegisterLocation::Dump( s.Printf("=reg(%u)", m_location.reg_num); } break; - case isOtherRegisterPlusOffset: { - const RegisterInfo *other_reg_info = nullptr; - if (unwind_plan) - other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num); - if (other_reg_info) - s.Printf("=%s", other_reg_info->name); - else - s.Printf("=reg(%u)+%u", m_location.reg_plus_offset.reg_num, - m_location.reg_plus_offset.offset); - } break; - case atDWARFExpression: case isDWARFExpression: { s.PutChar('='); diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 80345e6d3588c..9e9e2d86958f3 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -1655,30 +1655,6 @@ RegisterContextUnwind::SavedLocationForRegister( return UnwindLLDB::RegisterSearchResult::eRegisterFound; } - if (abs_regloc->IsOtherRegisterPlusOffset()) { - uint32_t unwindplan_regnum = abs_regloc->GetRegisterNumber(); - int unwindplan_offset = abs_regloc->GetOffset(); - RegisterNumber row_regnum(m_thread, abs_regkind, unwindplan_regnum); - if (row_regnum.GetAsKind(eRegisterKindLLDB) == LLDB_INVALID_REGNUM) { - UnwindLogMsg("could not supply caller's %s (%d) location - was saved in " - "another reg+offset but couldn't convert that regnum", - regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB)); - return UnwindLLDB::RegisterSearchResult::eRegisterNotFound; - } - regloc.type = - UnwindLLDB::ConcreteRegisterLocation::eRegisterIsRegisterPlusOffset; - regloc.location.reg_plus_offset.register_number = - row_regnum.GetAsKind(eRegisterKindLLDB); - regloc.location.reg_plus_offset.offset = unwindplan_offset; - m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = regloc; - UnwindLogMsg("supplying caller's register %s (%d), " - "from register %s (%d) plus offset %u", - regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB), - row_regnum.GetName(), row_regnum.GetAsKind(eRegisterKindLLDB), - unwindplan_offset); - return UnwindLLDB::RegisterSearchResult::eRegisterFound; - } - if (abs_regloc->IsDWARFExpression() || abs_regloc->IsAtDWARFExpression()) { DataExtractor dwarfdata(abs_regloc->GetDWARFExpressionBytes(), abs_regloc->GetDWARFExpressionLength(), >From 321b51671a9a2d492cc40d5d146f6a4e13776886 Mon Sep 17 00:00:00 2001 From: Daniel Sanders <daniel_l_sand...@apple.com> Date: Mon, 28 Jul 2025 12:58:48 -0700 Subject: [PATCH 4/6] fixup: formatting --- lldb/include/lldb/Symbol/UnwindPlan.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lldb/include/lldb/Symbol/UnwindPlan.h b/lldb/include/lldb/Symbol/UnwindPlan.h index a38d36a69302d..fe8081f83c590 100644 --- a/lldb/include/lldb/Symbol/UnwindPlan.h +++ b/lldb/include/lldb/Symbol/UnwindPlan.h @@ -57,19 +57,19 @@ class UnwindPlan { class AbstractRegisterLocation { public: enum RestoreType { - unspecified, // not specified, we may be able to assume this - // is the same register. gcc doesn't specify all - // initial values so we really don't know... - undefined, // reg is not available, e.g. volatile reg - same, // reg is unchanged - atCFAPlusOffset, // reg = deref(CFA + offset) - isCFAPlusOffset, // reg = CFA + offset - atAFAPlusOffset, // reg = deref(AFA + offset) - isAFAPlusOffset, // reg = AFA + offset - inOtherRegister, // reg = other reg - atDWARFExpression, // reg = deref(eval(dwarf_expr)) - isDWARFExpression, // reg = eval(dwarf_expr) - isConstant // reg = constant + unspecified, // not specified, we may be able to assume this + // is the same register. gcc doesn't specify all + // initial values so we really don't know... + undefined, // reg is not available, e.g. volatile reg + same, // reg is unchanged + atCFAPlusOffset, // reg = deref(CFA + offset) + isCFAPlusOffset, // reg = CFA + offset + atAFAPlusOffset, // reg = deref(AFA + offset) + isAFAPlusOffset, // reg = AFA + offset + inOtherRegister, // reg = other reg + atDWARFExpression, // reg = deref(eval(dwarf_expr)) + isDWARFExpression, // reg = eval(dwarf_expr) + isConstant // reg = constant }; AbstractRegisterLocation() : m_location() {} >From 2092e5660273141aa3abda9f297a1017d8d135f2 Mon Sep 17 00:00:00 2001 From: Daniel Sanders <daniel_l_sand...@apple.com> Date: Wed, 30 Jul 2025 19:19:49 -0700 Subject: [PATCH 5/6] fixup: Additional test case --- .../Inputs/eh-frame-dwarf-unwind-val-offset.s | 46 +++++++++++++++ .../eh-frame-dwarf-unwind-val-offset.test | 58 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s create mode 100644 lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test diff --git a/lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s b/lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s new file mode 100644 index 0000000000000..b0ce2c29c9991 --- /dev/null +++ b/lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s @@ -0,0 +1,46 @@ + .text + .globl bar +bar: + .cfi_startproc + leal (%edi, %edi), %eax + ret + .cfi_endproc + + .globl foo +foo: + .cfi_startproc + .cfi_escape 0x16, 0x10, 0x06, 0x38, 0x1c, 0x06, 0x08, 0x47, 0x1c + # Clobber r12 and record that it's reconstructable from CFA + .cfi_val_offset %r12, 0 + movq $0x456, %r12 + call bar + addl $1, %eax + # Reconstruct %r12 + movq %rsp, %r12 + addq %r12, 8 + popq %rdi + subq $0x47, %rdi + jmp *%rdi # Return + .cfi_endproc + + .globl asm_main +asm_main: + .cfi_startproc + pushq %rbp + .cfi_def_cfa_offset 16 + .cfi_offset %rbp, -16 + movq %rsp, %rbp + movq %rsp, %r12 + .cfi_def_cfa_register %rbp + movl $47, %edi + + # Non-standard calling convention. The real return address must be + # decremented by 0x47. + leaq 0x47+1f(%rip), %rax + pushq %rax + jmp foo # call +1: + popq %rbp + .cfi_def_cfa %rsp, 8 + ret + .cfi_endproc diff --git a/lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test b/lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test new file mode 100644 index 0000000000000..00b5fd31113b8 --- /dev/null +++ b/lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test @@ -0,0 +1,58 @@ +# Test handing of the dwarf val_offset() rule which can be used to reconstruct +# the value of a register that is neither in a live register or saved on the +# stack but is computable with CFA + offset. + +# UNSUPPORTED: system-windows, ld_new-bug +# REQUIRES: target-x86_64, native + +# RUN: %clang_host %p/Inputs/call-asm.c %p/Inputs/eh-frame-dwarf-unwind-val-offset.s -o %t +# RUN: %lldb %t -s %s -o exit | FileCheck %s + +breakpoint set -n asm_main +# CHECK: Breakpoint 1: where = {{.*}}`asm_main + +breakpoint set -n bar +# CHECK: Breakpoint 2: where = {{.*}}`bar + +process launch +# CHECK: stop reason = breakpoint 1.1 + +stepi +stepi +stepi +print/x $r12 +# CHECK: (unsigned long) 0x[[#%.16x,R12:]]{{$}} + +continue +# CHECK: stop reason = breakpoint 2.1 + +thread backtrace +# CHECK: frame #0: {{.*}}`bar +# CHECK: frame #1: {{.*}}`foo + 12 +# CHECK: frame #2: {{.*}}`asm_main + 25 + +target modules show-unwind -n bar +# CHECK: eh_frame UnwindPlan: +# CHECK: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] + +target modules show-unwind -n foo +# CHECK: eh_frame UnwindPlan: +# CHECK: row[0]: 0: CFA=rsp +8 => r12=CFA+0 rip=DW_OP_lit8, DW_OP_minus, DW_OP_deref, DW_OP_const1u 0x47, DW_OP_minus + +target modules show-unwind -n asm_main +# CHECK: eh_frame UnwindPlan: +# CHECK: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] +# CHECK: row[1]: 1: CFA=rsp+16 => rbp=[CFA-16] rip=[CFA-8] +# CHECK: row[2]: 7: CFA=rbp+16 => rbp=[CFA-16] rip=[CFA-8] +# CHECK: row[3]: 26: CFA=rsp +8 => rbp=[CFA-16] rip=[CFA-8] + +print/x $r12 +# CHECK: (unsigned long) 0x0000000000000456 + +frame select 1 +print/x $r12 +# CHECK: (unsigned long) 0x0000000000000456 + +frame select 2 +print/x $r12 +# CHECK: (unsigned long) 0x[[#R12]] >From ed7dea3a7b9afcc11bdf4f6356f24205e1afaa81 Mon Sep 17 00:00:00 2001 From: Daniel Sanders <daniel_l_sand...@apple.com> Date: Wed, 30 Jul 2025 19:31:24 -0700 Subject: [PATCH 6/6] fixup: Use a non-zero offset for the .cfi_val_offset --- .../Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s | 3 ++- .../Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s b/lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s index b0ce2c29c9991..cd67114498bb8 100644 --- a/lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s +++ b/lldb/test/Shell/Unwind/Inputs/eh-frame-dwarf-unwind-val-offset.s @@ -11,7 +11,7 @@ foo: .cfi_startproc .cfi_escape 0x16, 0x10, 0x06, 0x38, 0x1c, 0x06, 0x08, 0x47, 0x1c # Clobber r12 and record that it's reconstructable from CFA - .cfi_val_offset %r12, 0 + .cfi_val_offset %r12, 32 movq $0x456, %r12 call bar addl $1, %eax @@ -31,6 +31,7 @@ asm_main: .cfi_offset %rbp, -16 movq %rsp, %rbp movq %rsp, %r12 + addq $32, %r12 .cfi_def_cfa_register %rbp movl $47, %edi diff --git a/lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test b/lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test index 00b5fd31113b8..64811e6196326 100644 --- a/lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test +++ b/lldb/test/Shell/Unwind/eh-frame-dwarf-unwind-val-offset.test @@ -29,7 +29,7 @@ continue thread backtrace # CHECK: frame #0: {{.*}}`bar # CHECK: frame #1: {{.*}}`foo + 12 -# CHECK: frame #2: {{.*}}`asm_main + 25 +# CHECK: frame #2: {{.*}}`asm_main + 29 target modules show-unwind -n bar # CHECK: eh_frame UnwindPlan: @@ -37,14 +37,14 @@ target modules show-unwind -n bar target modules show-unwind -n foo # CHECK: eh_frame UnwindPlan: -# CHECK: row[0]: 0: CFA=rsp +8 => r12=CFA+0 rip=DW_OP_lit8, DW_OP_minus, DW_OP_deref, DW_OP_const1u 0x47, DW_OP_minus +# CHECK: row[0]: 0: CFA=rsp +8 => r12=CFA+32 rip=DW_OP_lit8, DW_OP_minus, DW_OP_deref, DW_OP_const1u 0x47, DW_OP_minus target modules show-unwind -n asm_main # CHECK: eh_frame UnwindPlan: # CHECK: row[0]: 0: CFA=rsp +8 => rip=[CFA-8] # CHECK: row[1]: 1: CFA=rsp+16 => rbp=[CFA-16] rip=[CFA-8] -# CHECK: row[2]: 7: CFA=rbp+16 => rbp=[CFA-16] rip=[CFA-8] -# CHECK: row[3]: 26: CFA=rsp +8 => rbp=[CFA-16] rip=[CFA-8] +# CHECK: row[2]: 11: CFA=rbp+16 => rbp=[CFA-16] rip=[CFA-8] +# CHECK: row[3]: 30: CFA=rsp +8 => rbp=[CFA-16] rip=[CFA-8] print/x $r12 # CHECK: (unsigned long) 0x0000000000000456 @@ -55,4 +55,4 @@ print/x $r12 frame select 2 print/x $r12 -# CHECK: (unsigned long) 0x[[#R12]] +# CHECK: (unsigned long) 0x[[#R12 + 32]] _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits