[Lldb-commits] [lldb] [lldb] Allow fetching of RA register when above fault handler (PR #98566)

Jason Molenda via lldb-commits lldb-commits at lists.llvm.org
Thu Jul 11 17:14:12 PDT 2024


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

In RegisterContextUnwind::SavedLocationForRegister we have special logic for retrieving the Return Address register when it has the caller's return address in it. An example would be the lr register on AArch64.

This register is never retrieved from a newer stack frame because it is necessarly overwritten by a normal ABI function call.  We allow frame 0 to provide its lr value to get the caller's return address, if it has not been overwritten/saved to stack yet.

When a function is interrupted asynchronously by a POSIX signal (sigtramp), or a fault handler more generally, the sigtramp/fault handler has the entire register context available. In this situation, if the fault handler is frame 0, the function that was async interrupted is frame 1 and frame 2's return address may still be stored in lr.  We need to get the lr value for frame 1 from the fault handler in frame 0, to get the return address for frame 2.

Without this fix, a frameless function that faults in a firmware environment (that's where we've seen this issue most commonly) hasn't spilled lr to stack, so we need to retrieve it from the fault handler's full-register-context to find the caller of the frameless function that faulted.

It's an unsurprising fix, all of the work was finding exactly where in RegisterContextUnwind we were only allowing RA register use for frame 0, when it should have been frame 0 or above a fault handler function.

rdar://127518945

>From 48a06a9d85d2ca07e50bfda9002350d3ab157ed9 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmolenda at apple.com>
Date: Thu, 11 Jul 2024 17:03:13 -0700
Subject: [PATCH] [lldb] Allow fetching of RA register when above fault handler

In RegisterContextUnwind::SavedLocationForRegister we have special
logic for retrieving the Return Address register when it has
the caller's return address in it. An example would be the lr
register on AArch64.

This register is never retrieved from a newer stack frame because
it is necessarly overwritten by a normal ABI function call.  We
allow frame 0 to provide its lr value to get the caller's return
address, if it has not been overwritten/saved to stack yet.

When a function is interrupted asynchronously by a POSIX signal
(sigtramp), or a fault handler more generally, the sigtramp/fault
handler has the entire register context available. In this situation,
if the fault handler is frame 0, the function that was async
interrupted is frame 1 and frame 2's return address may still be
stored in lr.  We need to get the lr value for frame 1 from the
fault handler in frame 0, to get the return address for frame 2.

Without this fix, a frameless function that faults in a firmware
environment (that's where we've seen this issue most commonly)
hasn't spilled lr to stack, so we need to retrieve it from the
fault handler's full-register-context to find the caller of the
frameless function that faulted.

It's an unsurprising fix, all of the work was finding exactly where
in RegisterContextUnwind we were only allowing RA register use for
frame 0, when it should have been frame 0 or above a fault handler
function.

rdar://127518945
---
 lldb/source/Target/RegisterContextUnwind.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index 95e8abd763d53..bc8081f4e3b31 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -1401,7 +1401,7 @@ RegisterContextUnwind::SavedLocationForRegister(
       // it's still live in the actual register. Handle this specially.
 
       if (!have_unwindplan_regloc && return_address_reg.IsValid() &&
-          IsFrameZero()) {
+          BehavesLikeZerothFrame()) {
         if (return_address_reg.GetAsKind(eRegisterKindLLDB) !=
             LLDB_INVALID_REGNUM) {
           lldb_private::UnwindLLDB::RegisterLocation new_regloc;



More information about the lldb-commits mailing list