@@ -99,10 +99,6 @@ static bool IsModuleSwiftRuntime(lldb_private::Process &process,
99
99
return module .GetFileSpec ().GetFilename () == GetStandardLibraryName (process);
100
100
}
101
101
102
- static UnwindPlanSP
103
- GetFollowAsyncContextUnwindPlan (RegisterContext *regctx, ArchSpec &arch,
104
- bool &behaves_like_zeroth_frame);
105
-
106
102
AppleObjCRuntimeV2 *
107
103
SwiftLanguageRuntime::GetObjCRuntime (lldb_private::Process &process) {
108
104
if (auto objc_runtime = ObjCLanguageRuntime::Get (process)) {
@@ -2608,7 +2604,7 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2608
2604
addr_t fp = regctx->GetFP (LLDB_INVALID_ADDRESS);
2609
2605
if (fp == LLDB_INVALID_ADDRESS) {
2610
2606
if (GetAsyncContext (regctx) != LLDB_INVALID_ADDRESS)
2611
- return GetFollowAsyncContextUnwindPlan (regctx, arch,
2607
+ return GetFollowAsyncContextUnwindPlan (process_sp, regctx, arch,
2612
2608
behaves_like_zeroth_frame);
2613
2609
return UnwindPlanSP ();
2614
2610
}
@@ -2749,9 +2745,31 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2749
2745
regnums->dummy_regnum , g_dummy_dwarf_expression,
2750
2746
sizeof (g_dummy_dwarf_expression), false );
2751
2747
}
2752
- row->SetRegisterLocationToAtCFAPlusOffset (regnums->pc_regnum , ptr_size,
2753
- false );
2754
2748
2749
+ std::optional<addr_t > pc_after_prologue = [&]() -> std::optional<addr_t > {
2750
+ // In the prologue, use the async_reg as is, it has not been clobbered.
2751
+ if (in_prologue)
2752
+ return TrySkipVirtualParentProlog (GetAsyncContext (regctx), *process_sp,
2753
+ indirect_context);
2754
+
2755
+ // Both ABIs (x86_64 and aarch64) guarantee the async reg is saved at:
2756
+ // *(fp - 8).
2757
+ Status error;
2758
+ addr_t async_reg_entry_value = LLDB_INVALID_ADDRESS;
2759
+ process_sp->ReadMemory (fp - ptr_size, &async_reg_entry_value, ptr_size,
2760
+ error);
2761
+ if (error.Fail ())
2762
+ return {};
2763
+ return TrySkipVirtualParentProlog (async_reg_entry_value, *process_sp,
2764
+ indirect_context);
2765
+ }();
2766
+
2767
+ if (pc_after_prologue)
2768
+ row->SetRegisterLocationToIsConstant (regnums->pc_regnum , *pc_after_prologue,
2769
+ false );
2770
+ else
2771
+ row->SetRegisterLocationToAtCFAPlusOffset (regnums->pc_regnum , ptr_size,
2772
+ false );
2755
2773
row->SetUnspecifiedRegistersAreUndefined (true );
2756
2774
2757
2775
UnwindPlanSP plan = std::make_shared<UnwindPlan>(lldb::eRegisterKindDWARF);
@@ -2763,11 +2781,9 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2763
2781
return plan;
2764
2782
}
2765
2783
2766
- // Creates an UnwindPlan for following the AsyncContext chain
2767
- // up the stack, from a current AsyncContext frame.
2768
- static UnwindPlanSP
2769
- GetFollowAsyncContextUnwindPlan (RegisterContext *regctx, ArchSpec &arch,
2770
- bool &behaves_like_zeroth_frame) {
2784
+ UnwindPlanSP SwiftLanguageRuntime::GetFollowAsyncContextUnwindPlan (
2785
+ ProcessSP process_sp, RegisterContext *regctx, ArchSpec &arch,
2786
+ bool &behaves_like_zeroth_frame) {
2771
2787
LLDB_SCOPED_TIMER ();
2772
2788
2773
2789
UnwindPlan::RowSP row (new UnwindPlan::Row);
@@ -2779,13 +2795,15 @@ GetFollowAsyncContextUnwindPlan(RegisterContext *regctx, ArchSpec &arch,
2779
2795
if (!regnums)
2780
2796
return UnwindPlanSP ();
2781
2797
2798
+ const bool is_indirect =
2799
+ regctx->ReadRegisterAsUnsigned (regnums->dummy_regnum , (uint64_t )-1ll ) ==
2800
+ (uint64_t )-1ll ;
2782
2801
// In the general case, the async register setup by the frame above us
2783
2802
// should be dereferenced twice to get our context, except when the frame
2784
2803
// above us is an async frame on the OS stack that takes its context directly
2785
2804
// (see discussion in GetRuntimeUnwindPlan()). The availability of
2786
2805
// dummy_regnum is used as a marker for this situation.
2787
- if (regctx->ReadRegisterAsUnsigned (regnums->dummy_regnum , (uint64_t )-1ll ) !=
2788
- (uint64_t )-1ll ) {
2806
+ if (!is_indirect) {
2789
2807
row->GetCFAValue ().SetIsRegisterDereferenced (regnums->async_ctx_regnum );
2790
2808
row->SetRegisterLocationToSame (regnums->async_ctx_regnum , false );
2791
2809
} else {
@@ -2822,8 +2840,21 @@ GetFollowAsyncContextUnwindPlan(RegisterContext *regctx, ArchSpec &arch,
2822
2840
regnums->async_ctx_regnum , expression, expr_size - 1 , false );
2823
2841
}
2824
2842
2825
- row->SetRegisterLocationToAtCFAPlusOffset (regnums->pc_regnum , ptr_size,
2826
- false );
2843
+ // Suppose this is unwinding frame #2 of a call stack. The value given for
2844
+ // the async register has two possible values, depending on what frame #1
2845
+ // expects:
2846
+ // 1. The CFA of frame #1, direct ABI, dereferencing it once produces CFA of
2847
+ // Frame #2.
2848
+ // 2. The CFA of frame #0, indirect ABI, dereferencing it twice produces CFA
2849
+ // of Frame #2.
2850
+ const unsigned num_indirections = 1 + is_indirect;
2851
+ if (std::optional<addr_t > pc_after_prologue = TrySkipVirtualParentProlog (
2852
+ GetAsyncContext (regctx), *process_sp, num_indirections))
2853
+ row->SetRegisterLocationToIsConstant (regnums->pc_regnum , *pc_after_prologue,
2854
+ false );
2855
+ else
2856
+ row->SetRegisterLocationToAtCFAPlusOffset (regnums->pc_regnum , ptr_size,
2857
+ false );
2827
2858
2828
2859
row->SetUnspecifiedRegistersAreUndefined (true );
2829
2860
@@ -2837,4 +2868,51 @@ GetFollowAsyncContextUnwindPlan(RegisterContext *regctx, ArchSpec &arch,
2837
2868
return plan;
2838
2869
}
2839
2870
2871
+ std::optional<lldb::addr_t > SwiftLanguageRuntime::TrySkipVirtualParentProlog (
2872
+ lldb::addr_t async_reg_val, Process &process, unsigned num_indirections) {
2873
+ assert (num_indirections <= 2 &&
2874
+ " more than two dereferences should not be needed" );
2875
+ if (async_reg_val == LLDB_INVALID_ADDRESS || async_reg_val == 0 )
2876
+ return {};
2877
+
2878
+ const auto ptr_size = process.GetAddressByteSize ();
2879
+ Status error;
2880
+
2881
+ // Compute the CFA of this frame.
2882
+ addr_t cfa = async_reg_val;
2883
+ for (; num_indirections != 0 ; --num_indirections) {
2884
+ process.ReadMemory (cfa, &cfa, ptr_size, error);
2885
+ if (error.Fail ())
2886
+ return {};
2887
+ }
2888
+
2889
+ // The last funclet will have a zero CFA, we don't want to read that.
2890
+ if (cfa == 0 )
2891
+ return {};
2892
+
2893
+ // Get the PC of the parent frame, i.e. the continuation pointer, which is
2894
+ // the second field of the CFA.
2895
+ addr_t pc_location = cfa + ptr_size;
2896
+ addr_t pc_value = LLDB_INVALID_ADDRESS;
2897
+ process.ReadMemory (pc_location, &pc_value, ptr_size, error);
2898
+ if (error.Fail ())
2899
+ return {};
2900
+
2901
+ Address pc;
2902
+ Target &target = process.GetTarget ();
2903
+ pc.SetLoadAddress (pc_value, &target);
2904
+ if (!pc.IsValid ())
2905
+ return {};
2906
+
2907
+ SymbolContext sc;
2908
+ if (!pc.CalculateSymbolContext (&sc,
2909
+ eSymbolContextFunction | eSymbolContextSymbol))
2910
+ return {};
2911
+ if (!sc.symbol && !sc.function )
2912
+ return {};
2913
+
2914
+ auto prologue_size = sc.symbol ? sc.symbol ->GetPrologueByteSize ()
2915
+ : sc.function ->GetPrologueByteSize ();
2916
+ return pc_value + prologue_size;
2917
+ }
2840
2918
} // namespace lldb_private
0 commit comments