[Lldb-commits] [lldb] 0a72429 - [LLDB][ProcessELFCore] Add Description to ProcessELFCore/ELFThread stop reasons (#110065)

via lldb-commits lldb-commits at lists.llvm.org
Thu Nov 21 14:47:11 PST 2024


Author: Jacob Lalonde
Date: 2024-11-21T14:47:08-08:00
New Revision: 0a7242959f5d3f9ccf7b149009b9eebd45b785b0

URL: https://github.com/llvm/llvm-project/commit/0a7242959f5d3f9ccf7b149009b9eebd45b785b0
DIFF: https://github.com/llvm/llvm-project/commit/0a7242959f5d3f9ccf7b149009b9eebd45b785b0.diff

LOG: [LLDB][ProcessELFCore] Add Description to ProcessELFCore/ELFThread stop reasons (#110065)

This fixes a functionality gap with GDB, where GDB will properly decode
the stop reason and give the address for SIGSEGV. I also added
descriptions to all stop reasons, following the same code path that the
Native Linux Thread uses.

Added: 
    

Modified: 
    lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
    lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
    lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
    lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
    lldb/test/API/linux/aarch64/non_address_bit_memory_access/TestAArch64LinuxNonAddressBitMemoryAccess.py
    lldb/test/Shell/Register/Core/x86-32-linux-multithread.test
    lldb/test/Shell/Register/Core/x86-64-linux-multithread.test

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
index cd95b000469dc5..a5ee3cfdb2932a 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -232,7 +232,7 @@ Status ProcessElfCore::DoLoadCore() {
   bool prstatus_signal_found = false;
   // Check we found a signal in a SIGINFO note.
   for (const auto &thread_data : m_thread_data) {
-    if (thread_data.signo != 0)
+    if (thread_data.siginfo.si_signo != 0)
       siginfo_signal_found = true;
     if (thread_data.prstatus_sig != 0)
       prstatus_signal_found = true;
@@ -242,10 +242,10 @@ Status ProcessElfCore::DoLoadCore() {
     // PRSTATUS note.
     if (prstatus_signal_found) {
       for (auto &thread_data : m_thread_data)
-        thread_data.signo = thread_data.prstatus_sig;
+        thread_data.siginfo.si_signo = thread_data.prstatus_sig;
     } else if (m_thread_data.size() > 0) {
       // If all else fails force the first thread to be SIGSTOP
-      m_thread_data.begin()->signo =
+      m_thread_data.begin()->siginfo.si_signo =
           GetUnixSignals()->GetSignalNumberFromName("SIGSTOP");
     }
   }
@@ -498,7 +498,7 @@ static void ParseFreeBSDPrStatus(ThreadData &thread_data,
   else
     offset += 16;
 
-  thread_data.signo = data.GetU32(&offset); // pr_cursig
+  thread_data.siginfo.si_signo = data.GetU32(&offset); // pr_cursig
   thread_data.tid = data.GetU32(&offset);   // pr_pid
   if (lp64)
     offset += 4;
@@ -581,7 +581,7 @@ static void ParseOpenBSDProcInfo(ThreadData &thread_data,
     return;
 
   offset += 4;
-  thread_data.signo = data.GetU32(&offset);
+  thread_data.siginfo.si_signo = data.GetU32(&offset);
 }
 
 llvm::Expected<std::vector<CoreNote>>
@@ -819,7 +819,7 @@ llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) {
   // Signal targeted at the whole process.
   if (siglwp == 0) {
     for (auto &data : m_thread_data)
-      data.signo = signo;
+      data.siginfo.si_signo = signo;
   }
   // Signal destined for a particular LWP.
   else {
@@ -827,7 +827,7 @@ llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) {
 
     for (auto &data : m_thread_data) {
       if (data.tid == siglwp) {
-        data.signo = signo;
+        data.siginfo.si_signo = signo;
         passed = true;
         break;
       }
@@ -930,12 +930,12 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
       break;
     }
     case ELF::NT_SIGINFO: {
+      const lldb_private::UnixSignals &unix_signals = *GetUnixSignals();
       ELFLinuxSigInfo siginfo;
-      Status status = siginfo.Parse(note.data, arch);
+      Status status = siginfo.Parse(note.data, arch, unix_signals);
       if (status.Fail())
         return status.ToError();
-      thread_data.signo = siginfo.si_signo;
-      thread_data.code = siginfo.si_code;
+      thread_data.siginfo = siginfo;
       break;
     }
     case ELF::NT_FILE: {

diff  --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
index f2838087298efb..c810eb9ed61d26 100644
--- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
@@ -9,6 +9,7 @@
 #include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/StopInfo.h"
 #include "lldb/Target/Target.h"
+#include "lldb/Target/UnixSignals.h"
 #include "lldb/Target/Unwind.h"
 #include "lldb/Utility/DataExtractor.h"
 #include "lldb/Utility/LLDBLog.h"
@@ -49,9 +50,9 @@ using namespace lldb_private;
 
 // 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_code(td.code), m_gpregset_data(td.gpregset),
-      m_notes(td.notes) {}
+    : Thread(process, td.tid), m_thread_reg_ctx_sp(), m_thread_name(td.name),
+      m_gpregset_data(td.gpregset), m_notes(td.notes),
+      m_siginfo(std::move(td.siginfo)) {}
 
 ThreadElfCore::~ThreadElfCore() { DestroyThread(); }
 
@@ -246,8 +247,21 @@ bool ThreadElfCore::CalculateStopInfo() {
   if (!process_sp)
     return false;
 
+  lldb::UnixSignalsSP unix_signals_sp(process_sp->GetUnixSignals());
+  if (!unix_signals_sp)
+    return false;
+
+  const char *sig_description;
+  std::string description = m_siginfo.GetDescription(*unix_signals_sp);
+  if (description.empty())
+    sig_description = nullptr;
+  else
+    sig_description = description.c_str();
+
   SetStopInfo(StopInfo::CreateStopReasonWithSignal(
-      *this, m_signo, /*description=*/nullptr, m_code));
+      *this, m_siginfo.si_signo, sig_description, m_siginfo.si_code));
+
+  SetStopInfo(m_stop_info_sp);
   return true;
 }
 
@@ -547,21 +561,64 @@ size_t ELFLinuxSigInfo::GetSize(const lldb_private::ArchSpec &arch) {
   }
 }
 
-Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch) {
+Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch,
+                              const lldb_private::UnixSignals &unix_signals) {
   Status error;
-  if (GetSize(arch) > data.GetByteSize()) {
+  uint64_t size = GetSize(arch);
+  if (size > data.GetByteSize()) {
     error = Status::FromErrorStringWithFormat(
         "NT_SIGINFO size should be %zu, but the remaining bytes are: %" PRIu64,
         GetSize(arch), data.GetByteSize());
     return error;
   }
 
+  // Set that we've parsed the siginfo from a SIGINFO note.
+  note_type = eNT_SIGINFO;
   // Parsing from a 32 bit ELF core file, and populating/reusing the structure
   // properly, because the struct is for the 64 bit version
   offset_t offset = 0;
   si_signo = data.GetU32(&offset);
   si_errno = data.GetU32(&offset);
   si_code = data.GetU32(&offset);
+  // 64b ELF have a 4 byte pad.
+  if (data.GetAddressByteSize() == 8)
+    offset += 4;
+  // Not every stop signal has a valid address, but that will get resolved in
+  // the unix_signals.GetSignalDescription() call below.
+  if (unix_signals.GetShouldStop(si_signo)) {
+    // Instead of memcpy we call all these individually as the extractor will
+    // handle endianness for us.
+    sigfault.si_addr = data.GetAddress(&offset);
+    sigfault.si_addr_lsb = data.GetU16(&offset);
+    if (data.GetByteSize() - offset >= sizeof(sigfault.bounds)) {
+      sigfault.bounds._addr_bnd._lower = data.GetAddress(&offset);
+      sigfault.bounds._addr_bnd._upper = data.GetAddress(&offset);
+      sigfault.bounds._pkey = data.GetU32(&offset);
+    } else {
+      // Set these to 0 so we don't use bogus data for the description.
+      sigfault.bounds._addr_bnd._lower = 0;
+      sigfault.bounds._addr_bnd._upper = 0;
+      sigfault.bounds._pkey = 0;
+    }
+  }
 
   return error;
 }
+
+std::string ELFLinuxSigInfo::GetDescription(
+    const lldb_private::UnixSignals &unix_signals) const {
+  if (unix_signals.GetShouldStop(si_signo) && note_type == eNT_SIGINFO) {
+    if (sigfault.bounds._addr_bnd._upper != 0)
+      return unix_signals.GetSignalDescription(
+          si_signo, si_code, sigfault.si_addr, sigfault.bounds._addr_bnd._lower,
+          sigfault.bounds._addr_bnd._upper);
+    else
+      return unix_signals.GetSignalDescription(si_signo, si_code,
+                                               sigfault.si_addr);
+  }
+
+  // This looks weird, but there is an existing pattern where we don't pass a
+  // description to keep up with that, we return empty here, and then the above
+  // function will set the description whether or not this is empty.
+  return std::string();
+}

diff  --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
index 3fa0b8b0eedb0b..4ebbaadebe9f90 100644
--- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
@@ -35,6 +35,8 @@ class ProcessInstanceInfo;
 #undef si_signo
 #undef si_code
 #undef si_errno
+#undef si_addr
+#undef si_addr_lsb
 
 struct ELFLinuxPrStatus {
   int32_t si_signo;
@@ -76,14 +78,36 @@ static_assert(sizeof(ELFLinuxPrStatus) == 112,
               "sizeof ELFLinuxPrStatus is not correct!");
 
 struct ELFLinuxSigInfo {
-  int32_t si_signo;
-  int32_t si_code;
+
+  int32_t si_signo; // Order matters for the first 3.
   int32_t si_errno;
+  int32_t si_code;
+  // Copied from siginfo_t so we don't have to include signal.h on non 'Nix
+  // builds.
+  struct {
+    lldb::addr_t si_addr;  /* faulting insn/memory ref. */
+    short int si_addr_lsb; /* Valid LSB of the reported address.  */
+    union {
+      /* used when si_code=SEGV_BNDERR */
+      struct {
+        lldb::addr_t _lower;
+        lldb::addr_t _upper;
+      } _addr_bnd;
+      /* used when si_code=SEGV_PKUERR */
+      uint32_t _pkey;
+    } bounds;
+  } sigfault;
+
+  enum { eUnspecified, eNT_SIGINFO } note_type;
 
   ELFLinuxSigInfo();
 
   lldb_private::Status Parse(const lldb_private::DataExtractor &data,
-                             const lldb_private::ArchSpec &arch);
+                             const lldb_private::ArchSpec &arch,
+                             const lldb_private::UnixSignals &unix_signals);
+
+  std::string
+  GetDescription(const lldb_private::UnixSignals &unix_signals) const;
 
   // Return the bytesize of the structure
   // 64 bit - just sizeof
@@ -93,7 +117,7 @@ struct ELFLinuxSigInfo {
   static size_t GetSize(const lldb_private::ArchSpec &arch);
 };
 
-static_assert(sizeof(ELFLinuxSigInfo) == 12,
+static_assert(sizeof(ELFLinuxSigInfo) == 56,
               "sizeof ELFLinuxSigInfo is not correct!");
 
 // PRPSINFO structure's size 
diff ers based on architecture.
@@ -142,10 +166,9 @@ struct ThreadData {
   lldb_private::DataExtractor gpregset;
   std::vector<lldb_private::CoreNote> notes;
   lldb::tid_t tid;
-  int signo = 0;
-  int code = 0;
-  int prstatus_sig = 0;
   std::string name;
+  ELFLinuxSigInfo siginfo;
+  int prstatus_sig = 0;
 };
 
 class ThreadElfCore : public lldb_private::Thread {
@@ -176,16 +199,17 @@ class ThreadElfCore : public lldb_private::Thread {
       m_thread_name.clear();
   }
 
+  void CreateStopFromSigInfo(const ELFLinuxSigInfo &siginfo,
+                             const lldb_private::UnixSignals &unix_signals);
+
 protected:
   // Member variables.
   std::string m_thread_name;
   lldb::RegisterContextSP m_thread_reg_ctx_sp;
 
-  int m_signo;
-  int m_code;
-
   lldb_private::DataExtractor m_gpregset_data;
   std::vector<lldb_private::CoreNote> m_notes;
+  ELFLinuxSigInfo m_siginfo;
 
   bool CalculateStopInfo() override;
 };

diff  --git a/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
index 0667759a341b83..779050edb054a4 100644
--- a/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
+++ b/lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py
@@ -2,7 +2,6 @@
 Test that memory tagging features work with Linux core files.
 """
 
-
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -216,8 +215,7 @@ def test_mte_tag_fault_reason(self):
         self.expect(
             "bt",
             substrs=[
-                "* thread #1, name = 'a.out.mte', stop reason = signal SIGSEGV: "
-                "sync tag check fault"
+                "* thread #1, name = 'a.out.mte', stop reason = SIGSEGV: sync tag check fault (fault address: 0xffff82c74010)"
             ],
         )
 

diff  --git a/lldb/test/API/linux/aarch64/non_address_bit_memory_access/TestAArch64LinuxNonAddressBitMemoryAccess.py b/lldb/test/API/linux/aarch64/non_address_bit_memory_access/TestAArch64LinuxNonAddressBitMemoryAccess.py
index d86070c37f98a3..668fca11903660 100644
--- a/lldb/test/API/linux/aarch64/non_address_bit_memory_access/TestAArch64LinuxNonAddressBitMemoryAccess.py
+++ b/lldb/test/API/linux/aarch64/non_address_bit_memory_access/TestAArch64LinuxNonAddressBitMemoryAccess.py
@@ -6,7 +6,6 @@
 API level it won't if we don't remove them there also.
 """
 
-
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -199,7 +198,13 @@ def test_non_address_bit_memory_caching(self):
     def test_non_address_bit_memory_corefile(self):
         self.runCmd("target create --core corefile")
 
-        self.expect("thread list", substrs=["stopped", "stop reason = signal SIGSEGV"])
+        self.expect(
+            "thread list",
+            substrs=[
+                "stopped",
+                "stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)",
+            ],
+        )
 
         # No caching (the program/corefile are the cache) and no writing
         # to memory. So just check that tagged/untagged addresses read

diff  --git a/lldb/test/Shell/Register/Core/x86-32-linux-multithread.test b/lldb/test/Shell/Register/Core/x86-32-linux-multithread.test
index 62e5bb8ee32fd7..eb0cf8708263cb 100644
--- a/lldb/test/Shell/Register/Core/x86-32-linux-multithread.test
+++ b/lldb/test/Shell/Register/Core/x86-32-linux-multithread.test
@@ -1,7 +1,7 @@
 # RUN: %lldb -b -s %s -c %p/Inputs/x86-32-linux-multithread.core | FileCheck %s
 
 thread list
-# CHECK: * thread #1: tid = 330633, 0x080492d2, name = 'a.out', stop reason = signal SIGSEGV
+# CHECK: * thread #1: tid = 330633, 0x080492d2, name = 'a.out', stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)
 # CHECK-NEXT:   thread #2: tid = 330634, 0x080492dd, stop reason = signal 0
 # CHECK-NEXT:   thread #3: tid = 330635, 0x080492dd, stop reason = signal 0
 # CHECK-NEXT:   thread #4: tid = 330632, 0xf7f59549, stop reason = signal 0

diff  --git a/lldb/test/Shell/Register/Core/x86-64-linux-multithread.test b/lldb/test/Shell/Register/Core/x86-64-linux-multithread.test
index ab28901cae9f20..a94a4de1c8080b 100644
--- a/lldb/test/Shell/Register/Core/x86-64-linux-multithread.test
+++ b/lldb/test/Shell/Register/Core/x86-64-linux-multithread.test
@@ -1,7 +1,7 @@
 # RUN: %lldb -b -s %s -c %p/Inputs/x86-64-linux-multithread.core | FileCheck %s
 
 thread list
-# CHECK: * thread #1: tid = 329384, 0x0000000000401262, name = 'a.out', stop reason = signal SIGSEGV
+# CHECK: * thread #1: tid = 329384, 0x0000000000401262, name = 'a.out', stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)
 # CHECK-NEXT:   thread #2: tid = 329385, 0x000000000040126d, stop reason = signal 0
 # CHECK-NEXT:   thread #3: tid = 329386, 0x000000000040126d, stop reason = signal 0
 # CHECK-NEXT:   thread #4: tid = 329383, 0x00007fcf5582f762, stop reason = signal 0


        


More information about the lldb-commits mailing list