37
37
#include " lldb/Interpreter/CommandObject.h"
38
38
#include " lldb/Interpreter/CommandObjectMultiword.h"
39
39
#include " lldb/Interpreter/CommandReturnObject.h"
40
+ #include " lldb/Symbol/FuncUnwinders.h"
40
41
#include " lldb/Symbol/Function.h"
41
42
#include " lldb/Symbol/VariableList.h"
42
43
#include " lldb/Target/RegisterContext.h"
44
+ #include " lldb/Target/UnwindLLDB.h"
43
45
#include " lldb/Utility/LLDBLog.h"
44
46
#include " lldb/Utility/Log.h"
45
47
#include " lldb/Utility/OptionParsing.h"
@@ -2584,37 +2586,111 @@ lldb::addr_t SwiftLanguageRuntime::GetAsyncContext(RegisterContext *regctx) {
2584
2586
return LLDB_INVALID_ADDRESS;
2585
2587
}
2586
2588
2587
- // / Creates an expression accessing *(fp - 8) or **(fp - 8) if
2588
- // / `with_double_deref` is true. This is only valid for x86_64 or aarch64.
2589
- llvm::ArrayRef< uint8_t >
2590
- GetAsyncRegFromFramePointerDWARFExpr (llvm::Triple::ArchType triple,
2591
- bool with_double_deref) {
2592
- assert (triple == llvm::Triple::x86_64 || triple == llvm::Triple::aarch64) ;
2593
-
2594
- // These expressions must have static storage, due to how UnwindPlan::Row
2595
- // works.
2596
- static const uint8_t g_cfa_dwarf_expression_x86_64[] = {
2597
- llvm::dwarf::DW_OP_breg6, // DW_OP_breg6, register 6 == rbp
2598
- 0x78 , // sleb128 -8 (ptrsize)
2599
- llvm::dwarf::DW_OP_deref,
2600
- llvm::dwarf::DW_OP_deref,
2601
- };
2602
- static const uint8_t g_cfa_dwarf_expression_arm64[] = {
2603
- llvm::dwarf::DW_OP_breg29, // DW_OP_breg29, register 29 == fp
2604
- 0x78 , // sleb128 -8 (ptrsize )
2605
- llvm::dwarf::DW_OP_deref,
2606
- llvm::dwarf::DW_OP_deref,
2607
- };
2589
+ // / Functional wrapper to read a register as an address.
2590
+ static std::optional< addr_t > ReadRegisterAsAddress (RegisterContext ®ctx,
2591
+ unsigned regnum) {
2592
+ auto reg = regctx. ReadRegisterAsUnsigned (regnum, LLDB_INVALID_ADDRESS);
2593
+ if (reg != LLDB_INVALID_ADDRESS)
2594
+ return reg ;
2595
+ return {};
2596
+ }
2597
+
2598
+ // / Functional wrapper to read a pointer from process memory at `addr +
2599
+ // / offset`.
2600
+ static std::optional< addr_t >
2601
+ ReadPtrFromAddr (Process &process, std::optional< addr_t > addr, int offset = 0 ) {
2602
+ if (!addr)
2603
+ return { };
2604
+ Status error;
2605
+ addr_t ptr = process. ReadPointerFromMemory (*addr + offset, error);
2606
+ if (ptr != LLDB_INVALID_ADDRESS )
2607
+ return ptr;
2608
+ return {};
2609
+ }
2608
2610
2609
- const uint8_t *expr = triple == llvm::Triple::x86_64
2610
- ? g_cfa_dwarf_expression_x86_64
2611
- : g_cfa_dwarf_expression_arm64;
2612
- auto size = triple == llvm::Triple::x86_64
2613
- ? sizeof (g_cfa_dwarf_expression_x86_64)
2614
- : sizeof (g_cfa_dwarf_expression_arm64);
2615
- if (with_double_deref)
2616
- return llvm::ArrayRef<uint8_t >(expr, size);
2617
- return llvm::ArrayRef<uint8_t >(expr, size - 1 );
2611
+ // / Computes the Canonical Frame Address (CFA) by converting the abstract
2612
+ // / location of UnwindPlan::Row::FAValue into a concrete address. This is a
2613
+ // / simplified version of the methods in RegisterContextUnwind, since plumbing
2614
+ // / access to those here would be challenging.
2615
+ static std::optional<addr_t > GetCFA (Process &process, RegisterContext ®ctx,
2616
+ UnwindPlan::Row::FAValue cfa_loc) {
2617
+ using ValueType = UnwindPlan::Row::FAValue::ValueType;
2618
+ switch (cfa_loc.GetValueType ()) {
2619
+ case ValueType::isRegisterPlusOffset: {
2620
+ unsigned regnum = cfa_loc.GetRegisterNumber ();
2621
+ if (std::optional<addr_t > regvalue = ReadRegisterAsAddress (regctx, regnum))
2622
+ return *regvalue + cfa_loc.GetOffset ();
2623
+ break ;
2624
+ }
2625
+ case ValueType::isConstant:
2626
+ case ValueType::isDWARFExpression:
2627
+ case ValueType::isRaSearch:
2628
+ case ValueType::isRegisterDereferenced:
2629
+ case ValueType::unspecified:
2630
+ break ;
2631
+ }
2632
+ return {};
2633
+ }
2634
+
2635
+ // / Attempts to use UnwindPlans that inspect assembly to recover the entry value
2636
+ // / of the async context register. This is a simplified version of the methods
2637
+ // / in RegisterContextUnwind, since plumbing access to those here would be
2638
+ // / challenging.
2639
+ static std::optional<addr_t > ReadAsyncContextRegisterFromUnwind (
2640
+ SymbolContext &sc, Process &process, Address pc, Address func_start_addr,
2641
+ RegisterContext ®ctx, AsyncUnwindRegisterNumbers regnums) {
2642
+ FuncUnwindersSP unwinders =
2643
+ pc.GetModule ()->GetUnwindTable ().GetFuncUnwindersContainingAddress (pc,
2644
+ sc);
2645
+ if (!unwinders)
2646
+ return {};
2647
+
2648
+ Target &target = process.GetTarget ();
2649
+ UnwindPlanSP unwind_plan =
2650
+ unwinders->GetUnwindPlanAtNonCallSite (target, regctx.GetThread ());
2651
+ if (!unwind_plan)
2652
+ return {};
2653
+
2654
+ UnwindPlan::RowSP row = unwind_plan->GetRowForFunctionOffset (
2655
+ pc.GetFileAddress () - func_start_addr.GetFileAddress ());
2656
+ UnwindPlan::Row::AbstractRegisterLocation regloc;
2657
+
2658
+ // If the plan doesn't have information about the async register, we can use
2659
+ // its current value, as this is a callee saved register.
2660
+ if (!row->GetRegisterInfo (regnums.async_ctx_regnum , regloc))
2661
+ return ReadRegisterAsAddress (regctx, regnums.async_ctx_regnum );
2662
+
2663
+ // Handle the few abstract locations we are likely to encounter.
2664
+ using RestoreType = UnwindPlan::Row::AbstractRegisterLocation::RestoreType;
2665
+ RestoreType loctype = regloc.GetLocationType ();
2666
+ switch (loctype) {
2667
+ case RestoreType::same:
2668
+ case RestoreType::inOtherRegister: {
2669
+ unsigned regnum = loctype == RestoreType::same ? regnums.async_ctx_regnum
2670
+ : regloc.GetRegisterNumber ();
2671
+ return ReadRegisterAsAddress (regctx, regnum);
2672
+ }
2673
+ case RestoreType::atCFAPlusOffset: {
2674
+ std::optional<addr_t > cfa = GetCFA (process, regctx, row->GetCFAValue ());
2675
+ return ReadPtrFromAddr (process, cfa, regloc.GetOffset ());
2676
+ }
2677
+ case RestoreType::isCFAPlusOffset: {
2678
+ std::optional<addr_t > cfa = GetCFA (process, regctx, row->GetCFAValue ());
2679
+ if (!cfa)
2680
+ return {};
2681
+ return *cfa + regloc.GetOffset ();
2682
+ }
2683
+ case RestoreType::isConstant:
2684
+ return regloc.GetConstant ();
2685
+ case RestoreType::unspecified:
2686
+ case RestoreType::undefined:
2687
+ case RestoreType::atAFAPlusOffset:
2688
+ case RestoreType::isAFAPlusOffset:
2689
+ case RestoreType::isDWARFExpression:
2690
+ case RestoreType::atDWARFExpression:
2691
+ return {};
2692
+ }
2693
+ return {};
2618
2694
}
2619
2695
2620
2696
// Examine the register state and detect the transition from a real
@@ -2644,12 +2720,6 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2644
2720
return UnwindPlanSP ();
2645
2721
}
2646
2722
2647
- // If we're in the prologue of a function, don't provide a Swift async
2648
- // unwind plan. We can be tricked by unmodified caller-registers that
2649
- // make this look like an async frame when this is a standard ABI function
2650
- // call, and the parent is the async frame.
2651
- // This assumes that the frame pointer register will be modified in the
2652
- // prologue.
2653
2723
Address pc;
2654
2724
pc.SetLoadAddress (regctx->GetPC (), &target);
2655
2725
SymbolContext sc;
@@ -2659,111 +2729,56 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2659
2729
return UnwindPlanSP ();
2660
2730
2661
2731
Address func_start_addr;
2662
- uint32_t prologue_size;
2663
2732
ConstString mangled_name;
2664
2733
if (sc.function ) {
2665
2734
func_start_addr = sc.function ->GetAddressRange ().GetBaseAddress ();
2666
- prologue_size = sc.function ->GetPrologueByteSize ();
2667
2735
mangled_name = sc.function ->GetMangled ().GetMangledName ();
2668
2736
} else if (sc.symbol ) {
2669
2737
func_start_addr = sc.symbol ->GetAddress ();
2670
- prologue_size = sc.symbol ->GetPrologueByteSize ();
2671
2738
mangled_name = sc.symbol ->GetMangled ().GetMangledName ();
2672
2739
} else {
2673
2740
return UnwindPlanSP ();
2674
2741
}
2675
2742
2676
- AddressRange prologue_range (func_start_addr, prologue_size);
2677
- bool in_prologue = (func_start_addr == pc ||
2678
- prologue_range.ContainsLoadAddress (pc, &target));
2679
-
2680
- if (in_prologue) {
2681
- if (!IsAnySwiftAsyncFunctionSymbol (mangled_name.GetStringRef ()))
2682
- return UnwindPlanSP ();
2683
- } else {
2684
- addr_t saved_fp = LLDB_INVALID_ADDRESS;
2685
- Status error;
2686
- if (!process_sp->ReadMemory (fp, &saved_fp, 8 , error))
2687
- return UnwindPlanSP ();
2688
-
2689
- // Get the high nibble of the dreferenced fp; if the 60th bit is set,
2690
- // this is the transition to a swift async AsyncContext chain.
2691
- if ((saved_fp & (0xfULL << 60 )) >> 60 != 1 )
2692
- return UnwindPlanSP ();
2693
- }
2743
+ if (!IsAnySwiftAsyncFunctionSymbol (mangled_name.GetStringRef ()))
2744
+ return UnwindPlanSP ();
2694
2745
2695
- // The coroutine funclets split from an async function have 2 different ABIs:
2696
- // - Async suspend partial functions and the first funclet get their async
2697
- // context directly in the async register.
2698
- // - Async await resume partial functions take their context indirectly, it
2699
- // needs to be dereferenced to get the actual function's context.
2700
- // The debug info for locals reflects this difference, so our unwinding of the
2701
- // context register needs to reflect it too.
2746
+ // The async register contains, at the start of the funclet:
2747
+ // 1. The async context of the async function that just finished executing,
2748
+ // for await resume ("Q") funclets ("indirect context").
2749
+ // 2. The async context for the currently executing async function, for all
2750
+ // other funclets ("Y" and "Yx" funclets, where "x" is a number).
2702
2751
bool indirect_context =
2703
2752
IsSwiftAsyncAwaitResumePartialFunctionSymbol (mangled_name.GetStringRef ());
2704
2753
2754
+ std::optional<addr_t > async_reg = ReadAsyncContextRegisterFromUnwind (
2755
+ sc, *process_sp, pc, func_start_addr, *regctx, *regnums);
2756
+ std::optional<addr_t > async_ctx =
2757
+ indirect_context ? ReadPtrFromAddr (*m_process, async_reg) : async_reg;
2758
+ if (!async_reg || !async_ctx)
2759
+ return UnwindPlanSP ();
2760
+
2705
2761
UnwindPlan::RowSP row (new UnwindPlan::Row);
2706
2762
const int32_t ptr_size = 8 ;
2707
2763
row->SetOffset (0 );
2708
2764
2709
- if (in_prologue) {
2710
- if (indirect_context)
2711
- row->GetCFAValue ().SetIsRegisterDereferenced (regnums->async_ctx_regnum );
2712
- else
2713
- row->GetCFAValue ().SetIsRegisterPlusOffset (regnums->async_ctx_regnum , 0 );
2714
- } else {
2715
- // In indirect funclets, dereferencing (fp-8) once produces the CFA of the
2716
- // frame above. Dereferencing twice will produce the current frame's CFA.
2717
- bool with_double_deref = indirect_context;
2718
- llvm::ArrayRef<uint8_t > expr = GetAsyncRegFromFramePointerDWARFExpr (
2719
- arch.GetMachine (), with_double_deref);
2720
- row->GetCFAValue ().SetIsDWARFExpression (expr.data (), expr.size ());
2721
- }
2722
-
2723
- if (indirect_context) {
2724
- if (in_prologue) {
2725
- row->SetRegisterLocationToSame (regnums->async_ctx_regnum , false );
2726
- } else {
2727
- llvm::ArrayRef<uint8_t > expr = GetAsyncRegFromFramePointerDWARFExpr (
2728
- arch.GetMachine (), false /* with_double_deref*/ );
2729
- row->SetRegisterLocationToIsDWARFExpression (
2730
- regnums->async_ctx_regnum , expr.data (), expr.size (), false );
2731
- }
2732
- } else {
2733
- // In the first part of a split async function, the context is passed
2734
- // directly, so we can use the CFA value directly.
2735
- row->SetRegisterLocationToIsCFAPlusOffset (regnums->async_ctx_regnum , 0 ,
2736
- false );
2737
- // The fact that we are in this case needs to be communicated to the frames
2738
- // below us as they need to react differently. There is no good way to
2739
- // expose this, so we set another dummy register to communicate this state.
2740
- static const uint8_t g_dummy_dwarf_expression[] = {
2741
- llvm::dwarf::DW_OP_const1u, 0
2742
- };
2743
- row->SetRegisterLocationToIsDWARFExpression (
2744
- regnums->dummy_regnum , g_dummy_dwarf_expression,
2745
- sizeof (g_dummy_dwarf_expression), false );
2746
- }
2747
-
2748
- std::optional<addr_t > pc_after_prologue = [&]() -> std::optional<addr_t > {
2749
- // In the prologue, use the async_reg as is, it has not been clobbered.
2750
- if (in_prologue)
2751
- return TrySkipVirtualParentProlog (GetAsyncContext (regctx), *process_sp,
2752
- indirect_context);
2753
-
2754
- // Both ABIs (x86_64 and aarch64) guarantee the async reg is saved at:
2755
- // *(fp - 8).
2756
- Status error;
2757
- addr_t async_reg_entry_value = LLDB_INVALID_ADDRESS;
2758
- process_sp->ReadMemory (fp - ptr_size, &async_reg_entry_value, ptr_size,
2759
- error);
2760
- if (error.Fail ())
2761
- return {};
2762
- return TrySkipVirtualParentProlog (async_reg_entry_value, *process_sp,
2763
- indirect_context);
2764
- }();
2765
+ // The CFA of a funclet is its own async context.
2766
+ row->GetCFAValue ().SetIsConstant (*async_ctx);
2767
+
2768
+ // The value of the async register in the parent frame is the entry value of
2769
+ // the async register in the current frame. This mimics a function call, as
2770
+ // if the parent funclet had called the current funclet.
2771
+ row->SetRegisterLocationToIsConstant (regnums->async_ctx_regnum , *async_reg,
2772
+ /* can_replace=*/ false );
2773
+
2774
+ // The parent frame needs to know how to interpret the value it is given for
2775
+ // its own async register. A dummy register is used to communicate that.
2776
+ if (!indirect_context)
2777
+ row->SetRegisterLocationToIsConstant (regnums->dummy_regnum , 0 ,
2778
+ /* can_replace=*/ false );
2765
2779
2766
- if (pc_after_prologue)
2780
+ if (std::optional<addr_t > pc_after_prologue =
2781
+ TrySkipVirtualParentProlog (*async_ctx, *process_sp))
2767
2782
row->SetRegisterLocationToIsConstant (regnums->pc_regnum , *pc_after_prologue,
2768
2783
false );
2769
2784
else
0 commit comments