DavidSpickett updated this revision to Diff 386466.
DavidSpickett added a comment.
- Set x registers to a different pattern in the signal handler
- Don't save frames before and after, just check for the right pattern when
unwinding out of the handler to sigill()
- Set register kind when we first make the unwind plan
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,64 @@
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void handler(int sig) {
+ // The kernel only changes a few registers so set them all to something other
+ // than the values in sigill() so that we can't fall back to real registers
+ // and still pass the test.
+#define SETREG(N) "mov x" N ", #" N "+1\n\t"
+ asm volatile(
+ /* clang-format off */
+ /* x0 is used for a parameter */
+ 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") // fp/x29 needed for unwiding
+ SETREG("30") // 31 is xzr/sp
+ /* clang-format on */
+ ::
+ : /* skipped 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",
+ /* skipped fp/x29 */ "x30");
+ 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 volatile(
+ /* 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 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
===================================================================
--- lldb/test/API/linux/aarch64/unwind_signal/TestUnwindSignal.py
+++ lldb/test/API/linux/aarch64/unwind_signal/TestUnwindSignal.py
@@ -1,6 +1,5 @@
-"""Test that we can unwind out of a SIGABRT handler"""
-
-
+"""Test that we can unwind out of a signal handler.
+ Which for AArch64 Linux requires a specific unwind plan."""
import lldb
@@ -9,33 +8,29 @@
from lldbsuite.test import lldbutil
-class HandleAbortTestCase(TestBase):
+class UnwindSignalTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
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.
- Stopped at a breakpoint in the handler, verify that the backtrace
- includes the function that called abort()."""
+ @skipUnlessArch("aarch64")
+ @skipUnlessPlatform(["linux"])
+ def test_unwind_signal(self):
+ """Inferior calls sigill() and handles the resultant SIGILL.
+ Stopped at a breakpoint in the handler, check that we can unwind
+ back to sigill() and get the expected register contents there."""
self.build()
exe = self.getBuildArtifact("a.out")
- # Create a target by the debugger.
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
- # launch
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("SIGABRT")
+ signo = process.GetUnixSignals().GetSignalNumberFromName("SIGILL")
thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal)
self.assertTrue(
@@ -45,9 +40,9 @@
thread.GetStopReasonDataCount() >= 1,
"There should be data in the event.")
self.assertEqual(thread.GetStopReasonDataAtIndex(0),
- signo, "The stop signal should be SIGABRT")
+ signo, "The stop signal should be SIGILL")
- # Continue to breakpoint in abort handler
+ # Continue to breakpoint in sigill handler
bkpt = target.FindBreakpointByID(
lldbutil.run_break_set_by_source_regexp(self, "Set a breakpoint here"))
threads = lldbutil.continue_to_breakpoint(process, bkpt)
@@ -58,13 +53,26 @@
frame = thread.GetFrameAtIndex(0)
self.assertEqual(frame.GetDisplayFunctionName(), "handler", "Unexpected break?")
- # Expect that unwinding should find 'abort_caller'
- foundFoo = False
- for frame in thread:
- if frame.GetDisplayFunctionName() == "abort_caller":
- foundFoo = True
-
- self.assertTrue(foundFoo, "Unwinding did not find func that called abort")
+ # Expect that unwinding should find 'sigill'
+ found_caller = False
+ for frame in thread.get_thread_frames():
+ if frame.GetDisplayFunctionName() == "sigill":
+ # We should have ascending values in the x registers
+ regs = frame.GetRegisters().GetValueAtIndex(0)
+ err = lldb.SBError()
+
+ for i in range(31):
+ name = 'x{}'.format(i)
+ value = regs.GetChildMemberWithName(name).GetValueAsUnsigned(err)
+ self.assertTrue(err.Success(), "Failed to get register {}: {}".format(
+ name, err))
+ self.assertEqual(value, i, "Unexpected value for register {}".format(
+ name))
+
+ found_caller = True
+ break
+
+ self.assertTrue(found_caller, "Unwinding did not find func that caused the SIGILL")
# Continue until we exit.
process.Continue()
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,96 @@
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);
+
+ // The sigcontext may also contain floating point and SVE registers.
+ // However this would require a dynamic unwind plan so they are not included
+ // here.
+
+ unwind_plan_sp = std::make_shared<UnwindPlan>(eRegisterKindDWARF);
+ 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);
+
+ 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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits