diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h --- a/lldb/include/lldb/Target/StopInfo.h +++ b/lldb/include/lldb/Target/StopInfo.h @@ -115,7 +115,8 @@ static lldb::StopInfoSP CreateStopReasonWithSignal(Thread &thread, int signo, - const char *description = nullptr); + const char *description = nullptr, + std::optional code = std::nullopt); static lldb::StopInfoSP CreateStopReasonToTrace(Thread &thread); diff --git a/lldb/include/lldb/Target/UnixSignals.h b/lldb/include/lldb/Target/UnixSignals.h --- a/lldb/include/lldb/Target/UnixSignals.h +++ b/lldb/include/lldb/Target/UnixSignals.h @@ -32,6 +32,9 @@ const char *GetSignalAsCString(int32_t signo) const; + std::string GetSignalAsString(int32_t signo, + std::optional code) const; + bool SignalIsValid(int32_t signo) const; int32_t GetSignalNumberFromName(const char *name) const; @@ -82,6 +85,8 @@ bool default_stop, bool default_notify, const char *description, const char *alias = nullptr); + void AddSignalCode(int signo, int code, const char *description); + void RemoveSignal(int signo); /// Track how many times signals are hit as stop reasons. @@ -115,6 +120,7 @@ ConstString m_name; ConstString m_alias; std::string m_description; + std::map m_codes; uint32_t m_hit_count = 0; bool m_suppress : 1, m_stop : 1, m_notify : 1; bool m_default_suppress : 1, m_default_stop : 1, m_default_notify : 1; diff --git a/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp b/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp --- a/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp +++ b/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp @@ -28,6 +28,8 @@ AddSignal(9, "SIGKILL", false, true, true, "kill"); AddSignal(10, "SIGUSR1", false, true, true, "user defined signal 1"); AddSignal(11, "SIGSEGV", false, true, true, "segmentation violation"); + AddSignalCode(11, 8 /*SEGV_MTEAERR*/, "async tag check fault"); + AddSignalCode(11, 9 /*SEGV_MTESERR*/, "sync tag check fault"); AddSignal(12, "SIGUSR2", false, true, true, "user defined signal 2"); AddSignal(13, "SIGPIPE", false, true, true, "write to pipe with reading end closed"); AddSignal(14, "SIGALRM", false, false, false, "alarm"); diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -920,6 +920,7 @@ if (status.Fail()) return status.ToError(); thread_data.signo = siginfo.si_signo; + thread_data.code = siginfo.si_code; break; } case ELF::NT_FILE: { diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h --- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h +++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h @@ -128,6 +128,7 @@ std::vector notes; lldb::tid_t tid; int signo = 0; + int code = 0; int prstatus_sig = 0; std::string name; }; @@ -166,6 +167,7 @@ lldb::RegisterContextSP m_thread_reg_ctx_sp; int m_signo; + int m_code; lldb_private::DataExtractor m_gpregset_data; std::vector m_notes; diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp --- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp @@ -46,7 +46,8 @@ // Construct a Thread object with given data ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td) : Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(), - m_signo(td.signo), m_gpregset_data(td.gpregset), m_notes(td.notes) {} + m_signo(td.signo), m_code(td.code), m_gpregset_data(td.gpregset), + m_notes(td.notes) {} ThreadElfCore::~ThreadElfCore() { DestroyThread(); } @@ -221,11 +222,12 @@ bool ThreadElfCore::CalculateStopInfo() { ProcessSP process_sp(GetProcess()); - if (process_sp) { - SetStopInfo(StopInfo::CreateStopReasonWithSignal(*this, m_signo)); - return true; - } - return false; + if (!process_sp) + return false; + + SetStopInfo(StopInfo::CreateStopReasonWithSignal( + *this, m_signo, /*description=*/nullptr, m_code)); + return true; } // Parse PRSTATUS from NOTE entry @@ -409,8 +411,8 @@ // properly, because the struct is for the 64 bit version offset_t offset = 0; si_signo = data.GetU32(&offset); - si_code = data.GetU32(&offset); si_errno = data.GetU32(&offset); + si_code = data.GetU32(&offset); return error; } diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp --- a/lldb/source/Target/StopInfo.cpp +++ b/lldb/source/Target/StopInfo.cpp @@ -1039,8 +1039,9 @@ class StopInfoUnixSignal : public StopInfo { public: - StopInfoUnixSignal(Thread &thread, int signo, const char *description) - : StopInfo(thread, signo) { + StopInfoUnixSignal(Thread &thread, int signo, const char *description, + std::optional code) + : StopInfo(thread, signo), m_code(code) { SetDescription(description); } @@ -1095,19 +1096,26 @@ if (m_description.empty()) { ThreadSP thread_sp(m_thread_wp.lock()); if (thread_sp) { + UnixSignalsSP unix_signals = thread_sp->GetProcess()->GetUnixSignals(); StreamString strm; - const char *signal_name = - thread_sp->GetProcess()->GetUnixSignals()->GetSignalAsCString( - m_value); - if (signal_name) - strm.Printf("signal %s", signal_name); + strm << "signal "; + + std::string signal_name = + unix_signals->GetSignalAsString(m_value, m_code); + if (signal_name.size()) + strm << signal_name; else - strm.Printf("signal %" PRIi64, m_value); + strm.Printf("%" PRIi64, m_value); + m_description = std::string(strm.GetString()); } } return m_description.c_str(); } + +private: + // In siginfo_t terms, if m_value is si_signo, m_code is si_code. + std::optional m_code; }; // StopInfoTrace @@ -1366,9 +1374,10 @@ } StopInfoSP StopInfo::CreateStopReasonWithSignal(Thread &thread, int signo, - const char *description) { + const char *description, + std::optional code) { thread.GetProcess()->GetUnixSignals()->IncrementSignalHitCount(signo); - return StopInfoSP(new StopInfoUnixSignal(thread, signo, description)); + return StopInfoSP(new StopInfoUnixSignal(thread, signo, description, code)); } StopInfoSP StopInfo::CreateStopReasonToTrace(Thread &thread) { diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp --- a/lldb/source/Target/UnixSignals.cpp +++ b/lldb/source/Target/UnixSignals.cpp @@ -122,6 +122,14 @@ ++m_version; } +void UnixSignals::AddSignalCode(int signo, int code, const char *description) { + collection::iterator signal = m_signals.find(signo); + if (signal == m_signals.end()) + return; + signal->second.m_codes.insert(std::pair{code, description}); + ++m_version; +} + void UnixSignals::RemoveSignal(int signo) { collection::iterator pos = m_signals.find(signo); if (pos != m_signals.end()) @@ -137,6 +145,27 @@ return pos->second.m_name.GetCString(); } +std::string UnixSignals::GetSignalAsString(int32_t signo, + std::optional code) const { + std::string str; + + collection::const_iterator pos = m_signals.find(signo); + if (pos != m_signals.end()) { + str = pos->second.m_name.GetCString(); + + if (code) { + std::map::const_iterator cpos = + pos->second.m_codes.find(*code); + if (cpos != pos->second.m_codes.end()) { + str += ": "; + str += cpos->second.GetCString(); + } + } + } + + return str; +} + bool UnixSignals::SignalIsValid(int32_t signo) const { return m_signals.find(signo) != m_signals.end(); } diff --git a/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py --- a/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py +++ b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py @@ -166,3 +166,14 @@ # the MTE core file which does support it but does not allow writing tags. self.expect("memory tag write 0 1", substrs=["error: Process does not support memory tagging"], error=True) + + @skipIfLLVMTargetMissing("AArch64") + def test_mte_tag_fault_reason(self): + """ Test that we correctly report the fault reason. """ + self.runCmd("target create --core core.mte") + + # There is no fault address shown here because core files do not include + # si_addr. + self.expect("bt", substrs=[ + "* thread #1, name = 'a.out.mte', stop reason = signal SIGSEGV: " + "sync tag check fault"]) diff --git a/lldb/unittests/Signals/UnixSignalsTest.cpp b/lldb/unittests/Signals/UnixSignalsTest.cpp --- a/lldb/unittests/Signals/UnixSignalsTest.cpp +++ b/lldb/unittests/Signals/UnixSignalsTest.cpp @@ -23,6 +23,7 @@ AddSignal(4, "SIG4", true, false, true, "DESC4"); AddSignal(8, "SIG8", true, true, true, "DESC8"); AddSignal(16, "SIG16", true, false, false, "DESC16"); + AddSignalCode(16, 1, "a specific type of SIG16"); } }; @@ -93,6 +94,25 @@ EXPECT_EQ(name, signals.GetSignalAsCString(signo)); } +TEST(UnixSignalsTest, GetAsCString) { + TestSignals signals; + + EXPECT_EQ(nullptr, signals.GetSignalAsCString(100)); + std::string name = signals.GetSignalAsCString(16); + EXPECT_EQ("SIG16", name); +} + +TEST(UnixSignalsTest, GetAsString) { + TestSignals signals; + + EXPECT_EQ("", signals.GetSignalAsString(100, std::nullopt)); + EXPECT_EQ("SIG16", signals.GetSignalAsString(16, std::nullopt)); + EXPECT_EQ("", signals.GetSignalAsString(100, 100)); + EXPECT_EQ("SIG16", signals.GetSignalAsString(16, 100)); + EXPECT_EQ("SIG16: a specific type of SIG16", + signals.GetSignalAsString(16, 1)); +} + TEST(UnixSignalsTest, VersionChange) { TestSignals signals; diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -188,6 +188,9 @@ omit defaulted template parameters. The full template parameter list can still be viewed with `expr --raw-output`/`frame var --raw-output`. (`D141828 `_) +* When debugging a Linux core file containing a segfault related to memory tagging, + LLDB now reports the specific type of that fault. For example ``SIGSEGV: sync tag check fault``. + Changes to Sanitizers ---------------------