3737#include " lldb/Interpreter/CommandObject.h"
3838#include " lldb/Interpreter/CommandObjectMultiword.h"
3939#include " lldb/Interpreter/CommandReturnObject.h"
40+ #include " lldb/Symbol/FuncUnwinders.h"
4041#include " lldb/Symbol/Function.h"
4142#include " lldb/Symbol/VariableList.h"
4243#include " lldb/Target/RegisterContext.h"
44+ #include " lldb/Target/UnwindLLDB.h"
4345#include " lldb/Utility/LLDBLog.h"
4446#include " lldb/Utility/Log.h"
4547#include " lldb/Utility/OptionParsing.h"
@@ -2542,6 +2544,8 @@ struct AsyncUnwindRegisterNumbers {
25422544 // / frames below us as they need to react differently. There is no good way to
25432545 // / expose this, so we set another dummy register to communicate this state.
25442546 uint32_t dummy_regnum;
2547+
2548+ RegisterKind GetRegisterKind () const { return lldb::eRegisterKindDWARF; }
25452549};
25462550} // namespace
25472551
@@ -2584,37 +2588,148 @@ lldb::addr_t SwiftLanguageRuntime::GetAsyncContext(RegisterContext *regctx) {
25842588 return LLDB_INVALID_ADDRESS;
25852589}
25862590
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 ());
26082661
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);
26182733}
26192734
26202735// Examine the register state and detect the transition from a real
@@ -2625,6 +2740,11 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
26252740 RegisterContext *regctx,
26262741 bool &behaves_like_zeroth_frame) {
26272742 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+ };
26282748
26292749 Target &target (process_sp->GetTarget ());
26302750 auto arch = target.GetArchitecture ();
@@ -2644,12 +2764,6 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
26442764 return UnwindPlanSP ();
26452765 }
26462766
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.
26532767 Address pc;
26542768 pc.SetLoadAddress (regctx->GetPC (), &target);
26552769 SymbolContext sc;
@@ -2659,111 +2773,58 @@ SwiftLanguageRuntime::GetRuntimeUnwindPlan(ProcessSP process_sp,
26592773 return UnwindPlanSP ();
26602774
26612775 Address func_start_addr;
2662- uint32_t prologue_size;
26632776 ConstString mangled_name;
26642777 if (sc.function ) {
26652778 func_start_addr = sc.function ->GetAddressRange ().GetBaseAddress ();
2666- prologue_size = sc.function ->GetPrologueByteSize ();
26672779 mangled_name = sc.function ->GetMangled ().GetMangledName ();
26682780 } else if (sc.symbol ) {
26692781 func_start_addr = sc.symbol ->GetAddress ();
2670- prologue_size = sc.symbol ->GetPrologueByteSize ();
26712782 mangled_name = sc.symbol ->GetMangled ().GetMangledName ();
26722783 } else {
26732784 return UnwindPlanSP ();
26742785 }
26752786
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 ();
26942789
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).
27022795 bool indirect_context =
27032796 IsSwiftAsyncAwaitResumePartialFunctionSymbol (mangled_name.GetStringRef ());
27042797
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+
27052807 UnwindPlan::RowSP row (new UnwindPlan::Row);
27062808 const int32_t ptr_size = 8 ;
27072809 row->SetOffset (0 );
27082810
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 );
27652825
2766- if (pc_after_prologue)
2826+ if (std::optional<addr_t > pc_after_prologue =
2827+ TrySkipVirtualParentProlog (*async_ctx, *process_sp))
27672828 row->SetRegisterLocationToIsConstant (regnums->pc_regnum , *pc_after_prologue,
27682829 false );
27692830 else
0 commit comments