https://github.com/jasonmolenda created 
https://github.com/llvm/llvm-project/pull/138805

When a frameless function faults or is interrupted asynchronously, the 
UnwindPlan MAY have no register location rule for the return address register 
(lr on arm64); the value is simply live in the lr register when it was 
interrupted, and the frame below this on the stack -- e.g. sigtramp on a Unix 
system -- has the full register context, including that register.

RegisterContextUnwind::SavedLocationForRegister, when asked to find the 
caller's pc value, will first see if there is a pc register location.  If there 
isn't, on a Return Address Register architecture like arm/mips/riscv, we 
rewrite the register request from "pc" to "RA register", and search for a 
location.

On frame 0 (the live frame) and an interrupted frame, the UnwindPlan may have 
no register location rule for the RA Reg, that is valid. A frameless function 
that never calls another may simply keep the return address in the live 
register the whole way.  Our instruction emulation unwind plans explicitly add 
a rule (see Pavel's May 2024 change 
https://github.com/llvm/llvm-project/pull/91321 ), but an UnwindPlan sourced 
from debug_frame may not.

I've got a case where this exactly happens - clang debug_frame for arm64 where 
there is no register location for the lr in a frameless function.  There is a 
fault in the middle of this frameless function and we only get the lr value 
from the fault handler below this frame if lr has a register location of 
`IsSame`, in line with Pavel's 2024 change.

Similar to how we see a request of the RA Reg from frame 0 after failing to 
find an unwind location for the pc register, the same style of special casing 
is needed when this is a function that was interrupted.

Without this change, we can find the pc of the frame that was executing when it 
was interrupted, but we need $lr to find its caller, and we don't descend down 
to the trap handler to get that value, truncating the stack.

rdar://145614545

>From 4fc9acd03a58a3617b113352c48e5dd2fdb58eda Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Tue, 6 May 2025 22:37:17 -0700
Subject: [PATCH] [lldb] Provide lr value in faulting frame on arm64

When a frameless function faults or is interrupted asynchronously,
the UnwindPlan MAY have no register location rule for the return
address register (lr on arm64); the value is simply live in the
lr register when it was interrupted, and the frame below this on
the stack -- e.g. sigtramp on a Unix system -- has the full
register context, including that register.

RegisterContextUnwind::SavedLocationForRegister, when asked to find
the caller's pc value, will first see if there is a pc register
location.  If there isn't, on a Return Address Register architecture
like arm/mips/riscv, we rewrite the register request from "pc" to
"RA register", and search for a location.

On frame 0 (the live frame) and an interrupted frame, the UnwindPlan
may have no register location rule for the RA Reg, that is valid.
A frameless function that never calls another may simply keep the
return address in the live register the whole way.  Our instruction
emulation unwind plans explicitly add a rule (see Pavel's May 2024
change https://github.com/llvm/llvm-project/pull/91321 ), but an
UnwindPlan sourced from debug_frame may not.

I've got a case where this exactly happens - clang debug_frame for
arm64 where there is no register location for the lr in a frameless
function.  There is a fault in the middle of this frameless function
and we only get the lr value from the fault handler below this frame
if lr has a register location of `IsSame`, in line with Pavel's
2024 change.

Similar to how we see a request of the RA Reg from frame 0 after
failing to find an unwind location for the pc register, the same
style of special casing is needed when this is a function that
was interrupted.

Without this change, we can find the pc of the frame that was
executing when it was interrupted, but we need $lr to find its
caller, and we don't descend down to the trap handler to get that
value, truncating the stack.

rdar://145614545
---
 lldb/source/Target/RegisterContextUnwind.cpp | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/lldb/source/Target/RegisterContextUnwind.cpp 
b/lldb/source/Target/RegisterContextUnwind.cpp
index 3ed49e12476dd..23a86bee2518b 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -1377,6 +1377,7 @@ RegisterContextUnwind::SavedLocationForRegister(
         }
       }
 
+      // Check if the active_row has a register location listed.
       if (regnum.IsValid() &&
           
active_row->GetRegisterInfo(regnum.GetAsKind(unwindplan_registerkind),
                                       unwindplan_regloc)) {
@@ -1390,11 +1391,10 @@ RegisterContextUnwind::SavedLocationForRegister(
       // 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() &&
-          IsFrameZero()) {
-        if (return_address_reg.GetAsKind(eRegisterKindLLDB) !=
-            LLDB_INVALID_REGNUM) {
+          return_address_reg.GetAsKind(eRegisterKindLLDB) !=
+              LLDB_INVALID_REGNUM) {
+        if (IsFrameZero()) {
           lldb_private::UnwindLLDB::ConcreteRegisterLocation new_regloc;
           new_regloc.type = UnwindLLDB::ConcreteRegisterLocation::
               eRegisterInLiveRegisterContext;
@@ -1408,6 +1408,17 @@ RegisterContextUnwind::SavedLocationForRegister(
                        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;
         }
       }
 

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to