@@ -2474,14 +2474,21 @@ static llvm::Expected<addr_t> ReadPtrFromAddr(Process &process, addr_t addr,
2474
2474
// / simplified version of the methods in RegisterContextUnwind, since plumbing
2475
2475
// / access to those here would be challenging.
2476
2476
static llvm::Expected<addr_t > GetCFA (Process &process, RegisterContext ®ctx,
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
+
2479
2486
using ValueType = UnwindPlan::Row::FAValue::ValueType;
2480
2487
switch (cfa_loc.GetValueType ()) {
2481
2488
case ValueType::isRegisterPlusOffset: {
2482
2489
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))
2485
2492
return *regvalue + cfa_loc.GetOffset ();
2486
2493
else
2487
2494
return regvalue;
@@ -2513,13 +2520,8 @@ static UnwindPlanSP GetUnwindPlanForAsyncRegister(FuncUnwinders &unwinders,
2513
2520
return unwinders.GetUnwindPlanAtNonCallSite (target, thread);
2514
2521
}
2515
2522
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 ®ctx, AsyncUnwindRegisterNumbers regnums) {
2523
+ static llvm::Expected<UnwindPlanSP>
2524
+ GetAsmUnwindPlan (Address pc, SymbolContext &sc, Thread &thread) {
2523
2525
FuncUnwindersSP unwinders =
2524
2526
pc.GetModule ()->GetUnwindTable ().GetFuncUnwindersContainingAddress (pc,
2525
2527
sc);
@@ -2528,77 +2530,137 @@ static llvm::Expected<addr_t> ReadAsyncContextRegisterFromUnwind(
2528
2530
" function unwinder at address 0x%8.8" PRIx64,
2529
2531
pc.GetFileAddress ());
2530
2532
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);
2534
2535
if (!unwind_plan)
2535
2536
return llvm::createStringError (
2536
2537
" SwiftLanguageRuntime: Failed to find non call site unwind plan at "
2537
2538
" address 0x%8.8" PRIx64,
2538
2539
pc.GetFileAddress ());
2540
+ return unwind_plan;
2541
+ }
2539
2542
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 ®ctx) {
2545
+ uint32_t fp_unwind_regdomain;
2547
2546
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 )) {
2550
2549
// This should never happen.
2551
2550
// If asserts are disabled, return an error to avoid creating an invalid
2552
2551
// unwind plan.
2553
- auto error_msg = " SwiftLanguageRuntime: Failed to convert register domains" ;
2552
+ const auto *error_msg =
2553
+ " SwiftLanguageRuntime: Failed to convert register domains" ;
2554
2554
llvm_unreachable (error_msg);
2555
2555
return llvm::createStringError (error_msg);
2556
2556
}
2557
+ return fp_unwind_regdomain;
2558
+ }
2557
2559
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
+ };
2564
2564
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 ®ctx) {
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 ®ctx, 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 )
2570
2647
return ReadRegisterAsAddress (regctx, regnums.GetRegisterKind (),
2571
2648
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;
2602
2664
}
2603
2665
2604
2666
// / Returns true if the async register should be dereferenced once to obtain the
0 commit comments