[lldb] Slide eh_frame unwind plan if it doesn't begin at function boundary (#135333)

This is mainly useful for discontinuous functions because individual
parts of the function will have separate FDE entries, which can begin
many megabytes from the start of the function. However, I'm separating
it out, because it turns out we already have a test case for the
situation where the FDE does not begin exactly at the function boundary.

The test works mostly by accident because the FDE starts only one byte
after the beginning of the function so it doesn't really matter whether
one looks up the unwind row using the function or fde offset. In this
patch, I beef up the test to catch this problem more reliably.

To make this work I've also needed to change a couple of places which
that an unwind plan always has a row at offset zero.
This commit is contained in:
Pavel Labath
2025-04-22 13:53:16 +02:00
committed by GitHub
parent 7851b1bcf1
commit 84cd0d3c38
6 changed files with 27 additions and 9 deletions

View File

@@ -73,7 +73,7 @@ bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite(
int wordsize = 8;
ProcessSP process_sp(thread.GetProcess());
if (process_sp.get() == nullptr)
if (!process_sp || !first_row || !last_row)
return false;
wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();

View File

@@ -190,8 +190,12 @@ bool DWARFCallFrameInfo::GetUnwindPlan(const AddressRange &range,
unwind_plan.SetUnwindPlanForSignalTrap(fde->for_signal_trap ? eLazyBoolYes
: eLazyBoolNo);
unwind_plan.SetReturnAddressRegister(fde->return_addr_reg_num);
for (UnwindPlan::Row &row : fde->rows)
int64_t slide =
fde->range.GetBaseAddress().GetFileAddress() - addr.GetFileAddress();
for (UnwindPlan::Row &row : fde->rows) {
row.SlideOffset(slide);
unwind_plan.AppendRow(std::move(row));
}
return true;
}

View File

@@ -474,7 +474,7 @@ bool UnwindPlan::PlanValidAtAddress(Address addr) const {
// If the 0th Row of unwind instructions is missing, or if it doesn't provide
// a register to use to find the Canonical Frame Address, this is not a valid
// UnwindPlan.
const Row *row0 = GetRowForFunctionOffset(0);
const Row *row0 = GetRowAtIndex(0);
if (!row0 ||
row0->GetCFAValue().GetValueType() == Row::FAValue::unspecified) {
Log *log = GetLog(LLDBLog::Unwind);

View File

@@ -10,12 +10,20 @@ bar:
.type foo, @function
foo:
nop # Make the FDE entry start one byte later than the actual function.
# Make the FDE entry start 16 bytes later than the actual function. The
# size is chosen such that it is larger than the size of the FDE entry.
# This allows us to test that we're using the correct offset for
# unwinding (we'll stop 21 bytes into the function, but only 5 bytes
# into the FDE).
.nops 16
.cfi_startproc
.cfi_register %rip, %r13
call bar
addl $1, %eax
jmp *%r13 # Return
movq %r13, %r14
.cfi_register %rip, %r14
movq $0, %r13
jmp *%r14 # Return
.cfi_endproc
.size foo, .-foo

View File

@@ -14,9 +14,10 @@ process launch
thread backtrace
# CHECK: frame #0: {{.*}}`bar
# CHECK: frame #1: {{.*}}`foo + 6
# CHECK: frame #1: {{.*}}`foo + 21
# CHECK: frame #2: {{.*}}`main + 20
target modules show-unwind -n foo
# CHECK: eh_frame UnwindPlan:
# CHECK: row[0]: 0: CFA=rsp +8 => rip=r13
# CHECK: eh_frame UnwindPlan:
# CHECK: row[0]: 16: CFA=rsp +8 => rip=r13
# CHECK-NEXT: row[1]: 27: CFA=rsp +8 => rip=r14

View File

@@ -62,15 +62,20 @@ TEST(UnwindPlan, PlanValidAtAddress) {
UnwindPlan::Row row2 = make_simple_row(10, 47);
UnwindPlan plan(eRegisterKindGeneric);
// When valid address range is not set, plans are valid as long as they have a
// row that sets the CFA.
EXPECT_FALSE(plan.PlanValidAtAddress(Address(0)));
EXPECT_FALSE(plan.PlanValidAtAddress(Address(10)));
plan.InsertRow(row2);
EXPECT_FALSE(plan.PlanValidAtAddress(Address(0)));
EXPECT_TRUE(plan.PlanValidAtAddress(Address(0)));
EXPECT_TRUE(plan.PlanValidAtAddress(Address(10)));
plan.InsertRow(row1);
EXPECT_TRUE(plan.PlanValidAtAddress(Address(0)));
EXPECT_TRUE(plan.PlanValidAtAddress(Address(10)));
// With an address range, they're only valid within that range.
plan.SetPlanValidAddressRanges({AddressRange(0, 5), AddressRange(15, 5)});
EXPECT_TRUE(plan.PlanValidAtAddress(Address(0)));
EXPECT_FALSE(plan.PlanValidAtAddress(Address(5)));