@@ -2745,9 +2745,31 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
2745
2745
regnums->dummy_regnum , g_dummy_dwarf_expression,
2746
2746
sizeof (g_dummy_dwarf_expression), false );
2747
2747
}
2748
- row->SetRegisterLocationToAtCFAPlusOffset (regnums->pc_regnum , ptr_size,
2749
- false );
2750
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;
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 );
2751
2773
row->SetUnspecifiedRegistersAreUndefined (true );
2752
2774
2753
2775
UnwindPlanSP plan = std::make_shared<UnwindPlan>(lldb::eRegisterKindDWARF);
@@ -2773,13 +2795,15 @@ UnwindPlanSP SwiftLanguageRuntime::GetFollowAsyncContextUnwindPlan(
2773
2795
if (!regnums)
2774
2796
return UnwindPlanSP ();
2775
2797
2798
+ const bool is_indirect =
2799
+ regctx->ReadRegisterAsUnsigned (regnums->dummy_regnum , (uint64_t )-1ll ) ==
2800
+ (uint64_t )-1ll ;
2776
2801
// In the general case, the async register setup by the frame above us
2777
2802
// should be dereferenced twice to get our context, except when the frame
2778
2803
// above us is an async frame on the OS stack that takes its context directly
2779
2804
// (see discussion in GetRuntimeUnwindPlan()). The availability of
2780
2805
// dummy_regnum is used as a marker for this situation.
2781
- if (regctx->ReadRegisterAsUnsigned (regnums->dummy_regnum , (uint64_t )-1ll ) !=
2782
- (uint64_t )-1ll ) {
2806
+ if (!is_indirect) {
2783
2807
row->GetCFAValue ().SetIsRegisterDereferenced (regnums->async_ctx_regnum );
2784
2808
row->SetRegisterLocationToSame (regnums->async_ctx_regnum , false );
2785
2809
} else {
@@ -2816,8 +2840,21 @@ UnwindPlanSP SwiftLanguageRuntime::GetFollowAsyncContextUnwindPlan(
2816
2840
regnums->async_ctx_regnum , expression, expr_size - 1 , false );
2817
2841
}
2818
2842
2819
- row->SetRegisterLocationToAtCFAPlusOffset (regnums->pc_regnum , ptr_size,
2820
- 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 );
2821
2858
2822
2859
row->SetUnspecifiedRegistersAreUndefined (true );
2823
2860
@@ -2831,4 +2868,49 @@ UnwindPlanSP SwiftLanguageRuntime::GetFollowAsyncContextUnwindPlan(
2831
2868
return plan;
2832
2869
}
2833
2870
2871
+ std::optional<lldb::addr_t > SwiftLanguageRuntime::TrySkipVirtualParentProlog (
2872
+ lldb::addr_t async_reg_val, Process &process, unsigned num_indirections) {
2873
+ if (async_reg_val == LLDB_INVALID_ADDRESS || async_reg_val == 0 )
2874
+ return {};
2875
+
2876
+ const auto ptr_size = process.GetAddressByteSize ();
2877
+ Status error;
2878
+
2879
+ // Compute the CFA of this frame.
2880
+ addr_t cfa = async_reg_val;
2881
+ while (num_indirections--) {
2882
+ process.ReadMemory (cfa, &cfa, ptr_size, error);
2883
+ if (error.Fail ())
2884
+ return {};
2885
+ }
2886
+
2887
+ // The last funclet will have a zero CFA, we don't want to read that.
2888
+ if (cfa == 0 )
2889
+ return {};
2890
+
2891
+ // Get the PC of the parent frame, i.e. the continuation pointer, which is
2892
+ // the second field of the CFA.
2893
+ addr_t pc_location = cfa + ptr_size;
2894
+ addr_t pc_value = LLDB_INVALID_ADDRESS;
2895
+ process.ReadMemory (pc_location, &pc_value, ptr_size, error);
2896
+ if (error.Fail ())
2897
+ return {};
2898
+
2899
+ Address pc;
2900
+ Target &target = process.GetTarget ();
2901
+ pc.SetLoadAddress (pc_value, &target);
2902
+ if (!pc.IsValid ())
2903
+ return {};
2904
+
2905
+ SymbolContext sc;
2906
+ if (!pc.CalculateSymbolContext (&sc,
2907
+ eSymbolContextFunction | eSymbolContextSymbol))
2908
+ return {};
2909
+ if (!sc.symbol && !sc.function )
2910
+ return {};
2911
+
2912
+ auto prologue_size = sc.symbol ? sc.symbol ->GetPrologueByteSize ()
2913
+ : sc.function ->GetPrologueByteSize ();
2914
+ return pc_value + prologue_size;
2915
+ }
2834
2916
} // namespace lldb_private
0 commit comments