Skip to content

Commit c7c3217

Browse files
Merge pull request #10637 from felipepiovezan/felipe/frame_formation_based_unwinding
[lldb][swift] Use frame formation as a guide for async unwinding
2 parents 4b86c62 + d1ac271 commit c7c3217

File tree

1 file changed

+127
-65
lines changed

1 file changed

+127
-65
lines changed

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

Lines changed: 127 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,14 +2474,21 @@ static llvm::Expected<addr_t> ReadPtrFromAddr(Process &process, addr_t addr,
24742474
/// simplified version of the methods in RegisterContextUnwind, since plumbing
24752475
/// access to those here would be challenging.
24762476
static llvm::Expected<addr_t> GetCFA(Process &process, RegisterContext &regctx,
2477-
RegisterKind regkind,
2478-
UnwindPlan::Row::FAValue cfa_loc) {
2477+
addr_t pc_offset,
2478+
UnwindPlan &unwind_plan) {
2479+
UnwindPlan::RowSP row = unwind_plan.GetRowForFunctionOffset(pc_offset);
2480+
if (!row)
2481+
return llvm::createStringError(
2482+
"SwiftLanguageRuntime: Invalid Unwind Row when computing CFA");
2483+
2484+
UnwindPlan::Row::FAValue cfa_loc = row->GetCFAValue();
2485+
24792486
using ValueType = UnwindPlan::Row::FAValue::ValueType;
24802487
switch (cfa_loc.GetValueType()) {
24812488
case ValueType::isRegisterPlusOffset: {
24822489
unsigned regnum = cfa_loc.GetRegisterNumber();
2483-
if (llvm::Expected<addr_t> regvalue =
2484-
ReadRegisterAsAddress(regctx, regkind, regnum))
2490+
if (llvm::Expected<addr_t> regvalue = ReadRegisterAsAddress(
2491+
regctx, unwind_plan.GetRegisterKind(), regnum))
24852492
return *regvalue + cfa_loc.GetOffset();
24862493
else
24872494
return regvalue;
@@ -2513,13 +2520,8 @@ static UnwindPlanSP GetUnwindPlanForAsyncRegister(FuncUnwinders &unwinders,
25132520
return unwinders.GetUnwindPlanAtNonCallSite(target, thread);
25142521
}
25152522

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

2531-
Target &target = process.GetTarget();
2532-
UnwindPlanSP unwind_plan =
2533-
GetUnwindPlanForAsyncRegister(*unwinders, target, regctx.GetThread());
2533+
UnwindPlanSP unwind_plan = GetUnwindPlanForAsyncRegister(
2534+
*unwinders, thread.GetProcess()->GetTarget(), thread);
25342535
if (!unwind_plan)
25352536
return llvm::createStringError(
25362537
"SwiftLanguageRuntime: Failed to find non call site unwind plan at "
25372538
"address 0x%8.8" PRIx64,
25382539
pc.GetFileAddress());
2540+
return unwind_plan;
2541+
}
25392542

2540-
const RegisterKind unwind_regkind = unwind_plan->GetRegisterKind();
2541-
UnwindPlan::RowSP row = unwind_plan->GetRowForFunctionOffset(
2542-
pc.GetFileAddress() - func_start_addr.GetFileAddress());
2543-
2544-
// To request info about a register from the unwind plan, the register must
2545-
// be in the same domain as the unwind plan's registers.
2546-
uint32_t async_reg_unwind_regdomain;
2543+
static llvm::Expected<uint32_t> GetFpRegisterNumber(UnwindPlan &unwind_plan,
2544+
RegisterContext &regctx) {
2545+
uint32_t fp_unwind_regdomain;
25472546
if (!regctx.ConvertBetweenRegisterKinds(
2548-
regnums.GetRegisterKind(), regnums.async_ctx_regnum, unwind_regkind,
2549-
async_reg_unwind_regdomain)) {
2547+
lldb::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP,
2548+
unwind_plan.GetRegisterKind(), fp_unwind_regdomain)) {
25502549
// This should never happen.
25512550
// If asserts are disabled, return an error to avoid creating an invalid
25522551
// unwind plan.
2553-
auto error_msg = "SwiftLanguageRuntime: Failed to convert register domains";
2552+
const auto *error_msg =
2553+
"SwiftLanguageRuntime: Failed to convert register domains";
25542554
llvm_unreachable(error_msg);
25552555
return llvm::createStringError(error_msg);
25562556
}
2557+
return fp_unwind_regdomain;
2558+
}
25572559

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

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

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

0 commit comments

Comments
 (0)