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

>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 01/14] [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;
         }
       }
 

>From b10162deb49ecddca6439665c2b8ea1995fdd81f Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Wed, 7 May 2025 23:24:17 -0700
Subject: [PATCH 02/14] Add the sources for an API test case to be written

---
 .../interrupt-and-trap-funcs.s                | 92 +++++++++++++++++++
 .../macosx/unwind-frameless-faulted/main.c    |  6 ++
 2 files changed, 98 insertions(+)
 create mode 100644 
lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
 create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/main.c

diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
new file mode 100644
index 0000000000000..23eb35c2b31ca
--- /dev/null
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
@@ -0,0 +1,92 @@
+#define DW_CFA_register 0x9
+#define ehframe_x22  22
+#define ehframe_x23  23
+#define ehframe_pc 32
+
+  .section  __TEXT,__text,regular,pure_instructions
+
+//--------------------------------------
+// to_be_interrupted() a frameless function that does a non-ABI
+// function call ("is interrupted/traps" simulated) to trap().
+// Before it branches to trap(), it puts its return address in
+// x23.  trap() knows to branch back to $x23 when it has finished.
+//--------------------------------------
+  .globl  _to_be_interrupted
+  .p2align  2
+_to_be_interrupted:
+  .cfi_startproc
+
+  // This is a garbage entry to ensure that eh_frame is emitted,
+  // it isn't used for anything.  If there's no eh_frame, lldb
+  // can do an assembly emulation scan and add a rule for $lr
+  // which won't expose the issue at hand.
+  .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23
+  mov x24, x0
+  add x24, x24, #1
+
+  adrp x23, L_.return@PAGE        ; put return address in x4
+  add x23, x23, L_.return@PAGEOFF
+
+  b _trap                     ; branch to trap handler, fake async interrupt
+
+L_.return:
+  mov x0, x24
+  ret
+  .cfi_endproc
+  
+
+
+//--------------------------------------
+// trap() trap handler function, sets up stack frame
+// with special unwind rule for the pc value of the
+// "interrupted" stack frame (it's in x23), then calls
+// break_to_debugger().
+//--------------------------------------
+  .globl  _trap
+  .p2align  2
+_trap:                                  
+  .cfi_startproc
+  .cfi_signal_frame
+
+  // The pc value when we were interrupted is in x23
+  .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23
+
+  // standard prologue save of fp & lr so we can call puts()
+  sub sp, sp, #32
+  stp x29, x30, [sp, #16]
+  add x29, sp, #16
+  .cfi_def_cfa w29, 16
+  .cfi_offset w30, -8
+  .cfi_offset w29, -16
+
+  bl _break_to_debugger
+
+  ldp x29, x30, [sp, #16]
+  add sp, sp, #32
+
+  // jump back to $x23 to resume execution of to_be_interrupted
+  br x23
+  .cfi_endproc
+
+//--------------------------------------
+// break_to_debugger() executes a BRK instruction
+//--------------------------------------
+  .globl _break_to_debugger
+  .p2align  2
+_break_to_debugger:                                  
+  .cfi_startproc
+
+  // standard prologue save of fp & lr so we can call puts()
+  sub sp, sp, #32
+  stp x29, x30, [sp, #16]
+  add x29, sp, #16
+  .cfi_def_cfa w29, 16
+  .cfi_offset w30, -8
+  .cfi_offset w29, -16
+
+  brk #0xf000   ;; __builtin_debugtrap aarch64 instruction
+
+  ldp x29, x30, [sp, #16]
+  add sp, sp, #32
+  ret
+  .cfi_endproc
diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c 
b/lldb/test/API/macosx/unwind-frameless-faulted/main.c
new file mode 100644
index 0000000000000..e121809f25478
--- /dev/null
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c
@@ -0,0 +1,6 @@
+int to_be_interrupted(int);
+int main() {
+  int c = 10;
+  c = to_be_interrupted(c);
+  return c;
+}

>From 508b9cb404072263a6ed52a75b3b7aad5d949510 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Wed, 7 May 2025 23:24:58 -0700
Subject: [PATCH 03/14] Add a log msg when a frame is marked as a trap handler
 via the UnwindPlan.  And use the trap handler flag for zeroth frame so we can
 properly unwind a frameless function that was interrupted while in the trap
 handler function.

---
 lldb/source/Target/RegisterContextUnwind.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/lldb/source/Target/RegisterContextUnwind.cpp 
b/lldb/source/Target/RegisterContextUnwind.cpp
index 51c0f56e9bc1e..cf4b96c6eda9f 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -248,6 +248,7 @@ void RegisterContextUnwind::InitializeZerothFrame() {
     active_row =
         m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
     row_register_kind = m_full_unwind_plan_sp->GetRegisterKind();
+    PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
     if (active_row && log) {
       StreamString active_row_strm;
       active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(), &m_thread,
@@ -1933,6 +1934,7 @@ void 
RegisterContextUnwind::PropagateTrapHandlerFlagFromUnwindPlan(
   }
 
   m_frame_type = eTrapHandlerFrame;
+  UnwindLogMsg("This frame is marked as a trap handler via its UnwindPlan");
 
   if (m_current_offset_backed_up_one != m_current_offset) {
     // We backed up the pc by 1 to compute the symbol context, but

>From d8843fe86c0eaaa1483bed0317f832b7630d8548 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Wed, 7 May 2025 23:28:40 -0700
Subject: [PATCH 04/14] Update assembly a bit.

---
 .../interrupt-and-trap-funcs.s                      | 13 ++-----------
 1 file changed, 2 insertions(+), 11 deletions(-)

diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
index 23eb35c2b31ca..708ad9ed56a1c 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
@@ -51,7 +51,8 @@ _trap:
   // The pc value when we were interrupted is in x23
   .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23
 
-  // standard prologue save of fp & lr so we can call puts()
+  // standard prologue save of fp & lr so we can call 
+  // break_to_debugger()
   sub sp, sp, #32
   stp x29, x30, [sp, #16]
   add x29, sp, #16
@@ -76,17 +77,7 @@ _trap:
 _break_to_debugger:                                  
   .cfi_startproc
 
-  // standard prologue save of fp & lr so we can call puts()
-  sub sp, sp, #32
-  stp x29, x30, [sp, #16]
-  add x29, sp, #16
-  .cfi_def_cfa w29, 16
-  .cfi_offset w30, -8
-  .cfi_offset w29, -16
-
   brk #0xf000   ;; __builtin_debugtrap aarch64 instruction
 
-  ldp x29, x30, [sp, #16]
-  add sp, sp, #32
   ret
   .cfi_endproc

>From 1d90aa48c92d9f8ef43c889223c0804ac1491d53 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Thu, 8 May 2025 18:18:14 -0700
Subject: [PATCH 05/14] Add unwind rules for x0 (volatile) and x20
 (non-voltile) to trap() and break_to_debugger() for fun, and add unwind
 instructions for the epilogue of trap().

When stopped in `break_to_debugger`,
```
  * frame #0: 0x00000001000003e8 a.out`break_to_debugger + 4
    frame #1: 0x00000001000003d8 a.out`trap + 16
    frame #2: 0x00000001000003c0 a.out`to_be_interrupted + 20
    frame #3: 0x0000000100000398 a.out`main + 32
```

Normally we can't provide a volatile register (e.g. x0) up
the stack.  And we can provide a non-volatile register (e.g. x20)
up the stack.  I added an IsSame rule for trap() and break_to_debugger()
for x0, so it CAN be passed up the stack.  And I added an Undefined
rule for x20 to trap() so it CAN'T be provided up the stack.

If a user selects `to_be_interrupted` and does `register read`,
they'll get x0 and they won't get x20.  From `main`, they will not
get x0 or x20 (x0 because `to_be_interrupted` doesn't have an IsSame
rule).
---
 .../interrupt-and-trap-funcs.s                | 25 ++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
index 708ad9ed56a1c..b50c4734f18f3 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
@@ -1,4 +1,6 @@
 #define DW_CFA_register 0x9
+#define ehframe_x0  0
+#define ehframe_x20  20
 #define ehframe_x22  22
 #define ehframe_x23  23
 #define ehframe_pc 32
@@ -7,9 +9,9 @@
 
 //--------------------------------------
 // to_be_interrupted() a frameless function that does a non-ABI
-// function call ("is interrupted/traps" simulated) to trap().
-// Before it branches to trap(), it puts its return address in
-// x23.  trap() knows to branch back to $x23 when it has finished.
+// function call to trap(), simulating an async 
signal/interrup/exception/fault.
+// Before it branches to trap(), put the return address in x23.
+// trap() knows to branch back to $x23 when it has finished.
 //--------------------------------------
   .globl  _to_be_interrupted
   .p2align  2
@@ -51,6 +53,15 @@ _trap:
   // The pc value when we were interrupted is in x23
   .cfi_escape DW_CFA_register, ehframe_pc, ehframe_x23
 
+  // For fun, mark x0 as unmodified so the caller can
+  // retrieve the value if it wants.
+  .cfi_same_value ehframe_x0
+
+  // Mark x20 as undefined.  This is a callee-preserved
+  // (non-volatile) register by the SysV AArch64 ABI, but
+  // it's be fun to see lldb not passing a value past this.
+  .cfi_undefined ehframe_x20
+
   // standard prologue save of fp & lr so we can call 
   // break_to_debugger()
   sub sp, sp, #32
@@ -63,7 +74,11 @@ _trap:
   bl _break_to_debugger
 
   ldp x29, x30, [sp, #16]
+  .cfi_same_value x29
+  .cfi_same_value x30
+  .cfi_def_cfa sp, 32
   add sp, sp, #32
+  .cfi_same_value sp
 
   // jump back to $x23 to resume execution of to_be_interrupted
   br x23
@@ -77,6 +92,10 @@ _trap:
 _break_to_debugger:                                  
   .cfi_startproc
 
+  // For fun, mark x0 as unmodified so the caller can
+  // retrieve the value if it wants.
+  .cfi_same_value ehframe_x0
+
   brk #0xf000   ;; __builtin_debugtrap aarch64 instruction
 
   ret

>From 303edd47f7bcfb6af0a9947ca7b0b9a0c5bec589 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Thu, 8 May 2025 18:46:14 -0700
Subject: [PATCH 06/14] small comment improvement

---
 .../unwind-frameless-faulted/interrupt-and-trap-funcs.s   | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
index b50c4734f18f3..38be06a681bf2 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
@@ -18,10 +18,10 @@
 _to_be_interrupted:
   .cfi_startproc
 
-  // This is a garbage entry to ensure that eh_frame is emitted,
-  // it isn't used for anything.  If there's no eh_frame, lldb
-  // can do an assembly emulation scan and add a rule for $lr
-  // which won't expose the issue at hand.
+  // This is a garbage entry to ensure that eh_frame is emitted.
+  // If there's no eh_frame, lldb can use the assembly emulation scan,
+  // which always includes a rule for $lr, and we won't replicate the
+  // bug we're testing for.
   .cfi_escape DW_CFA_register, ehframe_x22, ehframe_x23
   mov x24, x0
   add x24, x24, #1

>From 620eace34ae149b8c513767935118b6e870646fc Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Thu, 8 May 2025 18:47:54 -0700
Subject: [PATCH 07/14] more comment fix

---
 .../macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
index 38be06a681bf2..b5c5cb79656c3 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
@@ -59,7 +59,8 @@ _trap:
 
   // Mark x20 as undefined.  This is a callee-preserved
   // (non-volatile) register by the SysV AArch64 ABI, but
-  // it's be fun to see lldb not passing a value past this.
+  // it'll be fun to see lldb not passing a value past this
+  // point on the stack.
   .cfi_undefined ehframe_x20
 
   // standard prologue save of fp & lr so we can call 

>From 6185c513beaa2f63dc04b54f723b209de8af2286 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Thu, 8 May 2025 22:36:21 -0700
Subject: [PATCH 08/14] Add test case Makefile and API test python code. Also
 fix one bug in the by-hand unwind state in the assembly file.

---
 .../macosx/unwind-frameless-faulted/Makefile  | 12 ++++
 .../TestUnwindFramelessFaulted.py             | 59 +++++++++++++++++++
 .../interrupt-and-trap-funcs.s                |  1 +
 3 files changed, 72 insertions(+)
 create mode 100644 lldb/test/API/macosx/unwind-frameless-faulted/Makefile
 create mode 100644 
lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py

diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile 
b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile
new file mode 100644
index 0000000000000..76548928a3622
--- /dev/null
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile
@@ -0,0 +1,12 @@
+C_SOURCES := main.c
+
+interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s
+       $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o 
$(SRCDIR)/interrupt-and-trap-funcs.s
+
+include Makefile.rules
+
+a.out: interrupt-and-trap-funcs.o
+
+# Needs to come after include
+OBJECTS += interrupt-and-trap-funcs.o
+
diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py 
b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
new file mode 100644
index 0000000000000..34e7fbfffacde
--- /dev/null
+++ 
b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
@@ -0,0 +1,59 @@
+"""Test that lldb backtraces a frameless function that faults correctly."""
+
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import shutil
+import os
+
+
+class TestUnwindFramelessFaulted(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipUnlessDarwin
+    def test_frameless_faulted_unwind(self):
+        self.build()
+
+        (target, process, thread, bp) = lldbutil.run_to_name_breakpoint(
+            self, "main", only_one_thread=False
+        )
+
+        # The test program will have a backtrace like this at its deepest:
+        #
+        #   * frame #0: 0x0000000102adc468 a.out`break_to_debugger + 4
+        #     frame #1: 0x0000000102adc458 a.out`trap + 16
+        #     frame #2: 0x0000000102adc440 a.out`to_be_interrupted + 20
+        #     frame #3: 0x0000000102adc418 a.out`main at main.c:4:7
+        #     frame #4: 0x0000000193b7eb4c dyld`start + 6000
+
+        correct_frames = ["break_to_debugger", "trap", "to_be_interrupted", 
"main"]
+
+        # Keep track of when main has branch & linked, instruction step until 
we're
+        # back in main()
+        main_has_bl_ed = False
+
+        # Instruction step through the binary until we are in a function not
+        # listed in correct_frames.
+        while (
+            process.GetState() == lldb.eStateStopped
+            and thread.GetFrameAtIndex(0).name in correct_frames
+        ):
+            starting_index = 0
+            if self.TraceOn():
+                self.runCmd("bt")
+
+            # Find which index into correct_frames the current stack frame is
+            for idx, name in enumerate(correct_frames):
+                if thread.GetFrameAtIndex(0).name == name:
+                    starting_index = idx
+
+            # Test that all frames after the current frame listed in
+            # correct_frames appears in the backtrace.
+            frame_idx = 0
+            for expected_frame in correct_frames[starting_index:]:
+                self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, 
expected_frame)
+                frame_idx = frame_idx + 1
+
+            if self.TraceOn():
+                print("StepInstruction")
+            thread.StepInstruction(False)
diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
index b5c5cb79656c3..5bc991fc0f67d 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
@@ -80,6 +80,7 @@ _trap:
   .cfi_def_cfa sp, 32
   add sp, sp, #32
   .cfi_same_value sp
+  .cfi_def_cfa sp, 0
 
   // jump back to $x23 to resume execution of to_be_interrupted
   br x23

>From d239b7a84c1e489b2630aa63358061435a749ce7 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Thu, 8 May 2025 22:49:20 -0700
Subject: [PATCH 09/14] Add tests for x0 and x20, which I treat in normally
 ABI-wrong ways in these hand-written UnwindPlans.

---
 .../TestUnwindFramelessFaulted.py             | 34 ++++++++++++++++---
 1 file changed, 29 insertions(+), 5 deletions(-)

diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py 
b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
index 34e7fbfffacde..d899b7a2f7369 100644
--- 
a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
+++ 
b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
@@ -34,17 +34,15 @@ def test_frameless_faulted_unwind(self):
 
         # Instruction step through the binary until we are in a function not
         # listed in correct_frames.
-        while (
-            process.GetState() == lldb.eStateStopped
-            and thread.GetFrameAtIndex(0).name in correct_frames
-        ):
+        frame = thread.GetFrameAtIndex(0)
+        while process.GetState() == lldb.eStateStopped and frame.name in 
correct_frames:
             starting_index = 0
             if self.TraceOn():
                 self.runCmd("bt")
 
             # Find which index into correct_frames the current stack frame is
             for idx, name in enumerate(correct_frames):
-                if thread.GetFrameAtIndex(0).name == name:
+                if frame.name == name:
                     starting_index = idx
 
             # Test that all frames after the current frame listed in
@@ -54,6 +52,32 @@ def test_frameless_faulted_unwind(self):
                 self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, 
expected_frame)
                 frame_idx = frame_idx + 1
 
+            # When we're at our deepest level, test that register passing of 
x0 and x20
+            # follow the by-hand UnwindPlan rules.  In this test program, we 
can get x0
+            # in the middle of the stack and we CAN'T get x20. The opposites 
of the normal
+            # AArch64 SysV ABI.
+            if frame.name == "break_to_debugger":
+                tbi_frame = thread.GetFrameAtIndex(2)
+                self.assertEqual(tbi_frame.name, "to_be_interrupted")
+
+                # The original argument to to_be_interrupted(), 10
+                # Normally can't get x0 mid-stack, but UnwindPlans have 
special rules to
+                # make this possible.
+                x0_reg = tbi_frame.register["x0"]
+                self.assertTrue(x0_reg.IsValid())
+                self.assertEqual(x0_reg.GetValueAsUnsigned(), 10)
+
+                # The incremented return value from to_be_interrupted(), 11
+                x24_reg = tbi_frame.register["x24"]
+                self.assertTrue(x24_reg.IsValid())
+                self.assertEqual(x24_reg.GetValueAsUnsigned(), 11)
+
+                # x20 can normally be fetched mid-stack, but the UnwindPlan
+                # has a rule saying it can't be fetched.
+                x20_reg = tbi_frame.register["x20"]
+                self.assertTrue(x20_reg.error.fail)
+
             if self.TraceOn():
                 print("StepInstruction")
             thread.StepInstruction(False)
+            frame = thread.GetFrameAtIndex(0)

>From 33563049b37479db9ebebdad0a1c982deb4c6f14 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Thu, 8 May 2025 22:52:04 -0700
Subject: [PATCH 10/14] Remove some Mach-O/Darwinisms, get closer to this
 compiling on linux? I think maybe the only difference is that the symbol
 names are prefixed with _ on darwin?

---
 .../unwind-frameless-faulted/interrupt-and-trap-funcs.s      | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
index 5bc991fc0f67d..2ba6a711767b3 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
@@ -5,7 +5,7 @@
 #define ehframe_x23  23
 #define ehframe_pc 32
 
-  .section  __TEXT,__text,regular,pure_instructions
+  .text
 
 //--------------------------------------
 // to_be_interrupted() a frameless function that does a non-ABI
@@ -14,7 +14,6 @@
 // trap() knows to branch back to $x23 when it has finished.
 //--------------------------------------
   .globl  _to_be_interrupted
-  .p2align  2
 _to_be_interrupted:
   .cfi_startproc
 
@@ -45,7 +44,6 @@ L_.return:
 // break_to_debugger().
 //--------------------------------------
   .globl  _trap
-  .p2align  2
 _trap:                                  
   .cfi_startproc
   .cfi_signal_frame
@@ -90,7 +88,6 @@ _trap:
 // break_to_debugger() executes a BRK instruction
 //--------------------------------------
   .globl _break_to_debugger
-  .p2align  2
 _break_to_debugger:                                  
   .cfi_startproc
 

>From 80248f5a5841645a1da0026799c040d4db7ab6f3 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Fri, 9 May 2025 18:44:27 -0700
Subject: [PATCH 11/14] Adapt test case to build & run on linux or Darwin.

There's a slight syntax diff for the ADRP + ADD pair and the label
name.  And the function names in Darwin are prepended with an _ and
not on linux.

clang on darwin runs the .s file through the preprocessor, but it
doesn't seem to on linux.  I renamed interrupt-and-trap-funcs.s to
interrupt-and-trap-funcs.c and run it through the preprocessor then
assemble it in Makefile now.

The linux version would probably run on other AArch64 systems like
FreeBSD but I haven't checked those.
---
 .../API/macosx/unwind-frameless-faulted/Makefile |  5 +++--
 .../TestUnwindFramelessFaulted.py                |  5 ++++-
 ...d-trap-funcs.s => interrupt-and-trap-funcs.c} | 16 ++++++++++++----
 .../API/macosx/unwind-frameless-faulted/main.c   | 10 ++++++++++
 4 files changed, 29 insertions(+), 7 deletions(-)
 rename 
lldb/test/API/macosx/unwind-frameless-faulted/{interrupt-and-trap-funcs.s => 
interrupt-and-trap-funcs.c} (88%)

diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile 
b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile
index 76548928a3622..fca47ae47491c 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/Makefile
@@ -1,7 +1,8 @@
 C_SOURCES := main.c
 
-interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.s
-       $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o 
$(SRCDIR)/interrupt-and-trap-funcs.s
+interrupt-and-trap-funcs.o: interrupt-and-trap-funcs.c
+       $(CC) $(CFLAGS) -E -o interrupt-and-trap-funcs.s 
$(SRCDIR)/interrupt-and-trap-funcs.c
+       $(CC) $(CFLAGS) -c -o interrupt-and-trap-funcs.o 
interrupt-and-trap-funcs.s
 
 include Makefile.rules
 
diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py 
b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
index d899b7a2f7369..fe4b9b52abebb 100644
--- 
a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
+++ 
b/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
@@ -10,7 +10,10 @@
 class TestUnwindFramelessFaulted(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    @skipUnlessDarwin
+    @skipIf(
+        oslist=no_match([lldbplatformutil.getDarwinOSTriples(), "linux"]),
+        archs=no_match(["aarch64", "arm64", "arm64e"]),
+    )
     def test_frameless_faulted_unwind(self):
         self.build()
 
diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s 
b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c
similarity index 88%
rename from 
lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
rename to 
lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c
index 2ba6a711767b3..23c6cfd688969 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.s
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c
@@ -6,7 +6,6 @@
 #define ehframe_pc 32
 
   .text
-
 //--------------------------------------
 // to_be_interrupted() a frameless function that does a non-ABI
 // function call to trap(), simulating an async 
signal/interrup/exception/fault.
@@ -25,12 +24,21 @@
   mov x24, x0
   add x24, x24, #1
 
-  adrp x23, L_.return@PAGE        ; put return address in x4
+#if defined(__APPLE__)
+  adrp x23, L_.return@PAGE        // put return address in x4
   add x23, x23, L_.return@PAGEOFF
+#else
+  adrp x23, .L.return
+  add  x23, x23, :lo12:.L.return
+#endif
 
-  b _trap                     ; branch to trap handler, fake async interrupt
+  b _trap                     // branch to trap handler, fake async interrupt
 
+#if defined(__APPLE__)
 L_.return:
+#else
+.L.return:
+#endif
   mov x0, x24
   ret
   .cfi_endproc
@@ -95,7 +103,7 @@ L_.return:
   // retrieve the value if it wants.
   .cfi_same_value ehframe_x0
 
-  brk #0xf000   ;; __builtin_debugtrap aarch64 instruction
+  brk #0xf000   // __builtin_debugtrap aarch64 instruction
 
   ret
   .cfi_endproc
diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c 
b/lldb/test/API/macosx/unwind-frameless-faulted/main.c
index e121809f25478..27a2f228b5feb 100644
--- a/lldb/test/API/macosx/unwind-frameless-faulted/main.c
+++ b/lldb/test/API/macosx/unwind-frameless-faulted/main.c
@@ -1,6 +1,16 @@
+#if defined(__APPLE__)
 int to_be_interrupted(int);
+#else
+int _to_be_interrupted(int);
+#endif
+
 int main() {
   int c = 10;
+#if defined(__APPLE__)
   c = to_be_interrupted(c);
+#else
+  c = _to_be_interrupted(c);
+ #endif
+
   return c;
 }

>From 98be98ce9eb68dd89179ed1dc1905db9e5fd9cbf Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Fri, 9 May 2025 18:48:00 -0700
Subject: [PATCH 12/14] Move this test from macosx/ to functionalities/unwind
 because it can run on darwin or linux aarch64.

---
 .../unwind/frameless-faulted}/Makefile                            | 0
 .../unwind/frameless-faulted}/TestUnwindFramelessFaulted.py       | 0
 .../unwind/frameless-faulted}/interrupt-and-trap-funcs.c          | 0
 .../unwind/frameless-faulted}/main.c                              | 0
 4 files changed, 0 insertions(+), 0 deletions(-)
 rename lldb/test/API/{macosx/unwind-frameless-faulted => 
functionalities/unwind/frameless-faulted}/Makefile (100%)
 rename lldb/test/API/{macosx/unwind-frameless-faulted => 
functionalities/unwind/frameless-faulted}/TestUnwindFramelessFaulted.py (100%)
 rename lldb/test/API/{macosx/unwind-frameless-faulted => 
functionalities/unwind/frameless-faulted}/interrupt-and-trap-funcs.c (100%)
 rename lldb/test/API/{macosx/unwind-frameless-faulted => 
functionalities/unwind/frameless-faulted}/main.c (100%)

diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/Makefile 
b/lldb/test/API/functionalities/unwind/frameless-faulted/Makefile
similarity index 100%
rename from lldb/test/API/macosx/unwind-frameless-faulted/Makefile
rename to lldb/test/API/functionalities/unwind/frameless-faulted/Makefile
diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py 
b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py
similarity index 100%
rename from 
lldb/test/API/macosx/unwind-frameless-faulted/TestUnwindFramelessFaulted.py
rename to 
lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py
diff --git 
a/lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c 
b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c
similarity index 100%
rename from 
lldb/test/API/macosx/unwind-frameless-faulted/interrupt-and-trap-funcs.c
rename to 
lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c
diff --git a/lldb/test/API/macosx/unwind-frameless-faulted/main.c 
b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c
similarity index 100%
rename from lldb/test/API/macosx/unwind-frameless-faulted/main.c
rename to lldb/test/API/functionalities/unwind/frameless-faulted/main.c

>From 2e1250cf6bdaed75a3829a8cf465b2fb798505d3 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Fri, 9 May 2025 18:51:15 -0700
Subject: [PATCH 13/14] typeo

---
 lldb/test/API/functionalities/unwind/frameless-faulted/main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c 
b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c
index 27a2f228b5feb..1a016ea61affb 100644
--- a/lldb/test/API/functionalities/unwind/frameless-faulted/main.c
+++ b/lldb/test/API/functionalities/unwind/frameless-faulted/main.c
@@ -10,7 +10,7 @@ int main() {
   c = to_be_interrupted(c);
 #else
   c = _to_be_interrupted(c);
- #endif
+#endif
 
   return c;
 }

>From 2d30011693cf3c3aa1ebdfde57a2c43687b37757 Mon Sep 17 00:00:00 2001
From: Jason Molenda <jmole...@apple.com>
Date: Fri, 9 May 2025 19:05:15 -0700
Subject: [PATCH 14/14] Disabling clang-format of the assembly file called ".c"

Add two more checks for non-ABI compliant register availability
from the custom UnwindPlans.
---
 .../TestUnwindFramelessFaulted.py             | 37 ++++++++++++++-----
 .../interrupt-and-trap-funcs.c                |  7 ++++
 2 files changed, 35 insertions(+), 9 deletions(-)

diff --git 
a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py
 
b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py
index fe4b9b52abebb..dfb42c56164ac 100644
--- 
a/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py
+++ 
b/lldb/test/API/functionalities/unwind/frameless-faulted/TestUnwindFramelessFaulted.py
@@ -55,31 +55,50 @@ def test_frameless_faulted_unwind(self):
                 self.assertEqual(thread.GetFrameAtIndex(frame_idx).name, 
expected_frame)
                 frame_idx = frame_idx + 1
 
-            # When we're at our deepest level, test that register passing of 
x0 and x20
-            # follow the by-hand UnwindPlan rules.  In this test program, we 
can get x0
-            # in the middle of the stack and we CAN'T get x20. The opposites 
of the normal
-            # AArch64 SysV ABI.
+            # When we're at our deepest level, test that register passing of
+            # x0 and x20 follow the by-hand UnwindPlan rules.
+            # In this test program, we can get x0 in the middle of the stack
+            # and we CAN'T get x20. The opposites of the normal AArch64 SysV
+            # ABI.
             if frame.name == "break_to_debugger":
                 tbi_frame = thread.GetFrameAtIndex(2)
                 self.assertEqual(tbi_frame.name, "to_be_interrupted")
-
                 # The original argument to to_be_interrupted(), 10
-                # Normally can't get x0 mid-stack, but UnwindPlans have 
special rules to
-                # make this possible.
+                # Normally can't get x0 mid-stack, but UnwindPlans have
+                # special rules to make this possible.
                 x0_reg = tbi_frame.register["x0"]
                 self.assertTrue(x0_reg.IsValid())
                 self.assertEqual(x0_reg.GetValueAsUnsigned(), 10)
-
                 # The incremented return value from to_be_interrupted(), 11
                 x24_reg = tbi_frame.register["x24"]
                 self.assertTrue(x24_reg.IsValid())
                 self.assertEqual(x24_reg.GetValueAsUnsigned(), 11)
-
                 # x20 can normally be fetched mid-stack, but the UnwindPlan
                 # has a rule saying it can't be fetched.
                 x20_reg = tbi_frame.register["x20"]
                 self.assertTrue(x20_reg.error.fail)
 
+                trap_frame = thread.GetFrameAtIndex(1)
+                self.assertEqual(trap_frame.name, "trap")
+                # Confirm that we can fetch x0 from trap() which
+                # is normally not possible w/ SysV AbI, but special
+                # UnwindPlans in use.
+                x0_reg = trap_frame.register["x0"]
+                self.assertTrue(x0_reg.IsValid())
+                self.assertEqual(x0_reg.GetValueAsUnsigned(), 10)
+                x1_reg = trap_frame.register["x1"]
+                self.assertTrue(x1_reg.error.fail)
+
+                main_frame = thread.GetFrameAtIndex(3)
+                self.assertEqual(main_frame.name, "main")
+                # x20 can normally be fetched mid-stack, but the UnwindPlan
+                # has a rule saying it can't be fetched.
+                x20_reg = main_frame.register["x20"]
+                self.assertTrue(x20_reg.error.fail)
+                # x21 can be fetched mid-stack.
+                x21_reg = main_frame.register["x21"]
+                self.assertTrue(x21_reg.error.success)
+
             if self.TraceOn():
                 print("StepInstruction")
             thread.StepInstruction(False)
diff --git 
a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c
 
b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c
index 23c6cfd688969..d32f01a2ea429 100644
--- 
a/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c
+++ 
b/lldb/test/API/functionalities/unwind/frameless-faulted/interrupt-and-trap-funcs.c
@@ -1,3 +1,10 @@
+// This is assembly code that needs to be run
+// through the preprocessor, for simplicity of
+// preprocessing it's named .c to start with.
+//
+// clang-format off
+
+
 #define DW_CFA_register 0x9
 #define ehframe_x0  0
 #define ehframe_x20  20

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

Reply via email to