Skip to content

Commit 74f18b3

Browse files
Merge pull request #10671 from felipepiovezan/felipe/arm64e_unwind_heuristic
[lldb][swift] Improve async stepping with arm64e
2 parents b372ced + 6e361c7 commit 74f18b3

File tree

4 files changed

+58
-37
lines changed

4 files changed

+58
-37
lines changed

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,20 +2669,48 @@ static llvm::Expected<addr_t> ReadAsyncContextRegisterFromUnwind(
26692669
return async_reg;
26702670
}
26712671

2672+
static llvm::Expected<bool>
2673+
DoesContinuationPointToSameFunction(addr_t async_reg, SymbolContext &sc,
2674+
Process &process) {
2675+
llvm::Expected<addr_t> continuation_ptr = ReadPtrFromAddr(
2676+
process, async_reg, /*offset*/ process.GetAddressByteSize());
2677+
if (!continuation_ptr)
2678+
return continuation_ptr.takeError();
2679+
2680+
Address continuation_addr;
2681+
continuation_addr.SetLoadAddress(process.FixCodeAddress(*continuation_ptr),
2682+
&process.GetTarget());
2683+
if (sc.function)
2684+
return sc.function->GetAddressRange().ContainsLoadAddress(
2685+
continuation_addr, &process.GetTarget());
2686+
assert(sc.symbol);
2687+
return sc.symbol->ContainsFileAddress(continuation_addr.GetFileAddress());
2688+
}
2689+
26722690
/// Returns true if the async register should be dereferenced once to obtain the
26732691
/// CFA of the currently executing function. This is the case at the start of
26742692
/// "Q" funclets, before the low level code changes the meaning of the async
26752693
/// register to not require the indirection.
2676-
/// The end of the prologue approximates the transition point.
2694+
/// The end of the prologue approximates the transition point well in non-arm64e
2695+
/// targets.
26772696
/// FIXME: In the few instructions between the end of the prologue and the
26782697
/// transition point, this approximation fails. rdar://139676623
26792698
static llvm::Expected<bool> IsIndirectContext(Process &process,
26802699
StringRef mangled_name,
2681-
Address pc, SymbolContext &sc) {
2700+
Address pc, SymbolContext &sc,
2701+
addr_t async_reg) {
26822702
if (!SwiftLanguageRuntime::IsSwiftAsyncAwaitResumePartialFunctionSymbol(
26832703
mangled_name))
26842704
return false;
26852705

2706+
// For arm64e, pointer authentication generates branches that cause stepping
2707+
// algorithms to stop & unwind in more places. The "end of the prologue"
2708+
// approximation fails in those; instead, check whether the continuation
2709+
// pointer still points to the currently executing function. This works for
2710+
// all instructions, but fails when direct recursion is involved.
2711+
if (process.GetTarget().GetArchitecture().GetTriple().isArm64e())
2712+
return DoesContinuationPointToSameFunction(async_reg, sc, process);
2713+
26862714
// This is checked prior to calling this function.
26872715
assert(sc.function || sc.symbol);
26882716
uint32_t prologue_size = sc.function ? sc.function->GetPrologueByteSize()
@@ -2765,7 +2793,7 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
27652793
return log_expected(async_reg.takeError());
27662794

27672795
llvm::Expected<bool> maybe_indirect_context =
2768-
IsIndirectContext(*process_sp, mangled_name, pc, sc);
2796+
IsIndirectContext(*process_sp, mangled_name, pc, sc, *async_reg);
27692797
if (!maybe_indirect_context)
27702798
return log_expected(maybe_indirect_context.takeError());
27712799

lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntimeNames.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -379,17 +379,7 @@ CreateRunThroughTaskSwitchThreadPlan(Thread &thread,
379379
if (!resume_fn_ptr)
380380
return {};
381381

382-
auto arch = reg_ctx->CalculateTarget()->GetArchitecture();
383-
std::optional<AsyncUnwindRegisterNumbers> async_regs =
384-
GetAsyncUnwindRegisterNumbers(arch.GetMachine());
385-
if (!async_regs)
386-
return {};
387-
unsigned async_reg_number = reg_ctx->ConvertRegisterKindToRegisterNumber(
388-
async_regs->GetRegisterKind(), async_regs->async_ctx_regnum);
389-
uint64_t async_ctx = reg_ctx->ReadRegisterAsUnsigned(async_reg_number, 0);
390-
if (!async_ctx)
391-
return {};
392-
382+
resume_fn_ptr = thread.GetProcess()->FixCodeAddress(resume_fn_ptr);
393383
return std::make_shared<ThreadPlanRunToAddress>(thread, resume_fn_ptr,
394384
/*stop_others*/ false);
395385
}

lldb/source/Target/StackID.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) {
8383
static llvm::Expected<bool> IsReachableParent(lldb::addr_t source,
8484
lldb::addr_t maybe_parent,
8585
Process &process) {
86+
maybe_parent = process.FixDataAddress(maybe_parent);
8687
auto max_num_frames = 512;
8788
for (lldb::addr_t parent_ctx = source; parent_ctx && max_num_frames;
8889
max_num_frames--) {
@@ -94,7 +95,7 @@ static llvm::Expected<bool> IsReachableParent(lldb::addr_t source,
9495
return llvm::createStringError(llvm::formatv(
9596
"Failed to read parent async context of: {0:x}. Error: {1}",
9697
old_parent_ctx, error.AsCString()));
97-
if (parent_ctx == maybe_parent)
98+
if (process.FixDataAddress(parent_ctx) == maybe_parent)
9899
return true;
99100
}
100101
if (max_num_frames == 0)

lldb/unittests/StackID/StackIDTest.cpp

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -76,25 +76,27 @@ struct MockStackID : StackID {
7676
};
7777

7878
TEST_F(StackIDTest, StackStackCFAComparison) {
79-
auto process = MockProcess(m_target_sp, Listener::MakeListener("dummy"));
79+
auto process = std::make_shared<MockProcess>(m_target_sp,
80+
Listener::MakeListener("dummy"));
8081

8182
MockStackID small_cfa_on_stack(/*cfa*/ 10, OnStack::Yes);
8283
MockStackID big_cfa_on_stack(/*cfa*/ 100, OnStack::Yes);
8384

8485
EXPECT_TRUE(
85-
StackID::IsYounger(small_cfa_on_stack, big_cfa_on_stack, process));
86+
StackID::IsYounger(small_cfa_on_stack, big_cfa_on_stack, *process));
8687
EXPECT_FALSE(
87-
StackID::IsYounger(big_cfa_on_stack, small_cfa_on_stack, process));
88+
StackID::IsYounger(big_cfa_on_stack, small_cfa_on_stack, *process));
8889
}
8990

9091
TEST_F(StackIDTest, StackHeapCFAComparison) {
91-
auto process = MockProcess(m_target_sp, Listener::MakeListener("dummy"));
92+
auto process = std::make_shared<MockProcess>(m_target_sp,
93+
Listener::MakeListener("dummy"));
9294

9395
MockStackID cfa_on_stack(/*cfa*/ 100, OnStack::Yes);
9496
MockStackID cfa_on_heap(/*cfa*/ 10, OnStack::No);
9597

96-
EXPECT_TRUE(StackID::IsYounger(cfa_on_stack, cfa_on_heap, process));
97-
EXPECT_FALSE(StackID::IsYounger(cfa_on_heap, cfa_on_stack, process));
98+
EXPECT_TRUE(StackID::IsYounger(cfa_on_stack, cfa_on_heap, *process));
99+
EXPECT_FALSE(StackID::IsYounger(cfa_on_heap, cfa_on_stack, *process));
98100
}
99101

100102
TEST_F(StackIDTest, HeapHeapCFAComparison) {
@@ -107,21 +109,21 @@ TEST_F(StackIDTest, HeapHeapCFAComparison) {
107109
memory_map[100] = 108;
108110
memory_map[108] = 116;
109111
memory_map[116] = 0;
110-
auto process = MockProcess(m_target_sp, Listener::MakeListener("dummy"),
111-
std::move(memory_map));
112+
auto process = std::make_shared<MockProcess>(
113+
m_target_sp, Listener::MakeListener("dummy"), std::move(memory_map));
112114

113115
MockStackID oldest_cfa(/*cfa*/ 116, OnStack::No);
114116
MockStackID middle_cfa(/*cfa*/ 108, OnStack::No);
115117
MockStackID youngest_cfa(/*cfa*/ 100, OnStack::No);
116118

117-
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, oldest_cfa, process));
118-
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, youngest_cfa, process));
119+
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, oldest_cfa, *process));
120+
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, youngest_cfa, *process));
119121

