DavidSpickett updated this revision to Diff 386108.
DavidSpickett added a comment.

- Set locations for all general purpose registers
- Set register type to DWARF due to that
- Add a new test that sets known register values and compares them between 
signal catch and handler
- Remove changes to handle abort test (though I could do those as a separate 
thing later)

One thing remains, the sigcontext can include floating point and SVE
registers. We'd need to read some memory to determine if it does:
https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/sigcontext.h#L39

Which I can do by passing the target and generating the plan based on
what's present.

For now let me know if the test case makes sense. Maybe this is ok
to go in as is and I can follow up with the other registers?


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D112069/new/

https://reviews.llvm.org/D112069

Files:
  lldb/include/lldb/Target/Platform.h
  lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
  lldb/source/Plugins/Platform/Linux/PlatformLinux.h
  lldb/source/Target/RegisterContextUnwind.cpp
  lldb/test/API/functionalities/signal/handle-abrt/TestHandleAbort.py
  lldb/test/API/linux/aarch64/unwind_signal/Makefile
  lldb/test/API/linux/aarch64/unwind_signal/TestUnwindSignal.py
  lldb/test/API/linux/aarch64/unwind_signal/main.c

Index: lldb/test/API/linux/aarch64/unwind_signal/main.c
===================================================================
--- /dev/null
+++ lldb/test/API/linux/aarch64/unwind_signal/main.c
@@ -0,0 +1,41 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void handler(int sig) {
+  printf("Set a breakpoint here.\n");
+  exit(0);
+}
+
+static void sigill() {
+  // Set all general registers to known values to check
+  // that the signal unwind plan sets their locations correctly.
+#define SETREG(N) "mov x" N ", #" N "\n\t"
+  asm(
+      /* clang-format off */
+      SETREG("0")  SETREG("1")  SETREG("2")  SETREG("3")
+      SETREG("4")  SETREG("5")  SETREG("6")  SETREG("7")
+      SETREG("8")  SETREG("9")  SETREG("10") SETREG("11")
+      SETREG("12") SETREG("13") SETREG("14") SETREG("15")
+      SETREG("16") SETREG("17") SETREG("18") SETREG("19")
+      SETREG("20") SETREG("21") SETREG("22") SETREG("23")
+      SETREG("24") SETREG("25") SETREG("26") SETREG("27")
+      SETREG("28") SETREG("29") SETREG("30") /* 31 is xzr/sp */
+      /* clang-format on */
+      ".inst   0x00000000\n\t" // udf #0 (old binutils don't support do udf)
+      ::
+          : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10",
+            "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19",
+            "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28",
+            "x29", "x30");
+}
+
+int main() {
+  if (signal(SIGILL, handler) == SIG_ERR) {
+    perror("signal");
+    return 1;
+  }
+
+  sigill();
+  return 2;
+}
Index: lldb/test/API/linux/aarch64/unwind_signal/TestUnwindSignal.py
===================================================================
--- /dev/null
+++ lldb/test/API/linux/aarch64/unwind_signal/TestUnwindSignal.py
@@ -0,0 +1,115 @@
+"""Test that we can unwind out of a signal handler.
+   Which for AArch64 Linux requires a specific unwind plan."""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from collections import namedtuple
+
+
+# Since frames internally point to ExecutionContextRef which itself points
+# to something else, we can't make a deep copy of them from Python.
+# Instead save what we care about for comparison purposes.
+# Ignoring the frame ID because what is frame 0 in the first catch will
+# be frame 2 in the handler backtrace.
+class FrameInfo(namedtuple('FrameInfo', ['function_name', 'sp', 'gp_regs'])):
+    # So assert failures are more readable
+    def __repr__(self):
+        reg_lines = []
+        for k, v in self.gp_regs.items():
+            if v is None:
+              value = "{}".format(v)
+            else:
+              value = "0x{:x}".format(v)
+
+            reg_lines.append("{}: {}".format(k, value))
+
+        return "Fn: {} SP: 0x{:x}\n{}".format(self.function_name,
+                                              self.sp,
+                                              "\n".join(reg_lines))
+
+
+def make_frame_info(frame):
+    gp_regs = frame.GetRegisters().GetValueAtIndex(0)
+    reg_values = {}
+    err = lldb.SBError()
+    for i in range(0, 31):
+        name = 'x' + str(i)
+        # This will fail if we have no location information
+        value = gp_regs.GetChildMemberWithName(name).GetValueAsUnsigned(err)
+        reg_values[name] = value if err.Success() else None
+
+    return FrameInfo(frame.GetDisplayFunctionName(), frame.GetSP(), reg_values)
+
+
+class UnwindSignalTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    NO_DEBUG_INFO_TESTCASE = True
+
+    @skipUnlessArch("aarch64")
+    @skipUnlessPlatform(["linux"])
+    def test_unwind_signal(self):
+        """Inferior calls sigill() and handles the resultant SIGILL.
+           Stopped at a breakpoint in the handler, verify that the backtrace
+           includes the function that called sigill() and the common frames
+           in the initial signal catch match those at the breakpoint."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        process = target.LaunchSimple(
+            None, None, self.get_process_working_directory())
+        self.assertTrue(process, PROCESS_IS_VALID)
+        self.assertEqual(process.GetState(), lldb.eStateStopped)
+        signo = process.GetUnixSignals().GetSignalNumberFromName("SIGILL")
+
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
+        self.assertTrue(
+            thread and thread.IsValid(),
+            "Thread should be stopped due to a signal")
+        self.assertTrue(
+            thread.GetStopReasonDataCount() >= 1,
+            "There should be data in the event.")
+        self.assertEqual(thread.GetStopReasonDataAtIndex(0),
+                         signo, "The stop signal should be SIGILL")
+
+        # Save the backtrace frames to compare to the handler backtrace later.
+        signal_frames = [make_frame_info(f) for f in thread.get_thread_frames()]
+
+        # Continue to breakpoint in abort handler
+        bkpt = target.FindBreakpointByID(
+            lldbutil.run_break_set_by_source_regexp(self, "Set a breakpoint here"))
+        threads = lldbutil.continue_to_breakpoint(process, bkpt)
+        self.assertEqual(len(threads), 1, "Expected single thread")
+        thread = threads[0]
+
+        # Expect breakpoint in 'handler'
+        frame = thread.GetFrameAtIndex(0)
+        self.assertEqual(frame.GetDisplayFunctionName(), "handler", "Unexpected break?")
+
+        # Expect that unwinding should find 'sigill'
+        found_caller = False
+        for frame in thread.get_thread_frames():
+            if frame.GetDisplayFunctionName() == "sigill":
+                found_caller = True
+                break
+
+        self.assertTrue(found_caller, "Unwinding did not find func that caused the SIGILL")
+
+        # The signal handler backtrace has extra frames at the start, remove those
+        handler_frames = thread.get_thread_frames()[-len(signal_frames):]
+        handler_frames = [make_frame_info(f) for f in handler_frames]
+
+        # Check that frames present in both backtraces have the same content
+        self.assertEqual(signal_frames, handler_frames, "Common backtrace frames do not match")
+
+        # Continue until we exit.
+        process.Continue()
+        self.assertEqual(process.GetState(), lldb.eStateExited)
+        self.assertEqual(process.GetExitStatus(), 0)
Index: lldb/test/API/linux/aarch64/unwind_signal/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/linux/aarch64/unwind_signal/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
Index: lldb/test/API/functionalities/signal/handle-abrt/TestHandleAbort.py
===================================================================
--- lldb/test/API/functionalities/signal/handle-abrt/TestHandleAbort.py
+++ lldb/test/API/functionalities/signal/handle-abrt/TestHandleAbort.py
@@ -16,8 +16,6 @@
     NO_DEBUG_INFO_TESTCASE = True
 
     @skipIfWindows  # signals do not exist on Windows
-    # Fails on Ubuntu Focal
-    @skipIf(archs=["aarch64"], oslist=["linux"])
     @expectedFailureNetBSD
     def test_inferior_handle_sigabrt(self):
         """Inferior calls abort() and handles the resultant SIGABRT.
