DavidSpickett created this revision.
Herald added subscribers: kristof.beyls, mgorny.
DavidSpickett requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This adds a specific unwind plan for AArch64 Linux sigreturn frames.
Previously we assumed that the fp would be valid here but it is not.

https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S

On Ubuntu Bionic it happened to point to an old frame info which meant
you got what looked like a correct backtrace. On Focal, the info is
completely invalid. (probably due to some code shuffling in libc)

This adds an UnwindPlan that knows that the sp in a sigreturn frame
points to an rt_sigframe from which we can offset to get saved
sp and pc values to backtrace correctly.

Based on LibUnwind's change: https://reviews.llvm.org/D90898

This also updates TestHandleAbort to check that common frames
between signal and signal handler backtrace are in fact the same.

Fixes https://bugs.llvm.org/show_bug.cgi?id=52165


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D112069

Files:
  lldb/include/lldb/Symbol/AArch64UnwindInfo.h
  lldb/source/Symbol/AArch64UnwindInfo.cpp
  lldb/source/Symbol/CMakeLists.txt
  lldb/source/Target/RegisterContextUnwind.cpp
  lldb/test/API/functionalities/signal/handle-abrt/TestHandleAbort.py

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.
@@ -47,6 +45,9 @@
         self.assertEqual(thread.GetStopReasonDataAtIndex(0),
                          signo, "The stop signal should be SIGABRT")
 
+        # Save the backtrace frames to compare to the handler backtrace later.
+        signal_frames = 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"))
@@ -58,13 +59,21 @@
         frame = thread.GetFrameAtIndex(0)
         self.assertEqual(frame.GetDisplayFunctionName(), "handler", "Unexpected break?")
 
+        handler_frames = thread.get_thread_frames()
+
         # Expect that unwinding should find 'abort_caller'
-        foundFoo = False
-        for frame in thread:
+        found_caller = False
+        for frame in handler_frames:
             if frame.GetDisplayFunctionName() == "abort_caller":
-                foundFoo = True
+                found_caller = True
+                break
+
+        self.assertTrue(found_caller, "Unwinding did not find func that called abort")
 
-        self.assertTrue(foundFoo, "Unwinding did not find func that called abort")
+        # Check that frames present in both backtraces have the same addresses.
+        # The signal handler backtrace has extra frames at the start.
+        self.assertEqual(signal_frames, handler_frames[-len(signal_frames):],
+          "Common frames for signal and signal handler backtrace do not match")
 
         # Continue until we exit.
         process.Continue()
Index: lldb/source/Target/RegisterContextUnwind.cpp
===================================================================
--- lldb/source/Target/RegisterContextUnwind.cpp
+++ lldb/source/Target/RegisterContextUnwind.cpp
@@ -12,6 +12,7 @@
 #include "lldb/Core/Module.h"
 #include "lldb/Core/Value.h"
 #include "lldb/Expression/DWARFExpression.h"
+#include "lldb/Symbol/AArch64UnwindInfo.h"
 #include "lldb/Symbol/ArmUnwindInfo.h"
 #include "lldb/Symbol/CallFrameInfo.h"
 #include "lldb/Symbol/DWARFCallFrameInfo.h"
@@ -900,6 +901,11 @@
   // unwind out of sigtramp.
   if (m_frame_type == eTrapHandlerFrame && process) {
     m_fast_unwind_plan_sp.reset();
+
+    unwind_plan_sp = GetAArch64LinuxTrapHandlerUnwindPlan(process->GetTarget());
+    if (unwind_plan_sp)
+      return unwind_plan_sp;
+
     unwind_plan_sp =
         func_unwinders_sp->GetEHFrameUnwindPlan(process->GetTarget());
     if (!unwind_plan_sp)
Index: lldb/source/Symbol/CMakeLists.txt
===================================================================
--- lldb/source/Symbol/CMakeLists.txt
+++ lldb/source/Symbol/CMakeLists.txt
@@ -7,6 +7,7 @@
 endif()
 
 add_lldb_library(lldbSymbol
+  AArch64UnwindInfo.cpp
   ArmUnwindInfo.cpp
   Block.cpp
   CompactUnwindInfo.cpp
Index: lldb/source/Symbol/AArch64UnwindInfo.cpp
===================================================================
--- /dev/null
+++ lldb/source/Symbol/AArch64UnwindInfo.cpp
@@ -0,0 +1,63 @@
+//===-- AArch64UnwindInfo.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Symbol/AArch64UnwindInfo.h"
+#include "Utility/ARM64_DWARF_Registers.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+lldb::UnwindPlanSP
+lldb_private::GetAArch64LinuxTrapHandlerUnwindPlan(const Target &target) {
+  lldb::UnwindPlanSP unwind_plan_sp;
+  ArchSpec arch = target.GetArchitecture();
+  llvm::Triple::ArchType machine = arch.GetMachine();
+
+  if ((machine != llvm::Triple::ArchType::aarch64 &&
+       machine != llvm::Triple::ArchType::aarch64_be &&
+       machine != llvm::Triple::ArchType::aarch64_32) ||
+      arch.GetTriple().getOS() != llvm::Triple::OSType::Linux)
+    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
+  offset += 8 + (31 * 8);
+  row->GetCFAValue().SetIsRegisterPlusOffset(arm64_dwarf::sp, offset);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::fp, 0, false);
+  row->SetRegisterLocationToAtCFAPlusOffset(arm64_dwarf::pc, 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);
+  unwind_plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
+  unwind_plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolYes);
+  unwind_plan_sp->SetRegisterKind(eRegisterKindDWARF);
+
+  return unwind_plan_sp;
+}
Index: lldb/include/lldb/Symbol/AArch64UnwindInfo.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Symbol/AArch64UnwindInfo.h
@@ -0,0 +1,26 @@
+//===-- AArch64UnwindInfo.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SYMBOL_AARCH64UNWINDINFO_H
+#define LLDB_SYMBOL_AARCH64UNWINDINFO_H
+
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Target.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+// If target is AArch64 Linux return a shared pointer to
+// an UnwindPlan. Otherwise return an invalid shared pointer.
+// We assume the caller has checked that they are in a trap handler frame.
+lldb::UnwindPlanSP GetAArch64LinuxTrapHandlerUnwindPlan(const Target &target);
+
+} // namespace lldb_private
+
+#endif // LLDB_SYMBOL_AARCH64UNWINDINFO_H
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to