120-
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, middle_cfa, process));
121-
EXPECT_FALSE(StackID::IsYounger(middle_cfa, youngest_cfa, process));
122+
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, middle_cfa, *process));
123+
EXPECT_FALSE(StackID::IsYounger(middle_cfa, youngest_cfa, *process));
122124

123-
EXPECT_TRUE(StackID::IsYounger(middle_cfa, oldest_cfa, process));
124-
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, middle_cfa, process));
125+
EXPECT_TRUE(StackID::IsYounger(middle_cfa, oldest_cfa, *process));
126+
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, middle_cfa, *process));
125127
}
126128

127129
TEST_F(StackIDTest, HeapHeapCFAComparisonDecreasing) {
@@ -134,19 +136,19 @@ TEST_F(StackIDTest, HeapHeapCFAComparisonDecreasing) {
134136
memory_map[100] = 90;
135137
memory_map[90] = 80;
136138
memory_map[80] = 0;
137-
auto process = MockProcess(m_target_sp, Listener::MakeListener("dummy"),
138-
std::move(memory_map));
139+
auto process = std::make_shared<MockProcess>(
140+
m_target_sp, Listener::MakeListener("dummy"), std::move(memory_map));
139141

140142
MockStackID oldest_cfa(/*cfa*/ 80, OnStack::No);
141143
MockStackID middle_cfa(/*cfa*/ 90, OnStack::No);
142144
MockStackID youngest_cfa(/*cfa*/ 100, OnStack::No);
143145

144-
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, oldest_cfa, process));
145-
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, youngest_cfa, process));
146+
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, oldest_cfa, *process));
147+
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, youngest_cfa, *process));
146148

147-
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, middle_cfa, process));
148-
EXPECT_FALSE(StackID::IsYounger(middle_cfa, youngest_cfa, process));
149+
EXPECT_TRUE(StackID::IsYounger(youngest_cfa, middle_cfa, *process));
150+
EXPECT_FALSE(StackID::IsYounger(middle_cfa, youngest_cfa, *process));
149151

150-
EXPECT_TRUE(StackID::IsYounger(middle_cfa, oldest_cfa, process));
151-
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, middle_cfa, process));
152+
EXPECT_TRUE(StackID::IsYounger(middle_cfa, oldest_cfa, *process));
153+
EXPECT_FALSE(StackID::IsYounger(oldest_cfa, middle_cfa, *process));
152154
}

0 commit comments

Comments
 (0)