Skip to content

Commit 84cd0d3

Browse files
authored
[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.
1 parent 7851b1b commit 84cd0d3

File tree

6 files changed

+27
-9
lines changed

6 files changed

+27
-9
lines changed

lldb/source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ bool UnwindAssembly_x86::AugmentUnwindPlanFromCallSite(
7373

7474
int wordsize = 8;
7575
ProcessSP process_sp(thread.GetProcess());
76-
if (process_sp.get() == nullptr)
76+
if (!process_sp || !first_row || !last_row)
7777
return false;
7878

7979
wordsize = process_sp->GetTarget().GetArchitecture().GetAddressByteSize();

lldb/source/Symbol/DWARFCallFrameInfo.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,12 @@ bool DWARFCallFrameInfo::GetUnwindPlan(const AddressRange &range,
190190
unwind_plan.SetUnwindPlanForSignalTrap(fde->for_signal_trap ? eLazyBoolYes
191191
: eLazyBoolNo);
192192
unwind_plan.SetReturnAddressRegister(fde->return_addr_reg_num);
193-
for (UnwindPlan::Row &row : fde->rows)
193+
int64_t slide =
194+
fde->range.GetBaseAddress().GetFileAddress() - addr.GetFileAddress();
195+
for (UnwindPlan::Row &row : fde->rows) {
196+
row.SlideOffset(slide);
194197
unwind_plan.AppendRow(std::move(row));
198+
}
195199

196200
return true;
197201
}

lldb/source/Symbol/UnwindPlan.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ bool UnwindPlan::PlanValidAtAddress(Address addr) const {
474474
// If the 0th Row of unwind instructions is missing, or if it doesn't provide
475475
// a register to use to find the Canonical Frame Address, this is not a valid
476476
// UnwindPlan.
477-
const Row *row0 = GetRowForFunctionOffset(0);
477+
const Row *row0 = GetRowAtIndex(0);
478478
if (!row0 ||
479479
row0->GetCFAValue().GetValueType() == Row::FAValue::unspecified) {
480480
Log *log = GetLog(LLDBLog::Unwind);

lldb/test/Shell/Unwind/Inputs/eh-frame-small-fde.s

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ bar:
1010

1111
.type foo, @function
1212
foo:
13-
nop # Make the FDE entry start one byte later than the actual function.
13+
# Make the FDE entry start 16 bytes later than the actual function. The
14+
# size is chosen such that it is larger than the size of the FDE entry.
15+
# This allows us to test that we're using the correct offset for
16+
# unwinding (we'll stop 21 bytes into the function, but only 5 bytes
17+
# into the FDE).
18+
.nops 16
1419
.cfi_startproc
1520
.cfi_register %rip, %r13
1621
call bar
1722
addl $1, %eax
18-
jmp *%r13 # Return
23+
movq %r13, %r14
24+
.cfi_register %rip, %r14
25+
movq $0, %r13
26+
jmp *%r14 # Return
1927
.cfi_endproc
2028
.size foo, .-foo
2129

lldb/test/Shell/Unwind/eh-frame-small-fde.test

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ process launch
1414

1515
thread backtrace
1616
# CHECK: frame #0: {{.*}}`bar
17-
# CHECK: frame #1: {{.*}}`foo + 6
17+
# CHECK: frame #1: {{.*}}`foo + 21
1818
# CHECK: frame #2: {{.*}}`main + 20
1919

2020
target modules show-unwind -n foo
21-
# CHECK: eh_frame UnwindPlan:
22-
# CHECK: row[0]: 0: CFA=rsp +8 => rip=r13
21+
# CHECK: eh_frame UnwindPlan:
22+
# CHECK: row[0]: 16: CFA=rsp +8 => rip=r13
23+
# CHECK-NEXT: row[1]: 27: CFA=rsp +8 => rip=r14

lldb/unittests/Symbol/UnwindPlanTest.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,20 @@ TEST(UnwindPlan, PlanValidAtAddress) {
6262
UnwindPlan::Row row2 = make_simple_row(10, 47);
6363

6464
UnwindPlan plan(eRegisterKindGeneric);
65+
// When valid address range is not set, plans are valid as long as they have a
66+
// row that sets the CFA.
6567
EXPECT_FALSE(plan.PlanValidAtAddress(Address(0)));
68+
EXPECT_FALSE(plan.PlanValidAtAddress(Address(10)));
6669

6770
plan.InsertRow(row2);
68-
EXPECT_FALSE(plan.PlanValidAtAddress(Address(0)));
71+
EXPECT_TRUE(plan.PlanValidAtAddress(Address(0)));
72+
EXPECT_TRUE(plan.PlanValidAtAddress(Address(10)));
6973

7074
plan.InsertRow(row1);
7175
EXPECT_TRUE(plan.PlanValidAtAddress(Address(0)));
7276
EXPECT_TRUE(plan.PlanValidAtAddress(Address(10)));
7377

78+
// With an address range, they're only valid within that range.
7479
plan.SetPlanValidAddressRanges({AddressRange(0, 5), AddressRange(15, 5)});
7580
EXPECT_TRUE(plan.PlanValidAtAddress(Address(0)));
7681
EXPECT_FALSE(plan.PlanValidAtAddress(Address(5)));

0 commit comments

Comments
 (0)