Skip to content

Commit 55d4f57

Browse files
Merge pull request #10687 from felipepiovezan/felipe/cherry-pick-frame-formation
[cherry-pick][lldb][swift] Use frame formation as a guide for async unwinding
2 parents b432b54 + 5372a6f commit 55d4f57

File tree

1 file changed

+126
-65
lines changed

1 file changed

+126
-65
lines changed

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

Lines changed: 126 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2480,14 +2480,21 @@ static llvm::Expected<addr_t> ReadPtrFromAddr(Process &process, addr_t addr,
24802480
/// simplified version of the methods in RegisterContextUnwind, since plumbing
24812481
/// access to those here would be challenging.
24822482
static llvm::Expected<addr_t> GetCFA(Process &process, RegisterContext &regctx,
2483-
RegisterKind regkind,
2484-
UnwindPlan::Row::FAValue cfa_loc) {
2483+
addr_t pc_offset,
2484+
UnwindPlan &unwind_plan) {
2485+
auto *row = unwind_plan.GetRowForFunctionOffset(pc_offset);
2486+
if (!row)
2487+
return llvm::createStringError(
2488+
"SwiftLanguageRuntime: Invalid Unwind Row when computing CFA");
2489+
2490+
UnwindPlan::Row::FAValue cfa_loc = row->GetCFAValue();
2491+
24852492
using ValueType = UnwindPlan::Row::FAValue::ValueType;
24862493
switch (cfa_loc.GetValueType()) {
24872494
case ValueType::isRegisterPlusOffset: {
24882495
unsigned regnum = cfa_loc.GetRegisterNumber();
2489-
if (llvm::Expected<addr_t> regvalue =
2490-
ReadRegisterAsAddress(regctx, regkind, regnum))
2496+
if (llvm::Expected<addr_t> regvalue = ReadRegisterAsAddress(
2497+
regctx, unwind_plan.GetRegisterKind(), regnum))
24912498
return *regvalue + cfa_loc.GetOffset();
24922499
else
24932500
return regvalue;
@@ -2519,13 +2526,8 @@ static UnwindPlanSP GetUnwindPlanForAsyncRegister(FuncUnwinders &unwinders,
25192526
return unwinders.GetUnwindPlanAtNonCallSite(target, thread);
25202527
}
25212528

2522-
/// Attempts to use UnwindPlans that inspect assembly to recover the entry value
2523-
/// of the async context register. This is a simplified version of the methods
2524-
/// in RegisterContextUnwind, since plumbing access to those here would be
2525-
/// challenging.
2526-
static llvm::Expected<addr_t> ReadAsyncContextRegisterFromUnwind(
2527-
SymbolContext &sc, Process &process, Address pc, Address func_start_addr,
2528-
RegisterContext &regctx, AsyncUnwindRegisterNumbers regnums) {
2529+
static llvm::Expected<UnwindPlanSP>
2530+
GetAsmUnwindPlan(Address pc, SymbolContext &sc, Thread &thread) {
25292531
FuncUnwindersSP unwinders =
25302532
pc.GetModule()->GetUnwindTable().GetFuncUnwindersContainingAddress(pc,
25312533
sc);
@@ -2534,77 +2536,136 @@ static llvm::Expected<addr_t> ReadAsyncContextRegisterFromUnwind(
25342536
"function unwinder at address 0x%8.8" PRIx64,
25352537
pc.GetFileAddress());
25362538

2537-
Target &target = process.GetTarget();
2538-
UnwindPlanSP unwind_plan =
2539-
GetUnwindPlanForAsyncRegister(*unwinders, target, regctx.GetThread());
2539+
UnwindPlanSP unwind_plan = GetUnwindPlanForAsyncRegister(
2540+
*unwinders, thread.GetProcess()->GetTarget(), thread);
25402541
if (!unwind_plan)
25412542
return llvm::createStringError(
25422543
"SwiftLanguageRuntime: Failed to find non call site unwind plan at "
25432544
"address 0x%8.8" PRIx64,
25442545
pc.GetFileAddress());
2546+
return unwind_plan;
2547+
}
25452548

2546-
const RegisterKind unwind_regkind = unwind_plan->GetRegisterKind();
2547-
auto *row = unwind_plan->GetRowForFunctionOffset(
2548-
pc.GetFileAddress() - func_start_addr.GetFileAddress());
2549-
2550-
// To request info about a register from the unwind plan, the register must
2551-
// be in the same domain as the unwind plan's registers.
2552-
uint32_t async_reg_unwind_regdomain;
2549+
static llvm::Expected<uint32_t> GetFpRegisterNumber(UnwindPlan &unwind_plan,
2550+
RegisterContext &regctx) {
2551+
uint32_t fp_unwind_regdomain;
25532552
if (!regctx.ConvertBetweenRegisterKinds(
2554-
regnums.GetRegisterKind(), regnums.async_ctx_regnum, unwind_regkind,
2555-
async_reg_unwind_regdomain)) {
2553+
lldb::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
2554+
unwind_plan.GetRegisterKind(), fp_unwind_regdomain)) {
25562555
// This should never happen.
25572556
// If asserts are disabled, return an error to avoid creating an invalid
25582557
// unwind plan.
2559-
auto error_msg = "SwiftLanguageRuntime: Failed to convert register domains";
2558+
const auto *error_msg =
2559+
"SwiftLanguageRuntime: Failed to convert register domains";
25602560
llvm_unreachable(error_msg);
25612561
return llvm::createStringError(error_msg);
25622562
}
2563+
return fp_unwind_regdomain;
2564+
}
25632565

2564-
// If the plan doesn't have information about the async register, we can use
2565-
// its current value, as this is a callee saved register.
2566-
UnwindPlan::Row::AbstractRegisterLocation regloc;
2567-
if (!row->GetRegisterInfo(async_reg_unwind_regdomain, regloc))
2568-
return ReadRegisterAsAddress(regctx, regnums.GetRegisterKind(),
2569-
regnums.async_ctx_regnum);
2566+
struct FrameSetupInfo {
2567+
addr_t frame_setup_func_offset;
2568+
int fp_cfa_offset;
2569+
};
25702570

2571-
// Handle the few abstract locations we are likely to encounter.
2572-
using RestoreType = UnwindPlan::Row::AbstractRegisterLocation::RestoreType;
2573-
RestoreType loctype = regloc.GetLocationType();
2574-
switch (loctype) {
2575-
case RestoreType::same:
2571+
/// Detect the point in the function where the prologue created a frame,
2572+
/// returning:
2573+
/// 1. The offset of the first instruction after that point. For a frameless
2574+
/// function, this offset is large positive number, so that PC can still be
2575+
/// compared against it.
2576+
/// 2. The CFA offset at which FP is stored, meaningless in the frameless case.
2577+
static llvm::Expected<FrameSetupInfo>
2578+
GetFrameSetupInfo(UnwindPlan &unwind_plan, RegisterContext &regctx) {
2579+
using AbstractRegisterLocation = UnwindPlan::Row::AbstractRegisterLocation;
2580+
2581+
llvm::Expected<uint32_t> fp_unwind_regdomain =
2582+
GetFpRegisterNumber(unwind_plan, regctx);
2583+
if (!fp_unwind_regdomain)
2584+
return fp_unwind_regdomain.takeError();
2585+
2586+
// Look at the first few (4) rows of the plan and store FP's location.
2587+
const int upper_bound = std::min(4, unwind_plan.GetRowCount());
2588+
llvm::SmallVector<AbstractRegisterLocation, 4> fp_locs;
2589+
for (int row_idx = 0; row_idx < upper_bound; row_idx++) {
2590+
auto *row = unwind_plan.GetRowAtIndex(row_idx);
2591+
AbstractRegisterLocation regloc;
2592+
if (!row->GetRegisterInfo(*fp_unwind_regdomain, regloc))
2593+
regloc.SetSame();
2594+
fp_locs.push_back(regloc);
2595+
}
2596+
2597+
// Find first location where FP is stored *at* some CFA offset.
2598+
auto *it = llvm::find_if(
2599+
fp_locs, [](auto fp_loc) { return fp_loc.IsAtCFAPlusOffset(); });
2600+
2601+
// This is a frameless function, use large positive offset so that a PC can
2602+
// still be compared against it.
2603+
if (it == fp_locs.end())
2604+
return FrameSetupInfo{std::numeric_limits<addr_t>::max(), 0};
2605+
2606+
// This is an async function with a frame. The prologue roughly follows this
2607+
// sequence of instructions:
2608+
// adjust sp
2609+
// save lr @ CFA-8
2610+
// save fp @ CFA-16 << `it` points to this row.
2611+
// save async_reg @ CFA-24 << subsequent row.
2612+
// Use subsequent row, if available.
2613+
// Pointer auth may introduce more instructions, but they don't affect the
2614+
// unwinder rows / store to the stack.
2615+
int row_idx = fp_locs.end() - it;
2616+
int next_row_idx = row_idx + 1;
2617+
2618+
// If subsequent row is invalid, approximate through current row.
2619+
if (next_row_idx == unwind_plan.GetRowCount() ||
2620+
next_row_idx == upper_bound ||
2621+
!fp_locs[next_row_idx].IsAtCFAPlusOffset()) {
2622+
LLDB_LOG(GetLog(LLDBLog::Unwind), "SwiftLanguageRuntime:: UnwindPlan did "
2623+
"not contain a valid row after FP setup");
2624+
auto *row = unwind_plan.GetRowAtIndex(row_idx);
2625+
return FrameSetupInfo{row->GetOffset(), fp_locs[row_idx].GetOffset()};
2626+
}
2627+
2628+
auto *subsequent_row = unwind_plan.GetRowAtIndex(next_row_idx);
2629+
return FrameSetupInfo{subsequent_row->GetOffset(),
2630+
fp_locs[next_row_idx].GetOffset()};
2631+
}
2632+
2633+
/// Reads the async register from its ABI-guaranteed stack-slot, or directly
2634+
/// from the register depending on where pc is relative to the start of the
2635+
/// function.
2636+
static llvm::Expected<addr_t> ReadAsyncContextRegisterFromUnwind(
2637+
SymbolContext &sc, Process &process, Address pc, Address func_start_addr,
2638+
RegisterContext &regctx, AsyncUnwindRegisterNumbers regnums) {
2639+
llvm::Expected<UnwindPlanSP> unwind_plan =
2640+
GetAsmUnwindPlan(pc, sc, regctx.GetThread());
2641+
if (!unwind_plan)
2642+
return unwind_plan.takeError();
2643+
llvm::Expected<FrameSetupInfo> frame_setup =
2644+
GetFrameSetupInfo(**unwind_plan, regctx);
2645+
if (!frame_setup)
2646+
return frame_setup.takeError();
2647+
2648+
// Is PC before the frame formation? If so, use async register directly.
2649+
// This handles frameless functions, as frame_setup_func_offset is INT_MAX.
2650+
addr_t pc_offset = pc.GetFileAddress() - func_start_addr.GetFileAddress();
2651+
if (pc_offset < frame_setup->frame_setup_func_offset)
25762652
return ReadRegisterAsAddress(regctx, regnums.GetRegisterKind(),
25772653
regnums.async_ctx_regnum);
2578-
case RestoreType::inOtherRegister: {
2579-
unsigned regnum = regloc.GetRegisterNumber();
2580-
return ReadRegisterAsAddress(regctx, unwind_regkind, regnum);
2581-
}
2582-
case RestoreType::atCFAPlusOffset: {
2583-
llvm::Expected<addr_t> cfa =
2584-
GetCFA(process, regctx, unwind_regkind, row->GetCFAValue());
2585-
if (!cfa)
2586-
return cfa.takeError();
2587-
return ReadPtrFromAddr(process, *cfa, regloc.GetOffset());
2588-
}
2589-
case RestoreType::isCFAPlusOffset: {
2590-
if (llvm::Expected<addr_t> cfa =
2591-
GetCFA(process, regctx, unwind_regkind, row->GetCFAValue()))
2592-
return *cfa + regloc.GetOffset();
2593-
else
2594-
return cfa;
2595-
}
2596-
case RestoreType::isConstant:
2597-
return regloc.GetConstant();
2598-
case RestoreType::unspecified:
2599-
case RestoreType::undefined:
2600-
case RestoreType::atAFAPlusOffset:
2601-
case RestoreType::isAFAPlusOffset:
2602-
case RestoreType::isDWARFExpression:
2603-
case RestoreType::atDWARFExpression:
2604-
break;
2605-
}
2606-
return llvm::createStringError(
2607-
"SwiftLanguageRuntime: Unsupported register location type = %d", loctype);
2654+
2655+
// A frame was formed, and FP was saved at a CFA offset. Compute CFA and read
2656+
// the location beneath where FP was saved.
2657+
llvm::Expected<addr_t> cfa =
2658+
GetCFA(process, regctx, pc_offset, **unwind_plan);
2659+
if (!cfa)
2660+
return cfa.takeError();
2661+
2662+
addr_t async_reg_addr = process.FixDataAddress(
2663+
*cfa + frame_setup->fp_cfa_offset - process.GetAddressByteSize());
2664+
Status error;
2665+
addr_t async_reg = process.ReadPointerFromMemory(async_reg_addr, error);
2666+
if (error.Fail())
2667+
return error.ToError();
2668+
return async_reg;
26082669
}
26092670

26102671
/// Returns true if the async register should be dereferenced once to obtain the

0 commit comments

Comments
 (0)