Skip to content

Commit 0a72429

Browse files
authored
[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.
1 parent 95ddc1a commit 0a72429

File tree

7 files changed

+117
-33
lines changed

7 files changed

+117
-33
lines changed

lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ Status ProcessElfCore::DoLoadCore() {
232232
bool prstatus_signal_found = false;
233233
// Check we found a signal in a SIGINFO note.
234234
for (const auto &thread_data : m_thread_data) {
235-
if (thread_data.signo != 0)
235+
if (thread_data.siginfo.si_signo != 0)
236236
siginfo_signal_found = true;
237237
if (thread_data.prstatus_sig != 0)
238238
prstatus_signal_found = true;
@@ -242,10 +242,10 @@ Status ProcessElfCore::DoLoadCore() {
242242
// PRSTATUS note.
243243
if (prstatus_signal_found) {
244244
for (auto &thread_data : m_thread_data)
245-
thread_data.signo = thread_data.prstatus_sig;
245+
thread_data.siginfo.si_signo = thread_data.prstatus_sig;
246246
} else if (m_thread_data.size() > 0) {
247247
// If all else fails force the first thread to be SIGSTOP
248-
m_thread_data.begin()->signo =
248+
m_thread_data.begin()->siginfo.si_signo =
249249
GetUnixSignals()->GetSignalNumberFromName("SIGSTOP");
250250
}
251251
}
@@ -498,7 +498,7 @@ static void ParseFreeBSDPrStatus(ThreadData &thread_data,
498498
else
499499
offset += 16;
500500

501-
thread_data.signo = data.GetU32(&offset); // pr_cursig
501+
thread_data.siginfo.si_signo = data.GetU32(&offset); // pr_cursig
502502
thread_data.tid = data.GetU32(&offset); // pr_pid
503503
if (lp64)
504504
offset += 4;
@@ -581,7 +581,7 @@ static void ParseOpenBSDProcInfo(ThreadData &thread_data,
581581
return;
582582

583583
offset += 4;
584-
thread_data.signo = data.GetU32(&offset);
584+
thread_data.siginfo.si_signo = data.GetU32(&offset);
585585
}
586586

587587
llvm::Expected<std::vector<CoreNote>>
@@ -819,15 +819,15 @@ llvm::Error ProcessElfCore::parseNetBSDNotes(llvm::ArrayRef<CoreNote> notes) {
819819
// Signal targeted at the whole process.
820820
if (siglwp == 0) {
821821
for (auto &data : m_thread_data)
822-
data.signo = signo;
822+
data.siginfo.si_signo = signo;
823823
}
824824
// Signal destined for a particular LWP.
825825
else {
826826
bool passed = false;
827827

828828
for (auto &data : m_thread_data) {
829829
if (data.tid == siglwp) {
830-
data.signo = signo;
830+
data.siginfo.si_signo = signo;
831831
passed = true;
832832
break;
833833
}
@@ -930,12 +930,12 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
930930
break;
931931
}
932932
case ELF::NT_SIGINFO: {
933+
const lldb_private::UnixSignals &unix_signals = *GetUnixSignals();
933934
ELFLinuxSigInfo siginfo;
934-
Status status = siginfo.Parse(note.data, arch);
935+
Status status = siginfo.Parse(note.data, arch, unix_signals);
935936
if (status.Fail())
936937
return status.ToError();
937-
thread_data.signo = siginfo.si_signo;
938-
thread_data.code = siginfo.si_code;
938+
thread_data.siginfo = siginfo;
939939
break;
940940
}
941941
case ELF::NT_FILE: {

lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "lldb/Target/RegisterContext.h"
1010
#include "lldb/Target/StopInfo.h"
1111
#include "lldb/Target/Target.h"
12+
#include "lldb/Target/UnixSignals.h"
1213
#include "lldb/Target/Unwind.h"
1314
#include "lldb/Utility/DataExtractor.h"
1415
#include "lldb/Utility/LLDBLog.h"
@@ -49,9 +50,9 @@ using namespace lldb_private;
4950

5051
// Construct a Thread object with given data
5152
ThreadElfCore::ThreadElfCore(Process &process, const ThreadData &td)
52-
: Thread(process, td.tid), m_thread_name(td.name), m_thread_reg_ctx_sp(),
53-
m_signo(td.signo), m_code(td.code), m_gpregset_data(td.gpregset),
54-
m_notes(td.notes) {}
53+
: Thread(process, td.tid), m_thread_reg_ctx_sp(), m_thread_name(td.name),
54+
m_gpregset_data(td.gpregset), m_notes(td.notes),
55+
m_siginfo(std::move(td.siginfo)) {}
5556

5657
ThreadElfCore::~ThreadElfCore() { DestroyThread(); }
5758

@@ -246,8 +247,21 @@ bool ThreadElfCore::CalculateStopInfo() {
246247
if (!process_sp)
247248
return false;
248249

250+
lldb::UnixSignalsSP unix_signals_sp(process_sp->GetUnixSignals());
251+
if (!unix_signals_sp)
252+
return false;
253+
254+
const char *sig_description;
255+
std::string description = m_siginfo.GetDescription(*unix_signals_sp);
256+
if (description.empty())
257+
sig_description = nullptr;
258+
else
259+
sig_description = description.c_str();
260+
249261
SetStopInfo(StopInfo::CreateStopReasonWithSignal(
250-
*this, m_signo, /*description=*/nullptr, m_code));
262+
*this, m_siginfo.si_signo, sig_description, m_siginfo.si_code));
263+
264+
SetStopInfo(m_stop_info_sp);
251265
return true;
252266
}
253267

@@ -547,21 +561,64 @@ size_t ELFLinuxSigInfo::GetSize(const lldb_private::ArchSpec &arch) {
547561
}
548562
}
549563

550-
Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch) {
564+
Status ELFLinuxSigInfo::Parse(const DataExtractor &data, const ArchSpec &arch,
565+
const lldb_private::UnixSignals &unix_signals) {
551566
Status error;
552-
if (GetSize(arch) > data.GetByteSize()) {
567+
uint64_t size = GetSize(arch);
568+
if (size > data.GetByteSize()) {
553569
error = Status::FromErrorStringWithFormat(
554570
"NT_SIGINFO size should be %zu, but the remaining bytes are: %" PRIu64,
555571
GetSize(arch), data.GetByteSize());
556572
return error;
557573
}
558574

575+
// Set that we've parsed the siginfo from a SIGINFO note.
576+
note_type = eNT_SIGINFO;
559577
// Parsing from a 32 bit ELF core file, and populating/reusing the structure
560578
// properly, because the struct is for the 64 bit version
561579
offset_t offset = 0;
562580
si_signo = data.GetU32(&offset);
563581
si_errno = data.GetU32(&offset);
564582
si_code = data.GetU32(&offset);
583+
// 64b ELF have a 4 byte pad.
584+
if (data.GetAddressByteSize() == 8)
585+
offset += 4;
586+
// Not every stop signal has a valid address, but that will get resolved in
587+
// the unix_signals.GetSignalDescription() call below.
588+
if (unix_signals.GetShouldStop(si_signo)) {
589+
// Instead of memcpy we call all these individually as the extractor will
590+
// handle endianness for us.
591+
sigfault.si_addr = data.GetAddress(&offset);
592+
sigfault.si_addr_lsb = data.GetU16(&offset);
593+
if (data.GetByteSize() - offset >= sizeof(sigfault.bounds)) {
594+
sigfault.bounds._addr_bnd._lower = data.GetAddress(&offset);
595+
sigfault.bounds._addr_bnd._upper = data.GetAddress(&offset);
596+
sigfault.bounds._pkey = data.GetU32(&offset);
597+
} else {
598+
// Set these to 0 so we don't use bogus data for the description.
599+
sigfault.bounds._addr_bnd._lower = 0;
600+
sigfault.bounds._addr_bnd._upper = 0;
601+
sigfault.bounds._pkey = 0;
602+
}
603+
}
565604

566605
return error;
567606
}
607+
608+
std::string ELFLinuxSigInfo::GetDescription(
609+
const lldb_private::UnixSignals &unix_signals) const {
610+
if (unix_signals.GetShouldStop(si_signo) && note_type == eNT_SIGINFO) {
611+
if (sigfault.bounds._addr_bnd._upper != 0)
612+
return unix_signals.GetSignalDescription(
613+
si_signo, si_code, sigfault.si_addr, sigfault.bounds._addr_bnd._lower,
614+
sigfault.bounds._addr_bnd._upper);
615+
else
616+
return unix_signals.GetSignalDescription(si_signo, si_code,
617+
sigfault.si_addr);
618+
}
619+
620+
// This looks weird, but there is an existing pattern where we don't pass a
621+
// description to keep up with that, we return empty here, and then the above
622+
// function will set the description whether or not this is empty.
623+
return std::string();
624+
}

lldb/source/Plugins/Process/elf-core/ThreadElfCore.h

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class ProcessInstanceInfo;
3535
#undef si_signo
3636
#undef si_code
3737
#undef si_errno
38+
#undef si_addr
39+
#undef si_addr_lsb
3840

3941
struct ELFLinuxPrStatus {
4042
int32_t si_signo;
@@ -76,14 +78,36 @@ static_assert(sizeof(ELFLinuxPrStatus) == 112,
7678
"sizeof ELFLinuxPrStatus is not correct!");
7779

7880
struct ELFLinuxSigInfo {
79-
int32_t si_signo;
80-
int32_t si_code;
81+
82+
int32_t si_signo; // Order matters for the first 3.
8183
int32_t si_errno;
84+
int32_t si_code;
85+
// Copied from siginfo_t so we don't have to include signal.h on non 'Nix
86+
// builds.
87+
struct {
88+
lldb::addr_t si_addr; /* faulting insn/memory ref. */
89+
short int si_addr_lsb; /* Valid LSB of the reported address. */
90+
union {
91+
/* used when si_code=SEGV_BNDERR */
92+
struct {
93+
lldb::addr_t _lower;
94+
lldb::addr_t _upper;
95+
} _addr_bnd;
96+
/* used when si_code=SEGV_PKUERR */
97+
uint32_t _pkey;
98+
} bounds;
99+
} sigfault;
100+
101+
enum { eUnspecified, eNT_SIGINFO } note_type;
82102

83103
ELFLinuxSigInfo();
84104

85105
lldb_private::Status Parse(const lldb_private::DataExtractor &data,
86-
const lldb_private::ArchSpec &arch);
106+
const lldb_private::ArchSpec &arch,
107+
const lldb_private::UnixSignals &unix_signals);
108+
109+
std::string
110+
GetDescription(const lldb_private::UnixSignals &unix_signals) const;
87111

88112
// Return the bytesize of the structure
89113
// 64 bit - just sizeof
@@ -93,7 +117,7 @@ struct ELFLinuxSigInfo {
93117
static size_t GetSize(const lldb_private::ArchSpec &arch);
94118
};
95119

96-
static_assert(sizeof(ELFLinuxSigInfo) == 12,
120+
static_assert(sizeof(ELFLinuxSigInfo) == 56,
97121
"sizeof ELFLinuxSigInfo is not correct!");
98122

99123
// PRPSINFO structure's size differs based on architecture.
@@ -142,10 +166,9 @@ struct ThreadData {
142166
lldb_private::DataExtractor gpregset;
143167
std::vector<lldb_private::CoreNote> notes;
144168
lldb::tid_t tid;
145-
int signo = 0;
146-
int code = 0;
147-
int prstatus_sig = 0;
148169
std::string name;
170+
ELFLinuxSigInfo siginfo;
171+
int prstatus_sig = 0;
149172
};
150173

151174
class ThreadElfCore : public lldb_private::Thread {
@@ -176,16 +199,17 @@ class ThreadElfCore : public lldb_private::Thread {
176199
m_thread_name.clear();
177200
}
178201

202+
void CreateStopFromSigInfo(const ELFLinuxSigInfo &siginfo,
203+
const lldb_private::UnixSignals &unix_signals);
204+
179205
protected:
180206
// Member variables.
181207
std::string m_thread_name;
182208
lldb::RegisterContextSP m_thread_reg_ctx_sp;
183209

184-
int m_signo;
185-
int m_code;
186-
187210
lldb_private::DataExtractor m_gpregset_data;
188211
std::vector<lldb_private::CoreNote> m_notes;
212+
ELFLinuxSigInfo m_siginfo;
189213

190214
bool CalculateStopInfo() override;
191215
};

lldb/test/API/linux/aarch64/mte_core_file/TestAArch64LinuxMTEMemoryTagCoreFile.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Test that memory tagging features work with Linux core files.
33
"""
44

5-
65
import lldb
76
from lldbsuite.test.decorators import *
87
from lldbsuite.test.lldbtest import *
@@ -216,8 +215,7 @@ def test_mte_tag_fault_reason(self):
216215
self.expect(
217216
"bt",
218217
substrs=[
219-
"* thread #1, name = 'a.out.mte', stop reason = signal SIGSEGV: "
220-
"sync tag check fault"
218+
"* thread #1, name = 'a.out.mte', stop reason = SIGSEGV: sync tag check fault (fault address: 0xffff82c74010)"
221219
],
222220
)
223221

lldb/test/API/linux/aarch64/non_address_bit_memory_access/TestAArch64LinuxNonAddressBitMemoryAccess.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
API level it won't if we don't remove them there also.
77
"""
88

9-
109
import lldb
1110
from lldbsuite.test.decorators import *
1211
from lldbsuite.test.lldbtest import *
@@ -199,7 +198,13 @@ def test_non_address_bit_memory_caching(self):
199198
def test_non_address_bit_memory_corefile(self):
200199
self.runCmd("target create --core corefile")
201200

202-
self.expect("thread list", substrs=["stopped", "stop reason = signal SIGSEGV"])
201+
self.expect(
202+
"thread list",
203+
substrs=[
204+
"stopped",
205+
"stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)",
206+
],
207+
)
203208

204209
# No caching (the program/corefile are the cache) and no writing
205210
# to memory. So just check that tagged/untagged addresses read

lldb/test/Shell/Register/Core/x86-32-linux-multithread.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# RUN: %lldb -b -s %s -c %p/Inputs/x86-32-linux-multithread.core | FileCheck %s
22

33
thread list
4-
# CHECK: * thread #1: tid = 330633, 0x080492d2, name = 'a.out', stop reason = signal SIGSEGV
4+
# CHECK: * thread #1: tid = 330633, 0x080492d2, name = 'a.out', stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)
55
# CHECK-NEXT: thread #2: tid = 330634, 0x080492dd, stop reason = signal 0
66
# CHECK-NEXT: thread #3: tid = 330635, 0x080492dd, stop reason = signal 0
77
# CHECK-NEXT: thread #4: tid = 330632, 0xf7f59549, stop reason = signal 0

lldb/test/Shell/Register/Core/x86-64-linux-multithread.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# RUN: %lldb -b -s %s -c %p/Inputs/x86-64-linux-multithread.core | FileCheck %s
22

33
thread list
4-
# CHECK: * thread #1: tid = 329384, 0x0000000000401262, name = 'a.out', stop reason = signal SIGSEGV
4+
# CHECK: * thread #1: tid = 329384, 0x0000000000401262, name = 'a.out', stop reason = SIGSEGV: address not mapped to object (fault address: 0x0)
55
# CHECK-NEXT: thread #2: tid = 329385, 0x000000000040126d, stop reason = signal 0
66
# CHECK-NEXT: thread #3: tid = 329386, 0x000000000040126d, stop reason = signal 0
77
# CHECK-NEXT: thread #4: tid = 329383, 0x00007fcf5582f762, stop reason = signal 0

0 commit comments

Comments
 (0)