[binutils-gdb] gdb: align siginfo_t with the Linux kernel definition
Matthieu Longo via Gdb-cvs <[email protected]>
| Newsgroups | gmane.comp.gdb.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9c99987237dcaca0c6c493d6a5fb0ab4dc7488e7 commit 9c99987237dcaca0c6c493d6a5fb0ab4dc7488e7 Author: Matthieu Longo <[email protected]> Date: Fri May 29 16:31:28 2026 +0100 gdb: align siginfo_t with the Linux kernel definition GDB's current definition of siginfo_t is missing many fields present in the Linux kernel definition [1]. These fields are useful for providing detailed, user-friendly diagnostics when a fault occurs. Some new AArch64 extensions, such as Permission Overlay Enhancement used to implement Protection Keys [2], require the debugger to inspect 'si_pkey' alongside 'si_addr' to help the user identify the problematic key. This patch aligns GDB's definition of the __sifields._sigfault member of siginfo_t with the definition from the Linux kernel master branch. To avoid hardcoding the field access paths throughout the codebase, this patch also introduces compile-time accessors for the siginfo_t attributes, centralizing their definitions in a single location and making future updates easier. Finally, extend the testsuite to verify access to the new si_pkey field and its preservation when modifying $_siginfo and when reading core files. The tests in siginfo-obj.exp rely on the siginfo_t definition provided by glibc's <signal.h>, which does not yet expose all of the fields present in the kernel definition. As a result, the tests cannot exercise every newly added field and therefore focus on si_pkey, the field motivating this change. The test validates that GDB can read and modify the field correctly; it does not attempt to generate a real protection-key fault. [1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ tree/include/uapi/asm-generic/siginfo.h#n69 [2]: https://lore.kernel.org/all/[email protected]/ Reviewed-By: Thiago Jung Bauermann <[email protected]> Approved-By: Simon Marchi <[email protected]> Diff: --- gdb/aarch64-linux-tdep.c | 8 +++-- gdb/linux-tdep.c | 51 ++++++++++++++++++++++++++--- gdb/linux-tdep.h | 60 ++++++++++++++++++++++++++++++++++ gdb/sparc64-linux-tdep.c | 6 ++-- gdb/testsuite/gdb.base/siginfo-obj.c | 1 + gdb/testsuite/gdb.base/siginfo-obj.exp | 14 ++++++++ 6 files changed, 130 insertions(+), 10 deletions(-) diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index f11eccc1bc1..235b35bcfb4 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -2683,13 +2683,15 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch, try { + using gdb_si = gdb::siginfo_type; + using si_key = gdb::siginfo_type::key; /* Sigcode tells us if the segfault is actually a memory tag violation. */ - si_code = parse_and_eval_long ("$_siginfo.si_code"); - si_errno = parse_and_eval_long ("$_siginfo.si_errno"); + si_code = parse_and_eval_long (gdb_si::get (si_key::siginfo_code)); + si_errno = parse_and_eval_long (gdb_si::get (si_key::siginfo_errno)); fault_addr - = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr"); + = parse_and_eval_long (gdb_si::get (si_key::siginfo_addr)); } catch (const gdb_exception_error &exception) { diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 23e43ba5c5f..bbc4009d43c 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -272,10 +272,9 @@ static struct type * linux_get_siginfo_type (struct gdbarch *gdbarch) { struct linux_gdbarch_data *linux_gdbarch_data; - struct type *void_ptr_type; struct type *uid_type, *pid_type; struct type *sigval_type, *clock_type; - struct type *siginfo_type, *sifields_type; + struct type *siginfo_type, *sifields_type, *sigfault_union_type; struct type *type; linux_gdbarch_data = get_linux_gdbarch_data (gdbarch); @@ -285,11 +284,22 @@ linux_get_siginfo_type (struct gdbarch *gdbarch) type_allocator alloc (gdbarch); const struct builtin_type *builtin_types = builtin_type (gdbarch); + struct type *short_type = builtin_types->builtin_short; struct type *int_type = builtin_types->builtin_int; struct type *uint_type = builtin_types->builtin_unsigned_int; struct type *long_type = builtin_types->builtin_long; - - void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void); + struct type *unsigned_long_type = builtin_types->builtin_unsigned_long; + struct type *uint32_type = builtin_types->builtin_uint32; + struct type *void_ptr_type + = lookup_pointer_type (builtin_type (gdbarch)->builtin_void); + + /* Compute padding length, i.e. __ADDR_BND_PKEY_PAD. */ + unsigned alignof_void_ptr = type_align (void_ptr_type); + unsigned padding_size = (alignof_void_ptr < short_type->length () + ? short_type->length () + : alignof_void_ptr); + struct type *addr_bnd_pkey_padding_type + = init_vector_type (builtin_types->builtin_uint8, padding_size); /* sival_t */ sigval_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION); @@ -364,9 +374,40 @@ linux_get_siginfo_type (struct gdbarch *gdbarch) append_composite_type_field (type, "si_stime", clock_type); append_composite_type_field (sifields_type, "_sigchld", type); - /* _sigfault */ + /* Begin _sigfault's anonymous union. */ + sigfault_union_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION); + /* used on alpha and sparc */ + append_composite_type_field (sigfault_union_type, "si_trapno", int_type); + /* used when si_code is BUS_MCEERR_AR or BUS_MCEERR_AO. */ + append_composite_type_field (sigfault_union_type, "si_addr_lsb", short_type); + + /* used when si_code=SEGV_BNDERR */ + type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "_dummy_bnd", addr_bnd_pkey_padding_type); + append_composite_type_field (type, "si_lower", void_ptr_type); + append_composite_type_field (type, "si_upper", void_ptr_type); + append_composite_type_field (sigfault_union_type, "_addr_bnd", type); + + /* used when si_code=SEGV_PKUERR */ + type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "_dummy_pkey", addr_bnd_pkey_padding_type); + append_composite_type_field (type, "si_pkey", uint32_type); + append_composite_type_field (sigfault_union_type, "_addr_pkey", type); + + /* used when si_code=TRAP_PERF */ + type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + append_composite_type_field (type, "si_perf_data", unsigned_long_type); + append_composite_type_field (type, "si_perf_type", uint32_type); + append_composite_type_field (type, "si_perf_flags", uint32_type); + append_composite_type_field (sigfault_union_type, "_perf", type); + + /* End _sigfault's anonymous union. */ + + /* _sigfault is set by SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT */ type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); append_composite_type_field (type, "si_addr", void_ptr_type); + /* Note: this is an anonymous union. */ + append_composite_type_field (type, "", sigfault_union_type); append_composite_type_field (sifields_type, "_sigfault", type); /* _sigpoll */ diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h index c19839fde2c..1f40756eb2f 100644 --- a/gdb/linux-tdep.h +++ b/gdb/linux-tdep.h @@ -98,4 +98,64 @@ extern CORE_ADDR linux_get_hwcap2 (); extern bool linux_address_in_shadow_stack_mem_range (CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range); +namespace gdb { + +/* Maps each siginfo_type::key to the corresponding field-access expression + in $_siginfo. + + Keep the order of key values synchronized with the entries in get()'s + paths array. Each key is used directly as an array index. */ + +struct siginfo_type +{ + /* Identifies a field within siginfo_t that may be referenced by name. */ + enum class key + { + siginfo_signo = 0, + siginfo_errno, + siginfo_code, + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT */ + siginfo_addr, + siginfo_trapno, + siginfo_addr_lsb, + siginfo_lower, + siginfo_upper, + siginfo_pkey, + siginfo_perf_data, + siginfo_perf_type, + siginfo_perf_flags, + + /* Sentinel used to determine the number of mapped fields. */ + SIGINFO_ATTR_END + }; + + /* Return the $_siginfo access expression associated with ATTR_. + + ATTR_ must be a valid si_* key other than SIGINFO_ATTR_END. The array + order must exactly match the declaration order of the keys above. */ + static constexpr const char *get (key attr_) + { + const char *paths[static_cast<size_t> (key::SIGINFO_ATTR_END)] = { + "$_siginfo.si_signo", + "$_siginfo.si_errno", + "$_siginfo.si_code", + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT */ + "$_siginfo._sifields._sigfault.si_addr", + "$_siginfo._sifields._sigfault.si_trapno", + "$_siginfo._sifields._sigfault.si_addr_lsb", + "$_siginfo._sifields._sigfault._addr_bnd.si_lower", + "$_siginfo._sifields._sigfault._addr_bnd.si_upper", + "$_siginfo._sifields._sigfault._addr_pkey.si_pkey", + "$_siginfo._sifields._sigfault._perf.si_perf_data", + "$_siginfo._sifields._sigfault._perf.si_perf_type", + "$_siginfo._sifields._sigfault._perf.si_perf_flags", + }; + return paths[static_cast<size_t> (attr_)]; + } +}; + +} /* namespace gdb */ + #endif /* GDB_LINUX_TDEP_H */ diff --git a/gdb/sparc64-linux-tdep.c b/gdb/sparc64-linux-tdep.c index cb7ce41e5cb..b6245f64bae 100644 --- a/gdb/sparc64-linux-tdep.c +++ b/gdb/sparc64-linux-tdep.c @@ -134,11 +134,13 @@ sparc64_linux_report_signal_info (struct gdbarch *gdbarch, struct ui_out *uiout, try { + using gdb_si = gdb::siginfo_type; + using si_key = gdb::siginfo_type::key; /* Evaluate si_code to see if the segfault is ADI related. */ - si_code = parse_and_eval_long ("$_siginfo.si_code\n"); + si_code = parse_and_eval_long (gdb_si::get (si_key::siginfo_code)); if (si_code >= SEGV_ACCADI && si_code <= SEGV_ADIPERR) - addr = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr"); + addr = parse_and_eval_long (gdb_si::get (si_key::siginfo_addr)); } catch (const gdb_exception_error &exception) { diff --git a/gdb/testsuite/gdb.base/siginfo-obj.c b/gdb/testsuite/gdb.base/siginfo-obj.c index 43dc979bc50..960e5b8e9cd 100644 --- a/gdb/testsuite/gdb.base/siginfo-obj.c +++ b/gdb/testsuite/gdb.base/siginfo-obj.c @@ -35,6 +35,7 @@ handler (int sig, siginfo_t *info, void *context) int ssi_signo = info->si_signo; int ssi_code = info->si_code; void *ssi_addr = info->si_addr; + unsigned int ssi_pkey = info->si_pkey; _exit (0); /* set breakpoint here */ } diff --git a/gdb/testsuite/gdb.base/siginfo-obj.exp b/gdb/testsuite/gdb.base/siginfo-obj.exp index a94bf0e33ba..a724a634dc0 100644 --- a/gdb/testsuite/gdb.base/siginfo-obj.exp +++ b/gdb/testsuite/gdb.base/siginfo-obj.exp @@ -78,6 +78,14 @@ gdb_test_multiple "p \$_siginfo" "$test" { } } +set test "extract si_pkey" +gdb_test_multiple "p \$_siginfo" "$test" { + -re "si_pkey = (\[0-9\]\+).*$gdb_prompt $" { + set ssi_pkey $expect_out(1,string) + pass "$test" + } +} + set bp_location [gdb_get_line_number "set breakpoint here"] with_test_prefix "validate siginfo fields" { @@ -87,6 +95,7 @@ with_test_prefix "validate siginfo fields" { gdb_test "p ssi_errno" " = $ssi_errno" gdb_test "p ssi_code" " = $ssi_code" gdb_test "p ssi_signo" " = $ssi_signo" + gdb_test "p ssi_pkey" " = $ssi_pkey" } # Again, but this time, patch si_addr and check that the inferior sees @@ -106,6 +115,7 @@ gdb_test "p \$_siginfo._sifields._sigfault.si_addr = 0x666" " = \\(void \\*\\) 0 gdb_test "p \$_siginfo.si_errno = 666" " = 666" gdb_test "p \$_siginfo.si_code = 999" " = 999" gdb_test "p \$_siginfo.si_signo = 11" " = 11" +gdb_test "p \$_siginfo._sifields._sigfault._addr_pkey.si_pkey = 123" " = 123" with_test_prefix "validate modified siginfo fields" { gdb_test "break $bp_location" @@ -114,6 +124,7 @@ with_test_prefix "validate modified siginfo fields" { gdb_test "p ssi_errno" " = 666" gdb_test "p ssi_code" " = 999" gdb_test "p ssi_signo" " = 11" + gdb_test "p ssi_pkey" " = 123" } # Test siginfo preservation in core files. @@ -132,4 +143,7 @@ if {$gcore_created} { gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ " = \\(void \\*\\) $ssi_addr" \ "p \$_siginfo._sifields._sigfault.si_addr from core file" + gdb_test "p \$_siginfo._sifields._sigfault._addr_pkey.si_pkey" \ + " = $ssi_pkey" \ + "p \$_siginfo._sifields._sigfault._addr_pkey.si_pkey from core file" }