Index: lldb/source/Target/RegisterContextUnwind.cpp
===================================================================
--- lldb/source/Target/RegisterContextUnwind.cpp
+++ lldb/source/Target/RegisterContextUnwind.cpp
@@ -893,13 +893,22 @@
     return arch_default_unwind_plan_sp;
   }
 
-  // If we're in _sigtramp(), unwinding past this frame requires special
-  // knowledge.  On Mac OS X this knowledge is properly encoded in the eh_frame
-  // section, so prefer that if available. On other platforms we may need to
-  // provide a platform-specific UnwindPlan which encodes the details of how to
-  // unwind out of sigtramp.
   if (m_frame_type == eTrapHandlerFrame && process) {
     m_fast_unwind_plan_sp.reset();
+
+    // On some platforms the unwind information for signal handlers is not
+    // present or correct. Give the platform plugins a chance to provide
+    // substitute plan. Otherwise, use eh_frame.
+    if (m_sym_ctx_valid) {
+      lldb::PlatformSP platform = process->GetTarget().GetPlatform();
+      unwind_plan_sp = platform->GetTrapHandlerUnwindPlan(
+          process->GetTarget().GetArchitecture().GetTriple(),
+          GetSymbolOrFunctionName(m_sym_ctx));
+
+      if (unwind_plan_sp)
+        return unwind_plan_sp;
+    }
+
     unwind_plan_sp =
         func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
     if (!unwind_plan_sp)
Index: lldb/source/Plugins/Platform/Linux/PlatformLinux.h
===================================================================
--- lldb/source/Plugins/Platform/Linux/PlatformLinux.h
+++ lldb/source/Plugins/Platform/Linux/PlatformLinux.h
@@ -50,6 +50,9 @@
 
   void CalculateTrapHandlerSymbolNames() override;
 
+  lldb::UnwindPlanSP GetTrapHandlerUnwindPlan(const llvm::Triple &triple,
+                                              ConstString name) override;
+
   MmapArgList GetMmapArgumentList(const ArchSpec &arch, lldb::addr_t addr,
                                   lldb::addr_t length, unsigned prot,
                                   unsigned flags, lldb::addr_t fd,
Index: lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
===================================================================
--- lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
+++ lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp
@@ -14,9 +14,11 @@
 #include <sys/utsname.h>
 #endif
 
+#include "Utility/ARM64_DWARF_Registers.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/UnwindPlan.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/FileSpec.h"
@@ -251,6 +253,93 @@
   m_trap_handlers.push_back(ConstString("__restore_rt"));
 }
 
+static lldb::UnwindPlanSP GetAArch64TrapHanlderUnwindPlan(ConstString name) {
+  UnwindPlanSP unwind_plan_sp;
+  if (name != "__kernel_rt_sigreturn")
+    return unwind_plan_sp;
+
+  UnwindPlan::RowSP row = std::make_shared<UnwindPlan::Row>();
+  row->SetOffset(0);
+
+  // In the signal trampoline frame, sp points to an rt_sigframe[1], which is:
+  //  - 128-byte siginfo struct
+  //  - ucontext struct:
+  //     - 8-byte long (uc_flags)
+  //     - 8-byte pointer (uc_link)
+  //     - 24-byte stack_t
+  //     - 128-byte signal set
+  //     - 8 bytes of padding because sigcontext has 16-byte alignment
+  //     - sigcontext/mcontext_t
+  // [1]
+  // https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/signal.c
+  int32_t offset = 128 + 8 + 8 + 24 + 128 + 8;
+  // Then sigcontext[2] is:
+  // - 8 byte fault address
+  // - 31 8 byte registers
+  // - 8 byte sp
+  // - 8 byte pc
+  // [2]
+  // https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/sigcontext.h
+
+  // Skip fault address
+  offset += 8;
+  row->GetCFAValue().SetIsRegisterPlusOffset(arm64_dwarf::sp, offset);
+
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x0, 0 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x1, 1 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x2, 2 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x3, 3 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x4, 4 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x5, 5 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x6, 6 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x7, 7 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x8, 8 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x9, 9 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x10, 10 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x11, 11 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x12, 12 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x13, 13 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x14, 14 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x15, 15 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x16, 16 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x17, 17 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x18, 18 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x19, 19 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x20, 20 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x21, 21 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x22, 22 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x23, 23 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x24, 24 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x25, 25 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x26, 26 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x27, 27 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x28, 28 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::fp, 29 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::x30, 30 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::sp, 31 * 8, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::pc, 32 * 8, false);
+
+  unwind_plan_sp = std::make_shared<UnwindPlan>(eRegisterKindGeneric);
+  unwind_plan_sp->AppendRow(row);
+  unwind_plan_sp->SetSourceName("AArch64 Linux sigcontext");
+  unwind_plan_sp->SetSourcedFromCompiler(eLazyBoolYes);
+  // Because sp is the same throughout the function
+  unwind_plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+  unwind_plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolYes);
+  unwind_plan_sp->SetRegisterKind(eRegisterKindDWARF);
+
+  return unwind_plan_sp;
+}
+
+lldb::UnwindPlanSP
+PlatformLinux::GetTrapHandlerUnwindPlan(const llvm::Triple &triple,
+                                        ConstString name) {
+  if (triple.isAArch64())
+    return GetAArch64TrapHanlderUnwindPlan(name);
+
+  return {};
+}
+
 MmapArgList PlatformLinux::GetMmapArgumentList(const ArchSpec &arch,
                                                addr_t addr, addr_t length,
                                                unsigned prot, unsigned flags,
Index: lldb/include/lldb/Target/Platform.h
===================================================================
--- lldb/include/lldb/Target/Platform.h
+++ lldb/include/lldb/Target/Platform.h
@@ -719,6 +719,24 @@
   ///     A list of symbol names.  The list may be empty.
   virtual const std::vector<ConstString> &GetTrapHandlerSymbolNames();
 
+  /// Try to get a specific unwind plan for a named trap handler.
+  /// The default is not to have specific unwind plans for trap handlers.
+  ///
+  /// \param[in] triple
+  ///     Triple of the current target.
+  ///
+  /// \param[in] name
+  ///     Name of the trap handler function.
+  ///
+  /// \return
+  ///     A specific unwind plan for that trap handler, or an empty
+  ///     shared pointer. The latter means there is no specific plan,
+  ///     unwind as normal.
+  virtual lldb::UnwindPlanSP
+  GetTrapHandlerUnwindPlan(const llvm::Triple &triple, ConstString name) {
+    return {};
+  }
+
   /// Find a support executable that may not live within in the standard
   /// locations related to LLDB.
   ///
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to