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"
@@ -2542,6 +2544,8 @@ struct AsyncUnwindRegisterNumbers {
2542
2544
// / frames below us as they need to react differently. There is no good way to
2543
2545
// / expose this, so we set another dummy register to communicate this state.
2544
2546
uint32_t dummy_regnum;
2547
+
2548
+ RegisterKind GetRegisterKind () const { return lldb::eRegisterKindDWARF; }
2545
2549
};
2546
2550
} // namespace
2547
2551
@@ -2584,37 +2588,148 @@ lldb::addr_t SwiftLanguageRuntime::GetAsyncContext(RegisterContext *regctx) {
2584
2588
return LLDB_INVALID_ADDRESS;
2585
2589
}
2586
2590
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
- };
2591
+ // / Functional wrapper to read a register as an address.
2592
+ static llvm::Expected<addr_t > ReadRegisterAsAddress (RegisterContext ®ctx,
2593
+ RegisterKind regkind,
2594
+ unsigned regnum) {
2595
+ unsigned lldb_regnum =
2596
+ regctx.ConvertRegisterKindToRegisterNumber (regkind, regnum);
2597
+ auto reg = regctx.ReadRegisterAsUnsigned (lldb_regnum, LLDB_INVALID_ADDRESS);
2598
+ if (reg != LLDB_INVALID_ADDRESS)
2599
+ return reg;
2600
+ return llvm::createStringError (
2601
+ " SwiftLanguageRuntime: failed to read register from regctx" );
2602
+ }
2603
+
2604
+ // / Functional wrapper to read a pointer from process memory at `addr +
2605
+ // / offset`.
2606
+ static llvm::Expected<addr_t > ReadPtrFromAddr (Process &process, addr_t addr,
2607
+ int offset = 0 ) {
2608
+ Status error;
2609
+ addr_t ptr = process.ReadPointerFromMemory (addr + offset, error);
2610
+ if (ptr != LLDB_INVALID_ADDRESS)
2611
+ return ptr;
2612
+ return llvm::createStringError (" SwiftLanguageRuntime: Failed to read ptr "
2613
+ " from memory address 0x%8.8" PRIx64
2614
+ " Error was %s" ,
2615
+ addr + offset, error.AsCString ());
2616
+ }
2617
+
2618
+ // / Computes the Canonical Frame Address (CFA) by converting the abstract
2619
+ // / location of UnwindPlan::Row::FAValue into a concrete address. This is a
2620
+ // / simplified version of the methods in RegisterContextUnwind, since plumbing
2621
+ // / access to those here would be challenging.
2622
+ static llvm::Expected<addr_t > GetCFA (Process &process, RegisterContext ®ctx,
2623
+ RegisterKind regkind,
2624
+ UnwindPlan::Row::FAValue cfa_loc) {
2625
+ using ValueType = UnwindPlan::Row::FAValue::ValueType;
2626
+ switch (cfa_loc.GetValueType ()) {
2627
+ case ValueType::isRegisterPlusOffset: {
2628
+ unsigned regnum = cfa_loc.GetRegisterNumber ();
2629
+ if (llvm::Expected<addr_t > regvalue =
2630
+ ReadRegisterAsAddress (regctx, regkind, regnum))
2631
+ return *regvalue + cfa_loc.GetOffset ();
2632
+ else
2633
+ return regvalue;
2634
+ }
2635
+ case ValueType::isConstant:
2636
+ case ValueType::isDWARFExpression:
2637
+ case ValueType::isRaSearch:
2638
+ case ValueType::isRegisterDereferenced:
2639
+ case ValueType::unspecified:
2640
+ break ;
2641
+ }
2642
+ return llvm::createStringError (
2643
+ " SwiftLanguageRuntime: Unsupported FA location type = %d" ,
2644
+ cfa_loc.GetValueType ());
2645
+ }
2646
+
2647
+ // / Attempts to use UnwindPlans that inspect assembly to recover the entry value
2648
+ // / of the async context register. This is a simplified version of the methods
2649
+ // / in RegisterContextUnwind, since plumbing access to those here would be
2650
+ // / challenging.
2651
+ static llvm::Expected<addr_t > ReadAsyncContextRegisterFromUnwind (
2652
+ SymbolContext &sc, Process &process, Address pc, Address func_start_addr,
2653
+ RegisterContext ®ctx, AsyncUnwindRegisterNumbers regnums) {
2654
+ FuncUnwindersSP unwinders =
2655
+ pc.GetModule ()->GetUnwindTable ().GetFuncUnwindersContainingAddress (pc,
2656
+ sc);
2657
+ if (!unwinders)
2658
+ return llvm::createStringError (" SwiftLanguageRuntime: Failed to find "
2659
+ " function unwinder at address 0x%8.8" PRIx64,
2660
+ pc.GetFileAddress ());
2608
2661
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 );
2662
+ Target &target = process.GetTarget ();
2663
+ UnwindPlanSP unwind_plan =
2664
+ unwinders->GetUnwindPlanAtNonCallSite (target, regctx.GetThread ());
2665
+ if (!unwind_plan)
2666
+ return llvm::createStringError (
2667
+ " SwiftLanguageRuntime: Failed to find non call site unwind plan at "
2668
+ " address 0x%8.8" PRIx64,
2669
+ pc.GetFileAddress ());
2670
+
2671
+ const RegisterKind unwind_regkind = unwind_plan->GetRegisterKind ();
2672
+ UnwindPlan::RowSP row = unwind_plan->GetRowForFunctionOffset (
2673
+ pc.GetFileAddress () - func_start_addr.GetFileAddress ());
2674
+
2675
+ // To request info about a register from the unwind plan, the register must
2676
+ // be in the same domain as the unwind plan's registers.
2677
+ uint32_t async_reg_unwind_regdomain;
2678
+ if (!regctx.ConvertBetweenRegisterKinds (
2679
+ regnums.GetRegisterKind (), regnums.async_ctx_regnum , unwind_regkind,
2680
+ async_reg_unwind_regdomain)) {
2681
+ // This should never happen.
2682
+ // If asserts are disabled, return an error to avoid creating an invalid
2683
+ // unwind plan.
2684
+ auto error_msg = " SwiftLanguageRuntime: Failed to convert register domains" ;
2685
+ llvm_unreachable (error_msg);
2686
+ return llvm::createStringError (error_msg);
2687
+ }
2688
+
2689
+ // If the plan doesn't have information about the async register, we can use
2690
+ // its current value, as this is a callee saved register.
2691
+ UnwindPlan::Row::AbstractRegisterLocation regloc;
2692
+ if (!row->GetRegisterInfo (async_reg_unwind_regdomain, regloc))
2693
+ return ReadRegisterAsAddress (regctx, regnums.GetRegisterKind (),
2694
+ regnums.async_ctx_regnum );
2695
+
2696
+ // Handle the few abstract locations we are likely to encounter.
2697
+ using RestoreType = UnwindPlan::Row::AbstractRegisterLocation::RestoreType;
2698
+ RestoreType loctype = regloc.GetLocationType ();
2699
+ switch (loctype) {
2700
+ case RestoreType::same:
2701
+ return ReadRegisterAsAddress (regctx, regnums.GetRegisterKind (),
2702
+ regnums.async_ctx_regnum );
2703
+ case RestoreType::inOtherRegister: {
2704
+ unsigned regnum = regloc.GetRegisterNumber ();
2705
+ return ReadRegisterAsAddress (regctx, unwind_regkind, regnum);
2706
+ }
2707
+ case RestoreType::atCFAPlusOffset: {
2708
+ llvm::Expected<addr_t > cfa =
2709
+ GetCFA (process, regctx, unwind_regkind, row->GetCFAValue ());
2710
+ if (!cfa)
2711
+ return cfa.takeError ();
2712
+ return ReadPtrFromAddr (process, *cfa, regloc.GetOffset ());
2713
+ }
2714
+ case RestoreType::isCFAPlusOffset: {
2715
+ if (llvm::Expected<addr_t > cfa =
2716
+ GetCFA (process, regctx, unwind_regkind, row->GetCFAValue ()))
2717
+ return *cfa + regloc.GetOffset ();
2718
+ else
2719
+ return cfa;
2720
+ }
2721
+ case RestoreType::isConstant:
2722
+ return regloc.GetConstant ();
2723
+ case RestoreType::unspecified:
2724
+ case RestoreType::undefined:
2725
+ case RestoreType::atAFAPlusOffset:
2726
+ case RestoreType::isAFAPlusOffset:
2727
+ case RestoreType::isDWARFExpression:
2728
+ case RestoreType::atDWARFExpression:
2729
+ break ;
2730
+ }
2731
+ return llvm::createStringError (
2732
+ " SwiftLanguageRuntime: Unsupported register location type = %d" , loctype);
2618
2733
}
2619
2734
2620
2735
// Examine the register state and detect the transition from a real
@@ -2625,6 +2740,11 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2625
2740
RegisterContext *regctx,
2626
2741
bool &behaves_like_zeroth_frame) {
2627
2742
LLDB_SCOPED_TIMER ();
2743
+ auto log_expected = [](llvm::Error error) {
2744
+ Log *log = GetLog (LLDBLog::Unwind);
2745
+ LLDB_LOG_ERROR (log, std::move (error), " {0}" );
2746
+ return UnwindPlanSP ();
2747
+ };
2628
2748
2629
2749
Target &target (process_sp->GetTarget ());
2630
2750
auto arch = target.GetArchitecture ();
@@ -2644,12 +2764,6 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2644
2764
return UnwindPlanSP ();
2645
2765
}
2646
2766
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
2767
Address pc;
2654
2768
pc.SetLoadAddress (regctx->GetPC (), &target);
2655
2769
SymbolContext sc;
@@ -2659,111 +2773,58 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2659
2773
return UnwindPlanSP ();
2660
2774
2661
2775
Address func_start_addr;
2662
- uint32_t prologue_size;
2663
2776
ConstString mangled_name;
2664
2777
if (sc.function ) {
2665
2778
func_start_addr = sc.function ->GetAddressRange ().GetBaseAddress ();
2666
- prologue_size = sc.function ->GetPrologueByteSize ();
2667
2779
mangled_name = sc.function ->GetMangled ().GetMangledName ();
2668
2780
} else if (sc.symbol ) {
2669
2781
func_start_addr = sc.symbol ->GetAddress ();
2670
- prologue_size = sc.symbol ->GetPrologueByteSize ();
2671
2782
mangled_name = sc.symbol ->GetMangled ().GetMangledName ();
2672
2783
} else {
2673
2784
return UnwindPlanSP ();
2674
2785
}
2675
2786
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
- }
2787
+ if (!IsAnySwiftAsyncFunctionSymbol (mangled_name.GetStringRef ()))
2788
+ return UnwindPlanSP ();
2694
2789
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.
2790
+ // The async register contains, at the start of the funclet:
2791
+ // 1. The async context of the async function that just finished executing,
2792
+ // for await resume ("Q") funclets ("indirect context").
2793
+ // 2. The async context for the currently executing async function, for all
2794
+ // other funclets ("Y" and "Yx" funclets, where "x" is a number).
2702
2795
bool indirect_context =
2703
2796
IsSwiftAsyncAwaitResumePartialFunctionSymbol (mangled_name.GetStringRef ());
2704
2797
2798
+ llvm::Expected<addr_t > async_reg = ReadAsyncContextRegisterFromUnwind (
2799
+ sc, *process_sp, pc, func_start_addr, *regctx, *regnums);
2800
+ if (!async_reg)
2801
+ return log_expected (async_reg.takeError ());
2802
+ llvm::Expected<addr_t > async_ctx =
2803
+ indirect_context ? ReadPtrFromAddr (*m_process, *async_reg) : *async_reg;
2804
+ if (!async_ctx)
2805
+ return log_expected (async_ctx.takeError ());
2806
+
2705
2807
UnwindPlan::RowSP row (new UnwindPlan::Row);
2706
2808
const int32_t ptr_size = 8 ;
2707
2809
row->SetOffset (0 );
2708
2810
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
- }();
2811
+ // The CFA of a funclet is its own async context.
2812
+ row->GetCFAValue ().SetIsConstant (*async_ctx);
2813
+
2814
+ // The value of the async register in the parent frame is the entry value of
2815
+ // the async register in the current frame. This mimics a function call, as
2816
+ // if the parent funclet had called the current funclet.
2817
+ row->SetRegisterLocationToIsConstant (regnums->async_ctx_regnum , *async_reg,
2818
+ /* can_replace=*/ false );
2819
+
2820
+ // The parent frame needs to know how to interpret the value it is given for
2821
+ // its own async register. A dummy register is used to communicate that.
2822
+ if (!indirect_context)
2823
+ row->SetRegisterLocationToIsConstant (regnums->dummy_regnum , 0 ,
2824
+ /* can_replace=*/ false );
2765
2825
2766
- if (pc_after_prologue)
2826
+ if (std::optional<addr_t > pc_after_prologue =
2827
+ TrySkipVirtualParentProlog (*async_ctx, *process_sp))
2767
2828
row->SetRegisterLocationToIsConstant (regnums->pc_regnum , *pc_after_prologue,
2768
2829
false );
2769
2830
else
0 commit comments