diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/Makefile b/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/Makefile new file mode 100644 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/TestHandleAbort.py b/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/TestHandleAbort.py new file mode 100644 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/TestHandleAbort.py @@ -0,0 +1,72 @@ +"""Test that we can unwind out of a SIGABRT handler""" + +from __future__ import print_function + + +import os +import re + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class HandleAbortTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + NO_DEBUG_INFO_TESTCASE = True + + @skipIfWindows # signals do not exist on Windows + def test_inferior_handle_sigabrt(self): + """Inferierior calls abort() and handles the resultant sigabrt(). + Stopped at a breakpoint in the handler, verify that the backtrace + includes the function that called abort().""" + 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") + + 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 SIGABRT") + + # 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 '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") + + # Continue until we exit. + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/main.c b/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/main.c new file mode 100644 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/test/functionalities/signal/handle-abrt/main.c @@ -0,0 +1,25 @@ +#include +#include +#include + +void handler(int sig) +{ + printf("Set a breakpoint here.\n"); + exit(0); +} + +void abort_caller() { + abort(); +} + +int main() +{ + if (signal(SIGABRT, handler) == SIG_ERR) + { + perror("signal"); + return 1; + } + + abort_caller(); + return 2; +} diff --git a/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp b/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp --- a/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp +++ b/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp @@ -396,6 +396,7 @@ void PlatformLinux::CalculateTrapHandlerSymbolNames() { m_trap_handlers.push_back(ConstString("_sigtramp")); + m_trap_handlers.push_back(ConstString("__kernel_rt_sigreturn")); } MmapArgList PlatformLinux::GetMmapArgumentList(const ArchSpec &arch, diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp --- a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -472,20 +472,30 @@ m_sym_ctx_valid = false; } - bool decr_pc_and_recompute_addr_range = false; + bool decr_pc_and_recompute_addr_range; - // If the symbol lookup failed... - if (!m_sym_ctx_valid) + if (!m_sym_ctx_valid) { + // Always decrement and recompute if the symbol lookup failed decr_pc_and_recompute_addr_range = true; - - // Or if we're in the middle of the stack (and not "above" an asynchronous - // event like sigtramp), and our "current" pc is the start of a function... - if (GetNextFrame()->m_frame_type != eTrapHandlerFrame && - GetNextFrame()->m_frame_type != eDebuggerFrame && - (!m_sym_ctx_valid || - (addr_range.GetBaseAddress().IsValid() && - addr_range.GetBaseAddress().GetSection() == m_current_pc.GetSection() && - addr_range.GetBaseAddress().GetOffset() == m_current_pc.GetOffset()))) { + } else if (GetNextFrame()->m_frame_type == eTrapHandlerFrame || + GetNextFrame()->m_frame_type == eDebuggerFrame) { + // Don't decrement if we're "above" an asynchronous event like + // sigtramp. + decr_pc_and_recompute_addr_range = false; + } else if (!addr_range.GetBaseAddress().IsValid() || + addr_range.GetBaseAddress().GetSection() != m_current_pc.GetSection() || + addr_range.GetBaseAddress().GetOffset() != m_current_pc.GetOffset()) { + // If our "current" pc isn't the start of a function, no need + // to decrement and recompute. + decr_pc_and_recompute_addr_range = false; + } else if (IsTrapHandlerSymbol(process, m_sym_ctx)) { + // Signal dispatch may set the return address of the handler it calls to + // point to the first byte of a return trampoline (like __kernel_rt_sigreturn), + // so do not decrement and recompute if the symbol we already found is a trap + // handler. + decr_pc_and_recompute_addr_range = false; + } else { + // Decrement to find the function containing the call. decr_pc_and_recompute_addr_range = true; }