diff options
| author | Matthieu Longo <matthieu.longo@arm.com> | 2026-05-29 16:31:28 +0100 |
|---|---|---|
| committer | Matthieu Longo <matthieu.longo@arm.com> | 2026-08-17 22:34:56 +0100 |
| commit | 9c99987237dcaca0c6c493d6a5fb0ab4dc7488e7 (patch) | |
| tree | 80fd772ecd8dc059be6763f1a7f6ac918d6e9ab0 | |
| parent | add_archive_element(): Record pointer to my_archive for later use in the func... (diff) | |
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/20160212210213.ABC488FA@viggo.jf.intel.com/
Reviewed-By: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Approved-By: Simon Marchi <simon.marchi@efficios.com>
| -rw-r--r-- | gdb/aarch64-linux-tdep.c | 8 | ||||
| -rw-r--r-- | gdb/linux-tdep.c | 51 | ||||
| -rw-r--r-- | gdb/linux-tdep.h | 60 | ||||
| -rw-r--r-- | gdb/sparc64-linux-tdep.c | 6 | ||||
| -rw-r--r-- | gdb/testsuite/gdb.base/siginfo-obj.c | 1 | ||||
| -rw-r--r-- | 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, | |||
| 2683 | 2683 | ||
| 2684 | try | 2684 | try |
| 2685 | { | 2685 | { |
| 2686 | using gdb_si = gdb::siginfo_type; | ||
| 2687 | using si_key = gdb::siginfo_type::key; | ||
| 2686 | /* Sigcode tells us if the segfault is actually a memory tag | 2688 | /* Sigcode tells us if the segfault is actually a memory tag |
| 2687 | violation. */ | 2689 | violation. */ |
| 2688 | si_code = parse_and_eval_long ("$_siginfo.si_code"); | 2690 | si_code = parse_and_eval_long (gdb_si::get (si_key::siginfo_code)); |
| 2689 | si_errno = parse_and_eval_long ("$_siginfo.si_errno"); | 2691 | si_errno = parse_and_eval_long (gdb_si::get (si_key::siginfo_errno)); |
| 2690 | 2692 | ||
| 2691 | fault_addr | 2693 | fault_addr |
| 2692 | = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr"); | 2694 | = parse_and_eval_long (gdb_si::get (si_key::siginfo_addr)); |
| 2693 | } | 2695 | } |
| 2694 | catch (const gdb_exception_error &exception) | 2696 | catch (const gdb_exception_error &exception) |
| 2695 | { | 2697 | { |
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 * | |||
| 272 | linux_get_siginfo_type (struct gdbarch *gdbarch) | 272 | linux_get_siginfo_type (struct gdbarch *gdbarch) |
| 273 | { | 273 | { |
| 274 | struct linux_gdbarch_data *linux_gdbarch_data; | 274 | struct linux_gdbarch_data *linux_gdbarch_data; |
| 275 | struct type *void_ptr_type; | ||
| 276 | struct type *uid_type, *pid_type; | 275 | struct type *uid_type, *pid_type; |
| 277 | struct type *sigval_type, *clock_type; | 276 | struct type *sigval_type, *clock_type; |
| 278 | struct type *siginfo_type, *sifields_type; | 277 | struct type *siginfo_type, *sifields_type, *sigfault_union_type; |
| 279 | struct type *type; | 278 | struct type *type; |
| 280 | 279 | ||
| 281 | linux_gdbarch_data = get_linux_gdbarch_data (gdbarch); | 280 | linux_gdbarch_data = get_linux_gdbarch_data (gdbarch); |
| @@ -285,11 +284,22 @@ linux_get_siginfo_type (struct gdbarch *gdbarch) | |||
| 285 | type_allocator alloc (gdbarch); | 284 | type_allocator alloc (gdbarch); |
| 286 | 285 | ||
| 287 | const struct builtin_type *builtin_types = builtin_type (gdbarch); | 286 | const struct builtin_type *builtin_types = builtin_type (gdbarch); |
| 287 | struct type *short_type = builtin_types->builtin_short; | ||
| 288 | struct type *int_type = builtin_types->builtin_int; | 288 | struct type *int_type = builtin_types->builtin_int; |
| 289 | struct type *uint_type = builtin_types->builtin_unsigned_int; | 289 | struct type *uint_type = builtin_types->builtin_unsigned_int; |
| 290 | struct type *long_type = builtin_types->builtin_long; | 290 | struct type *long_type = builtin_types->builtin_long; |
| 291 | 291 | struct type *unsigned_long_type = builtin_types->builtin_unsigned_long; | |
| 292 | void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void); | 292 | struct type *uint32_type = builtin_types->builtin_uint32; |
| 293 | struct type *void_ptr_type | ||
| 294 | = lookup_pointer_type (builtin_type (gdbarch)->builtin_void); | ||
| 295 | |||
| 296 | /* Compute padding length, i.e. __ADDR_BND_PKEY_PAD. */ | ||
| 297 | unsigned alignof_void_ptr = type_align (void_ptr_type); | ||
| 298 | unsigned padding_size = (alignof_void_ptr < short_type->length () | ||
| 299 | ? short_type->length () | ||
| 300 | : alignof_void_ptr); | ||
| 301 | struct type *addr_bnd_pkey_padding_type | ||
| 302 | = init_vector_type (builtin_types->builtin_uint8, padding_size); | ||
| 293 | 303 | ||
| 294 | /* sival_t */ | 304 | /* sival_t */ |
| 295 | sigval_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION); | 305 | sigval_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION); |
| @@ -364,9 +374,40 @@ linux_get_siginfo_type (struct gdbarch *gdbarch) | |||
| 364 | append_composite_type_field (type, "si_stime", clock_type); | 374 | append_composite_type_field (type, "si_stime", clock_type); |
| 365 | append_composite_type_field (sifields_type, "_sigchld", type); | 375 | append_composite_type_field (sifields_type, "_sigchld", type); |
| 366 | 376 | ||
| 367 | /* _sigfault */ | 377 | /* Begin _sigfault's anonymous union. */ |
| 378 | sigfault_union_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION); | ||
| 379 | /* used on alpha and sparc */ | ||
| 380 | append_composite_type_field (sigfault_union_type, "si_trapno", int_type); | ||
| 381 | /* used when si_code is BUS_MCEERR_AR or BUS_MCEERR_AO. */ | ||
| 382 | append_composite_type_field (sigfault_union_type, "si_addr_lsb", short_type); | ||
| 383 | |||
| 384 | /* used when si_code=SEGV_BNDERR */ | ||
| 385 | type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); | ||
| 386 | append_composite_type_field (type, "_dummy_bnd", addr_bnd_pkey_padding_type); | ||
| 387 | append_composite_type_field (type, "si_lower", void_ptr_type); | ||
| 388 | append_composite_type_field (type, "si_upper", void_ptr_type); | ||
| 389 | append_composite_type_field (sigfault_union_type, "_addr_bnd", type); | ||
| 390 | |||
| 391 | /* used when si_code=SEGV_PKUERR */ | ||
| 392 | type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); | ||
| 393 | append_composite_type_field (type, "_dummy_pkey", addr_bnd_pkey_padding_type); | ||
| 394 | append_composite_type_field (type, "si_pkey", uint32_type); | ||
| 395 | append_composite_type_field (sigfault_union_type, "_addr_pkey", type); | ||
| 396 | |||
| 397 | /* used when si_code=TRAP_PERF */ | ||
| 398 | type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); | ||
| 399 | append_composite_type_field (type, "si_perf_data", unsigned_long_type); | ||
| 400 | append_composite_type_field (type, "si_perf_type", uint32_type); | ||
| 401 | append_composite_type_field (type, "si_perf_flags", uint32_type); | ||
| 402 | append_composite_type_field (sigfault_union_type, "_perf", type); | ||
| 403 | |||
| 404 | /* End _sigfault's anonymous union. */ | ||
| 405 | |||
| 406 | /* _sigfault is set by SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT */ | ||
| 368 | type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); | 407 | type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); |
| 369 | append_composite_type_field (type, "si_addr", void_ptr_type); | 408 | append_composite_type_field (type, "si_addr", void_ptr_type); |
| 409 | /* Note: this is an anonymous union. */ | ||
| 410 | append_composite_type_field (type, "", sigfault_union_type); | ||
| 370 | append_composite_type_field (sifields_type, "_sigfault", type); | 411 | append_composite_type_field (sifields_type, "_sigfault", type); |
| 371 | 412 | ||
| 372 | /* _sigpoll */ | 413 | /* _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 (); | |||
| 98 | extern bool linux_address_in_shadow_stack_mem_range | 98 | extern bool linux_address_in_shadow_stack_mem_range |
| 99 | (CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range); | 99 | (CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range); |
| 100 | 100 | ||
| 101 | namespace gdb { | ||
| 102 | |||
| 103 | /* Maps each siginfo_type::key to the corresponding field-access expression | ||
| 104 | in $_siginfo. | ||
| 105 | |||
| 106 | Keep the order of key values synchronized with the entries in get()'s | ||
| 107 | paths array. Each key is used directly as an array index. */ | ||
| 108 | |||
| 109 | struct siginfo_type | ||
| 110 | { | ||
| 111 | /* Identifies a field within siginfo_t that may be referenced by name. */ | ||
| 112 | enum class key | ||
| 113 | { | ||
| 114 | siginfo_signo = 0, | ||
| 115 | siginfo_errno, | ||
| 116 | siginfo_code, | ||
| 117 | |||
| 118 | /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT */ | ||
| 119 | siginfo_addr, | ||
| 120 | siginfo_trapno, | ||
| 121 | siginfo_addr_lsb, | ||
| 122 | siginfo_lower, | ||
| 123 | siginfo_upper, | ||
| 124 | siginfo_pkey, | ||
| 125 | siginfo_perf_data, | ||
| 126 | siginfo_perf_type, | ||
| 127 | siginfo_perf_flags, | ||
| 128 | |||
| 129 | /* Sentinel used to determine the number of mapped fields. */ | ||
| 130 | SIGINFO_ATTR_END | ||
| 131 | }; | ||
| 132 | |||
| 133 | /* Return the $_siginfo access expression associated with ATTR_. | ||
| 134 | |||
| 135 | ATTR_ must be a valid si_* key other than SIGINFO_ATTR_END. The array | ||
| 136 | order must exactly match the declaration order of the keys above. */ | ||
| 137 | static constexpr const char *get (key attr_) | ||
| 138 | { | ||
| 139 | const char *paths[static_cast<size_t> (key::SIGINFO_ATTR_END)] = { | ||
| 140 | "$_siginfo.si_signo", | ||
| 141 | "$_siginfo.si_errno", | ||
| 142 | "$_siginfo.si_code", | ||
| 143 | |||
| 144 | /* SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP, SIGEMT */ | ||
| 145 | "$_siginfo._sifields._sigfault.si_addr", | ||
| 146 | "$_siginfo._sifields._sigfault.si_trapno", | ||
| 147 | "$_siginfo._sifields._sigfault.si_addr_lsb", | ||
| 148 | "$_siginfo._sifields._sigfault._addr_bnd.si_lower", | ||
| 149 | "$_siginfo._sifields._sigfault._addr_bnd.si_upper", | ||
| 150 | "$_siginfo._sifields._sigfault._addr_pkey.si_pkey", | ||
| 151 | "$_siginfo._sifields._sigfault._perf.si_perf_data", | ||
| 152 | "$_siginfo._sifields._sigfault._perf.si_perf_type", | ||
| 153 | "$_siginfo._sifields._sigfault._perf.si_perf_flags", | ||
| 154 | }; | ||
| 155 | return paths[static_cast<size_t> (attr_)]; | ||
| 156 | } | ||
| 157 | }; | ||
| 158 | |||
| 159 | } /* namespace gdb */ | ||
| 160 | |||
| 101 | #endif /* GDB_LINUX_TDEP_H */ | 161 | #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, | |||
| 134 | 134 | ||
| 135 | try | 135 | try |
| 136 | { | 136 | { |
| 137 | using gdb_si = gdb::siginfo_type; | ||
| 138 | using si_key = gdb::siginfo_type::key; | ||
| 137 | /* Evaluate si_code to see if the segfault is ADI related. */ | 139 | /* Evaluate si_code to see if the segfault is ADI related. */ |
| 138 | si_code = parse_and_eval_long ("$_siginfo.si_code\n"); | 140 | si_code = parse_and_eval_long (gdb_si::get (si_key::siginfo_code)); |
| 139 | 141 | ||
| 140 | if (si_code >= SEGV_ACCADI && si_code <= SEGV_ADIPERR) | 142 | if (si_code >= SEGV_ACCADI && si_code <= SEGV_ADIPERR) |
| 141 | addr = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr"); | 143 | addr = parse_and_eval_long (gdb_si::get (si_key::siginfo_addr)); |
| 142 | } | 144 | } |
| 143 | catch (const gdb_exception_error &exception) | 145 | catch (const gdb_exception_error &exception) |
| 144 | { | 146 | { |
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) | |||
| 35 | int ssi_signo = info->si_signo; | 35 | int ssi_signo = info->si_signo; |
| 36 | int ssi_code = info->si_code; | 36 | int ssi_code = info->si_code; |
| 37 | void *ssi_addr = info->si_addr; | 37 | void *ssi_addr = info->si_addr; |
| 38 | unsigned int ssi_pkey = info->si_pkey; | ||
| 38 | 39 | ||
| 39 | _exit (0); /* set breakpoint here */ | 40 | _exit (0); /* set breakpoint here */ |
| 40 | } | 41 | } |
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" { | |||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | set test "extract si_pkey" | ||
| 82 | gdb_test_multiple "p \$_siginfo" "$test" { | ||
| 83 | -re "si_pkey = (\[0-9\]\+).*$gdb_prompt $" { | ||
| 84 | set ssi_pkey $expect_out(1,string) | ||
| 85 | pass "$test" | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 81 | set bp_location [gdb_get_line_number "set breakpoint here"] | 89 | set bp_location [gdb_get_line_number "set breakpoint here"] |
| 82 | 90 | ||
| 83 | with_test_prefix "validate siginfo fields" { | 91 | with_test_prefix "validate siginfo fields" { |
| @@ -87,6 +95,7 @@ with_test_prefix "validate siginfo fields" { | |||
| 87 | gdb_test "p ssi_errno" " = $ssi_errno" | 95 | gdb_test "p ssi_errno" " = $ssi_errno" |
| 88 | gdb_test "p ssi_code" " = $ssi_code" | 96 | gdb_test "p ssi_code" " = $ssi_code" |
| 89 | gdb_test "p ssi_signo" " = $ssi_signo" | 97 | gdb_test "p ssi_signo" " = $ssi_signo" |
| 98 | gdb_test "p ssi_pkey" " = $ssi_pkey" | ||
| 90 | } | 99 | } |
| 91 | 100 | ||
| 92 | # Again, but this time, patch si_addr and check that the inferior sees | 101 | # 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 | |||
| 106 | gdb_test "p \$_siginfo.si_errno = 666" " = 666" | 115 | gdb_test "p \$_siginfo.si_errno = 666" " = 666" |
| 107 | gdb_test "p \$_siginfo.si_code = 999" " = 999" | 116 | gdb_test "p \$_siginfo.si_code = 999" " = 999" |
| 108 | gdb_test "p \$_siginfo.si_signo = 11" " = 11" | 117 | gdb_test "p \$_siginfo.si_signo = 11" " = 11" |
| 118 | gdb_test "p \$_siginfo._sifields._sigfault._addr_pkey.si_pkey = 123" " = 123" | ||
| 109 | 119 | ||
| 110 | with_test_prefix "validate modified siginfo fields" { | 120 | with_test_prefix "validate modified siginfo fields" { |
| 111 | gdb_test "break $bp_location" | 121 | gdb_test "break $bp_location" |
| @@ -114,6 +124,7 @@ with_test_prefix "validate modified siginfo fields" { | |||
| 114 | gdb_test "p ssi_errno" " = 666" | 124 | gdb_test "p ssi_errno" " = 666" |
| 115 | gdb_test "p ssi_code" " = 999" | 125 | gdb_test "p ssi_code" " = 999" |
| 116 | gdb_test "p ssi_signo" " = 11" | 126 | gdb_test "p ssi_signo" " = 11" |
| 127 | gdb_test "p ssi_pkey" " = 123" | ||
| 117 | } | 128 | } |
| 118 | 129 | ||
| 119 | # Test siginfo preservation in core files. | 130 | # Test siginfo preservation in core files. |
| @@ -132,4 +143,7 @@ if {$gcore_created} { | |||
| 132 | gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ | 143 | gdb_test "p \$_siginfo._sifields._sigfault.si_addr" \ |
| 133 | " = \\(void \\*\\) $ssi_addr" \ | 144 | " = \\(void \\*\\) $ssi_addr" \ |
| 134 | "p \$_siginfo._sifields._sigfault.si_addr from core file" | 145 | "p \$_siginfo._sifields._sigfault.si_addr from core file" |
| 146 | gdb_test "p \$_siginfo._sifields._sigfault._addr_pkey.si_pkey" \ | ||
| 147 | " = $ssi_pkey" \ | ||
| 148 | "p \$_siginfo._sifields._sigfault._addr_pkey.si_pkey from core file" | ||
| 135 | } | 149 | } |
