[Lldb-commits] [lldb] [lldb][NFC] Split RegisterContextUnwind::SavedLocationForRegister (PR #139817)
Pavel Labath via lldb-commits
lldb-commits at lists.llvm.org
Wed May 14 03:01:33 PDT 2025
================
@@ -1243,303 +1243,285 @@ bool RegisterContextUnwind::IsTrapHandlerSymbol(
return false;
}
-// Answer the question: Where did THIS frame save the CALLER frame ("previous"
-// frame)'s register value?
-
-enum UnwindLLDB::RegisterSearchResult
-RegisterContextUnwind::SavedLocationForRegister(
- uint32_t lldb_regnum,
- lldb_private::UnwindLLDB::ConcreteRegisterLocation ®loc) {
+/// Search this stack frame's UnwindPlans for the AbstractRegisterLocation
+/// for this register.
+///
+/// \param[out] kind
+/// Set to the RegisterKind of the UnwindPlan which is the basis for
+/// the returned AbstractRegisterLocation; if the location is in terms
+/// of another register number, this Kind is needed to interpret it
+/// correctly.
+///
+/// \return
+/// An empty optional indicaTes that there was an error in processing
+/// the request.
+///
+/// If there is no unwind rule for a volatile (caller-preserved) register,
+/// the returned AbstractRegisterLocation will be IsUndefined,
+/// indicating that we should stop searching.
+///
+/// If there is no unwind rule for a non-volatile (callee-preserved)
+/// register, the returned AbstractRegisterLocation will be IsSame.
+/// In frame 0, IsSame means get the value from the live register context.
+/// Else it means to continue descending down the stack to more-live frames
+/// looking for a location/value.
+///
+/// If an AbstractRegisterLocation is found in an UnwindPlan, that will
+/// be returned, with no consideration of the current ABI rules for
+/// registers. Functions using an alternate ABI calling convention
+/// will work as long as the UnwindPlans are exhaustive about what
+/// registers are volatile/non-volatile.
+std::optional<UnwindPlan::Row::AbstractRegisterLocation>
+RegisterContextUnwind::GetAbstractRegisterLocation(uint32_t lldb_regnum,
+ lldb::RegisterKind &kind) {
RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum);
Log *log = GetLog(LLDBLog::Unwind);
- // Have we already found this register location?
- if (!m_registers.empty()) {
- std::map<uint32_t,
- lldb_private::UnwindLLDB::ConcreteRegisterLocation>::const_iterator
- iterator;
- iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB));
- if (iterator != m_registers.end()) {
- regloc = iterator->second;
- UnwindLogMsg("supplying caller's saved %s (%d)'s location, cached",
- regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
- return UnwindLLDB::RegisterSearchResult::eRegisterFound;
- }
- }
-
- // Look through the available UnwindPlans for the register location.
-
UnwindPlan::Row::AbstractRegisterLocation unwindplan_regloc;
- bool have_unwindplan_regloc = false;
- RegisterKind unwindplan_registerkind = kNumRegisterKinds;
+ // First, try to find a register location via the FastUnwindPlan
if (m_fast_unwind_plan_sp) {
const UnwindPlan::Row *active_row =
m_fast_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
- unwindplan_registerkind = m_fast_unwind_plan_sp->GetRegisterKind();
- if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) {
+ kind = m_fast_unwind_plan_sp->GetRegisterKind();
+ if (regnum.GetAsKind(kind) == LLDB_INVALID_REGNUM) {
UnwindLogMsg("could not convert lldb regnum %s (%d) into %d RegisterKind "
"reg numbering scheme",
regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
- (int)unwindplan_registerkind);
- return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
+ (int)kind);
+ return {};
}
// The architecture default unwind plan marks unknown registers as
// Undefined so that we don't forward them up the stack when a
// jitted stack frame may have overwritten them. But when the
// arch default unwind plan is used as the Fast Unwind Plan, we
// need to recognize this & switch over to the Full Unwind Plan
- // to see what unwind rule that (more knoweldgeable, probably)
- // UnwindPlan has. If the full UnwindPlan says the register
- // location is Undefined, then it really is.
- if (active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
+ // to see what unwind rule that (more knowledgeable, probably)
+ // UnwindPlan has.
+ if (active_row->GetRegisterInfo(regnum.GetAsKind(kind),
unwindplan_regloc) &&
!unwindplan_regloc.IsUndefined()) {
UnwindLogMsg(
"supplying caller's saved %s (%d)'s location using FastUnwindPlan",
regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
- have_unwindplan_regloc = true;
+ return unwindplan_regloc;
}
}
- if (!have_unwindplan_regloc) {
- // m_full_unwind_plan_sp being NULL means that we haven't tried to find a
- // full UnwindPlan yet
- bool got_new_full_unwindplan = false;
- if (!m_full_unwind_plan_sp) {
- m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
- got_new_full_unwindplan = true;
+ // Second, try to find a register location via the FullUnwindPlan.
+ bool got_new_full_unwindplan = false;
+ if (!m_full_unwind_plan_sp) {
+ m_full_unwind_plan_sp = GetFullUnwindPlanForFrame();
+ got_new_full_unwindplan = true;
+ }
+ if (m_full_unwind_plan_sp) {
+ RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+
+ const UnwindPlan::Row *active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(
+ m_current_offset_backed_up_one);
+ kind = m_full_unwind_plan_sp->GetRegisterKind();
+
+ if (got_new_full_unwindplan && active_row && log) {
+ StreamString active_row_strm;
+ ExecutionContext exe_ctx(m_thread.shared_from_this());
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread,
+ m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
+ UnwindLogMsg("Using full unwind plan '%s'",
+ m_full_unwind_plan_sp->GetSourceName().AsCString());
+ UnwindLogMsg("active row: %s", active_row_strm.GetData());
}
- if (m_full_unwind_plan_sp) {
- RegisterNumber pc_regnum(m_thread, eRegisterKindGeneric,
- LLDB_REGNUM_GENERIC_PC);
+ if (regnum.GetAsKind(kind) == LLDB_INVALID_REGNUM) {
+ if (kind == eRegisterKindGeneric)
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into "
+ "eRegisterKindGeneric reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
+ else
+ UnwindLogMsg("could not convert lldb regnum %s (%d) into %d "
+ "RegisterKind reg numbering scheme",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ (int)kind);
+ return {};
+ }
- const UnwindPlan::Row *active_row =
- m_full_unwind_plan_sp->GetRowForFunctionOffset(
- m_current_offset_backed_up_one);
- unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind();
+ if (regnum.IsValid() && active_row &&
+ active_row->GetRegisterInfo(regnum.GetAsKind(kind),
+ unwindplan_regloc)) {
+ UnwindLogMsg(
+ "supplying caller's saved %s (%d)'s location using %s UnwindPlan",
+ regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+ return unwindplan_regloc;
+ }
- if (got_new_full_unwindplan && active_row && log) {
- StreamString active_row_strm;
- ExecutionContext exe_ctx(m_thread.shared_from_this());
- active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(),
- &m_thread,
- m_start_pc.GetLoadAddress(exe_ctx.GetTargetPtr()));
- UnwindLogMsg("Using full unwind plan '%s'",
- m_full_unwind_plan_sp->GetSourceName().AsCString());
- UnwindLogMsg("active row: %s", active_row_strm.GetData());
- }
- RegisterNumber return_address_reg;
-
- // If we're fetching the saved pc and this UnwindPlan defines a
- // ReturnAddress register (e.g. lr on arm), look for the return address
- // register number in the UnwindPlan's row.
- if (pc_regnum.IsValid() && pc_regnum == regnum &&
- m_full_unwind_plan_sp->GetReturnAddressRegister() !=
- LLDB_INVALID_REGNUM) {
- // If this is a trap handler frame, we should have access to
- // the complete register context when the interrupt/async
- // signal was received, we should fetch the actual saved $pc
- // value instead of the Return Address register.
- // If $pc is not available, fall back to the RA reg.
- UnwindPlan::Row::AbstractRegisterLocation scratch;
- if (m_frame_type == eTrapHandlerFrame && active_row &&
- active_row->GetRegisterInfo(
- pc_regnum.GetAsKind(unwindplan_registerkind), scratch)) {
- UnwindLogMsg("Providing pc register instead of rewriting to "
- "RA reg because this is a trap handler and there is "
- "a location for the saved pc register value.");
- } else {
- return_address_reg.init(
- m_thread, m_full_unwind_plan_sp->GetRegisterKind(),
- m_full_unwind_plan_sp->GetReturnAddressRegister());
- regnum = return_address_reg;
- UnwindLogMsg("requested caller's saved PC but this UnwindPlan uses a "
- "RA reg; getting %s (%d) instead",
- return_address_reg.GetName(),
- return_address_reg.GetAsKind(eRegisterKindLLDB));
- }
+ // When asking for the caller's pc, and did not find a register
+ // location for PC above in the UnwindPlan. Check if we have a
+ // Return Address register on this target.
+ //
+ // On a Return Address Register architecture like arm/mips/riscv,
+ // the caller's pc is in the RA register, and will be spilled to
+ // stack before any other function is called. If no function
+ // has been called yet, the return address may still be in the
+ // live RA reg.
+ //
+ // There's a lot of variety of what we might see in an UnwindPlan.
+ // We may have
+ // ra=IsSame {unncessary}
+ // ra=StackAddr {caller's return addr spilled to stack}
+ // or no reg location for pc or ra at all, in a frameless function -
+ // the caller's return address is in live ra reg.
+ //
+ // If a function has been interrupted in a non-call way --
+ // async signal/sigtramp, or a hardware exception / interrupt / fault --
+ // then the "pc" and "ra" are two distinct values, and must be
+ // handled separately. The "pc" is the pc value at the point
+ // the function was interrupted. The "ra" is the return address
+ // register value at that point.
+ // The UnwindPlan for the sigtramp/trap handler will normally have
+ // register loations for both pc and lr, and so we'll have already
+ // fetched them above.
+ if (pc_regnum.IsValid() && pc_regnum == regnum) {
+ uint32_t return_address_regnum = LLDB_INVALID_REGNUM;
+
+ // Get the return address register number from the UnwindPlan
+ // or the register set definition.
+ if (m_full_unwind_plan_sp->GetReturnAddressRegister() !=
+ LLDB_INVALID_REGNUM) {
+ return_address_regnum =
+ m_full_unwind_plan_sp->GetReturnAddressRegister();
} else {
- if (regnum.GetAsKind(unwindplan_registerkind) == LLDB_INVALID_REGNUM) {
- if (unwindplan_registerkind == eRegisterKindGeneric) {
- UnwindLogMsg("could not convert lldb regnum %s (%d) into "
- "eRegisterKindGeneric reg numbering scheme",
- regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
- } else {
- UnwindLogMsg("could not convert lldb regnum %s (%d) into %d "
- "RegisterKind reg numbering scheme",
- regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
- (int)unwindplan_registerkind);
- }
- return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
- }
- }
-
- // Check if the active_row has a register location listed.
- if (regnum.IsValid() && active_row &&
- active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
- unwindplan_regloc)) {
- have_unwindplan_regloc = true;
- UnwindLogMsg(
- "supplying caller's saved %s (%d)'s location using %s UnwindPlan",
- regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB),
- m_full_unwind_plan_sp->GetSourceName().GetCString());
+ RegisterNumber arch_default_ra_regnum(m_thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA);
+ return_address_regnum = arch_default_ra_regnum.GetAsKind(kind);
}
- // This is frame 0 and we're retrieving the PC and it's saved in a Return
- // Address register and it hasn't been saved anywhere yet -- that is,
- // it's still live in the actual register. Handle this specially.
- if (!have_unwindplan_regloc && return_address_reg.IsValid() &&
- return_address_reg.GetAsKind(eRegisterKindLLDB) !=
- LLDB_INVALID_REGNUM) {
- if (IsFrameZero()) {
- lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc;
- new_regloc.type = UnwindLLDB::ConcreteRegisterLocation::
- eRegisterInLiveRegisterContext;
- new_regloc.location.register_number =
- return_address_reg.GetAsKind(eRegisterKindLLDB);
- m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
- regloc = new_regloc;
- UnwindLogMsg("supplying caller's register %s (%d) from the live "
- "RegisterContext at frame 0, saved in %d",
+ // This system is using a return address register.
+ if (return_address_regnum != LLDB_INVALID_REGNUM) {
+ RegisterNumber return_address_reg;
+ return_address_reg.init(m_thread,
+ m_full_unwind_plan_sp->GetRegisterKind(),
+ return_address_regnum);
+ UnwindLogMsg("requested caller's saved PC but this UnwindPlan uses a "
+ "RA reg; getting %s (%d) instead",
+ return_address_reg.GetName(),
+ return_address_reg.GetAsKind(eRegisterKindLLDB));
+
+ // Do we have a location for the ra register?
+ if (active_row &&
+ active_row->GetRegisterInfo(return_address_reg.GetAsKind(kind),
+ unwindplan_regloc)) {
+ UnwindLogMsg("supplying caller's saved %s (%d)'s location using "
+ "%s UnwindPlan",
return_address_reg.GetName(),
return_address_reg.GetAsKind(eRegisterKindLLDB),
- return_address_reg.GetAsKind(eRegisterKindLLDB));
- return UnwindLLDB::RegisterSearchResult::eRegisterFound;
- } else if (BehavesLikeZerothFrame()) {
- // This function was interrupted asynchronously -- it faulted,
- // an async interrupt, a timer fired, a debugger expression etc.
- // The caller's pc is in the Return Address register, but the
- // UnwindPlan for this function may have no location rule for
- // the RA reg.
- // This means that the caller's return address is in the RA reg
- // when the function was interrupted--descend down one stack frame
- // to retrieve it from the trap handler's saved context.
- unwindplan_regloc.SetSame();
- have_unwindplan_regloc = true;
- }
- }
-
- // If this architecture stores the return address in a register (it
- // defines a Return Address register) and we're on a non-zero stack frame
- // and the Full UnwindPlan says that the pc is stored in the
- // RA registers (e.g. lr on arm), then we know that the full unwindplan is
- // not trustworthy -- this
- // is an impossible situation and the instruction emulation code has
- // likely been misled. If this stack frame meets those criteria, we need
- // to throw away the Full UnwindPlan that the instruction emulation came
- // up with and fall back to the architecture's Default UnwindPlan so the
- // stack walk can get past this point.
-
- // Special note: If the Full UnwindPlan was generated from the compiler,
- // don't second-guess it when we're at a call site location.
-
- // arch_default_ra_regnum is the return address register # in the Full
- // UnwindPlan register numbering
- RegisterNumber arch_default_ra_regnum(m_thread, eRegisterKindGeneric,
- LLDB_REGNUM_GENERIC_RA);
-
- if (arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) !=
- LLDB_INVALID_REGNUM &&
- pc_regnum == regnum && unwindplan_regloc.IsInOtherRegister() &&
- unwindplan_regloc.GetRegisterNumber() ==
- arch_default_ra_regnum.GetAsKind(unwindplan_registerkind) &&
- m_full_unwind_plan_sp->GetSourcedFromCompiler() != eLazyBoolYes &&
- !m_all_registers_available) {
- UnwindLogMsg("%s UnwindPlan tried to restore the pc from the link "
- "register but this is a non-zero frame",
- m_full_unwind_plan_sp->GetSourceName().GetCString());
-
- // Throw away the full unwindplan; install the arch default unwindplan
- if (ForceSwitchToFallbackUnwindPlan()) {
- // Update for the possibly new unwind plan
- unwindplan_registerkind = m_full_unwind_plan_sp->GetRegisterKind();
- const UnwindPlan::Row *active_row =
- m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
-
- // Sanity check: Verify that we can fetch a pc value and CFA value
- // with this unwind plan
-
- RegisterNumber arch_default_pc_reg(m_thread, eRegisterKindGeneric,
- LLDB_REGNUM_GENERIC_PC);
- bool can_fetch_pc_value = false;
- bool can_fetch_cfa = false;
- addr_t cfa_value;
- if (active_row) {
- if (arch_default_pc_reg.GetAsKind(unwindplan_registerkind) !=
- LLDB_INVALID_REGNUM &&
- active_row->GetRegisterInfo(
- arch_default_pc_reg.GetAsKind(unwindplan_registerkind),
- unwindplan_regloc)) {
- can_fetch_pc_value = true;
- }
- if (ReadFrameAddress(unwindplan_registerkind,
- active_row->GetCFAValue(), cfa_value)) {
- can_fetch_cfa = true;
- }
- }
-
- have_unwindplan_regloc = can_fetch_pc_value && can_fetch_cfa;
+ m_full_unwind_plan_sp->GetSourceName().GetCString());
+ // If we have "ra=IsSame", rewrite to "ra=InRegister(ra)" because the
+ // calling function thinks it is fetching "pc" and if we return an
+ // IsSame register location, it will try to read pc.
+ if (unwindplan_regloc.IsSame())
+ unwindplan_regloc.SetInRegister(return_address_reg.GetAsKind(kind));
+ return unwindplan_regloc;
} else {
- // We were unable to fall back to another unwind plan
- have_unwindplan_regloc = false;
+ // No unwind rule for the return address reg on frame 0, or an
+ // interrupted function, means that the caller's address is still in
+ // RA reg (0th frame) or the trap handler below this one (sigtramp
+ // etc) has a save location for the RA reg.
+ if (BehavesLikeZerothFrame()) {
+ unwindplan_regloc.SetInRegister(return_address_reg.GetAsKind(kind));
+ return unwindplan_regloc;
+ }
}
}
}
}
ExecutionContext exe_ctx(m_thread.shared_from_this());
Process *process = exe_ctx.GetProcessPtr();
- if (!have_unwindplan_regloc) {
- // If the UnwindPlan failed to give us an unwind location for this
- // register, we may be able to fall back to some ABI-defined default. For
- // example, some ABIs allow to determine the caller's SP via the CFA. Also,
- // the ABI may set volatile registers to the undefined state.
- ABI *abi = process ? process->GetABI().get() : nullptr;
- if (abi) {
- const RegisterInfo *reg_info =
- GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB));
- if (reg_info &&
- abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) {
+
+ // Third, try finding a register location via the ABI
+ // FallbackRegisterLocation.
+ //
+ // If the UnwindPlan failed to give us an unwind location for this
+ // register, we may be able to fall back to some ABI-defined default. For
+ // example, some ABIs allow to determine the caller's SP via the CFA. Also,
+ // the ABI willset volatile registers to the undefined state.
+ ABI *abi = process ? process->GetABI().get() : nullptr;
+ if (abi) {
+ const RegisterInfo *reg_info =
+ GetRegisterInfoAtIndex(regnum.GetAsKind(eRegisterKindLLDB));
+ if (reg_info &&
+ abi->GetFallbackRegisterLocation(reg_info, unwindplan_regloc)) {
+ if (!unwindplan_regloc.IsUndefined())
UnwindLogMsg(
"supplying caller's saved %s (%d)'s location using ABI default",
regnum.GetName(), regnum.GetAsKind(eRegisterKindLLDB));
- have_unwindplan_regloc = true;
- }
+ // ABI defined volatile registers with no register location
+ // will be returned as IsUndefined, stopping the search down
+ // the stack.
+ return unwindplan_regloc;
}
}
- if (!have_unwindplan_regloc) {
- if (IsFrameZero()) {
- // This is frame 0 - we should return the actual live register context
- // value
- lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc;
- new_regloc.type =
- UnwindLLDB::ConcreteRegisterLocation::eRegisterInLiveRegisterContext;
- new_regloc.location.register_number = regnum.GetAsKind(eRegisterKindLLDB);
- m_registers[regnum.GetAsKind(eRegisterKindLLDB)] = new_regloc;
- regloc = new_regloc;
- UnwindLogMsg("supplying caller's register %s (%d) from the live "
- "RegisterContext at frame 0",
+ // We have no AbstractRegisterLocation, and the ABI says this is a
+ // non-volatile / callee-preserved register. Continue down the stack
+ // or to frame 0 & the live RegisterContext.
+ std::string unwindplan_name;
+ if (m_full_unwind_plan_sp) {
+ unwindplan_name += "via '";
+ unwindplan_name += m_full_unwind_plan_sp->GetSourceName().AsCString();
+ unwindplan_name += "'";
+ }
+ UnwindLogMsg("no save location for %s (%d) %s", regnum.GetName(),
+ regnum.GetAsKind(eRegisterKindLLDB), unwindplan_name.c_str());
+
+ unwindplan_regloc.SetSame();
+ return unwindplan_regloc;
+}
+
+// Answer the question: Where did THIS frame save the CALLER frame ("previous"
+// frame)'s register value?
+
+enum UnwindLLDB::RegisterSearchResult
+RegisterContextUnwind::SavedLocationForRegister(
+ uint32_t lldb_regnum,
+ lldb_private::UnwindLLDB::ConcreteRegisterLocation ®loc) {
+ RegisterNumber regnum(m_thread, eRegisterKindLLDB, lldb_regnum);
+ Log *log = GetLog(LLDBLog::Unwind);
+
+ // Have we already found this register location?
+ if (!m_registers.empty()) {
+ std::map<uint32_t,
+ lldb_private::UnwindLLDB::ConcreteRegisterLocation>::const_iterator
+ iterator;
+ iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB));
----------------
labath wrote:
this is one of the few officially sanctioned uses of auto
```suggestion
auto iterator = m_registers.find(regnum.GetAsKind(eRegisterKindLLDB));
```
https://github.com/llvm/llvm-project/pull/139817
More information about the lldb-commits
mailing list