diff options
author | Thiago Jung Bauermann <thiago.bauermann@linaro.org> | 2025-05-01 21:24:01 -0300 |
---|---|---|
committer | Thiago Jung Bauermann <thiago.bauermann@linaro.org> | 2025-08-29 18:35:58 -0300 |
commit | a5ae00c47eb678462c5d2ed9c3b31fc59bb4ba45 (patch) | |
tree | d4dd687cf978b9bf489e37cb94089cccb31eafb0 | |
parent | ff62d39aa4999f44ac79360dee6357fc69d4e322 (diff) | |
download | binutils-a5ae00c47eb678462c5d2ed9c3b31fc59bb4ba45.zip binutils-a5ae00c47eb678462c5d2ed9c3b31fc59bb4ba45.tar.gz binutils-a5ae00c47eb678462c5d2ed9c3b31fc59bb4ba45.tar.bz2 |
GDB: aarch64-linux: GCS support in Linux signals
The signal frame can have a GCS context, so teach GDB how to use it.
Also, there's a new SEGV sigcode when the inferior does an illegal
memory access in the Guarded Control Stack, so display a message when
that is the case.
Reviewed-By: Christina Schimpe <christina.schimpe@intel.com>
Approved-By: Luis Machado <luis.machado@arm.com>
-rw-r--r-- | gdb/aarch64-linux-tdep.c | 83 | ||||
-rw-r--r-- | gdb/linux-tdep.h | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-gcs-core.exp | 6 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-gcs.exp | 10 |
4 files changed, 91 insertions, 12 deletions
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 7d360eb..88d4ad9 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -166,6 +166,7 @@ #define AARCH64_ZA_MAGIC 0x54366345 #define AARCH64_TPIDR2_MAGIC 0x54504902 #define AARCH64_ZT_MAGIC 0x5a544e01 +#define AARCH64_GCS_MAGIC 0x47435300 /* Defines for the extra_context that follows an AARCH64_EXTRA_MAGIC. */ #define AARCH64_EXTRA_DATAP_OFFSET 8 @@ -207,6 +208,11 @@ the signal context state. */ #define AARCH64_SME2_CONTEXT_REGS_OFFSET 16 +/* GCSPR register value offset in the GCS signal frame context. */ +#define AARCH64_GCS_CONTEXT_GCSPR_OFFSET 8 +/* features_enabled value offset in the GCS signal frame context. */ +#define AARCH64_GCS_CONTEXT_FEATURES_ENABLED_OFFSET 16 + /* Holds information about the signal frame. */ struct aarch64_linux_sigframe { @@ -247,6 +253,13 @@ struct aarch64_linux_sigframe bool za_payload = false; /* True if we have a ZT entry in the signal context, false otherwise. */ bool zt_available = false; + + /* True if we have a GCS entry in the signal context, false otherwise. */ + bool gcs_availabe = false; + /* The Guarded Control Stack Pointer Register. */ + uint64_t gcspr; + /* Flags indicating which GCS features are enabled for the thread. */ + uint64_t gcs_features_enabled; }; /* Read an aarch64_ctx, returning the magic value, and setting *SIZE to the @@ -530,6 +543,39 @@ aarch64_linux_read_signal_frame_info (const frame_info_ptr &this_frame, section += size; break; } + case AARCH64_GCS_MAGIC: + { + gdb_byte buf[8]; + + /* Extract the GCSPR. */ + if (target_read_memory (section + AARCH64_GCS_CONTEXT_GCSPR_OFFSET, + buf, 8) != 0) + { + warning (_("Failed to read the GCS pointer from the GCS signal" + " frame context.")); + section += size; + break; + } + + signal_frame.gcspr = extract_unsigned_integer (buf, byte_order); + + /* Extract the features_enabled field. */ + if (target_read_memory (section + + AARCH64_GCS_CONTEXT_FEATURES_ENABLED_OFFSET, + buf, sizeof (buf)) != 0) + { + warning (_("Failed to read the enabled features from the GCS" + " signal frame context.")); + section += size; + break; + } + + signal_frame.gcs_features_enabled + = extract_unsigned_integer (buf, byte_order); + signal_frame.gcs_availabe = true; + section += size; + break; + } case AARCH64_EXTRA_MAGIC: { /* Extra is always the last valid section in reserved and points to @@ -704,6 +750,19 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self, + AARCH64_TPIDR2_CONTEXT_TPIDR2_OFFSET); } + /* Restore the GCS registers, if the target supports it and if there is + an entry for them. */ + if (signal_frame.gcs_availabe && tdep->has_gcs_linux ()) + { + /* Restore GCSPR. */ + trad_frame_set_reg_value (this_cache, tdep->gcs_reg_base, + signal_frame.gcspr); + /* Restore gcs_features_enabled. */ + trad_frame_set_reg_value (this_cache, tdep->gcs_linux_reg_base, + signal_frame.gcs_features_enabled); + /* gcs_features_locked isn't present in the GCS signal context. */ + } + trad_frame_set_id (this_cache, frame_id_build (signal_frame.sp, func)); } @@ -2487,17 +2546,18 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch, { aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch); - if (!tdep->has_mte () || siggnal != GDB_SIGNAL_SEGV) + if (!(tdep->has_mte () || tdep->has_gcs ()) || siggnal != GDB_SIGNAL_SEGV) return; CORE_ADDR fault_addr = 0; - long si_code = 0; + long si_code = 0, si_errno = 0; try { /* 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"); fault_addr = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr"); @@ -2508,13 +2568,18 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch, return; } - /* If this is not a memory tag violation, just return. */ - if (si_code != SEGV_MTEAERR && si_code != SEGV_MTESERR) + const char *meaning; + + if (si_code == SEGV_MTEAERR || si_code == SEGV_MTESERR) + meaning = _("Memory tag violation"); + else if (si_code == SEGV_CPERR && si_errno == 0) + meaning = _("Guarded Control Stack error"); + else return; uiout->text ("\n"); - uiout->field_string ("sigcode-meaning", _("Memory tag violation")); + uiout->field_string ("sigcode-meaning", meaning); /* For synchronous faults, show additional information. */ if (si_code == SEGV_MTESERR) @@ -2540,7 +2605,7 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch, uiout->field_string ("logical-tag", hex_string (ltag)); } } - else + else if (si_code != SEGV_CPERR) { uiout->text ("\n"); uiout->text (_("Fault address unavailable")); @@ -2844,9 +2909,6 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Register a hook for checking if an address is tagged or not. */ set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p); - set_gdbarch_report_signal_info (gdbarch, - aarch64_linux_report_signal_info); - /* Core file helpers. */ /* Core file helper to create a memory tag section for a particular @@ -2863,6 +2925,9 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) aarch64_linux_decode_memtag_section); } + if (tdep->has_mte () || tdep->has_gcs ()) + set_gdbarch_report_signal_info (gdbarch, aarch64_linux_report_signal_info); + /* Initialize the aarch64_linux_record_tdep. */ /* These values are the size of the type that will be used in a system call. They are obtained from Linux Kernel source. */ diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h index 2e7022d..a3b331f 100644 --- a/gdb/linux-tdep.h +++ b/gdb/linux-tdep.h @@ -27,6 +27,10 @@ struct inferior; struct regcache; +#ifndef SEGV_CPERR +#define SEGV_CPERR 10 /* Control protection error. */ +#endif + /* Enum used to define the extra fields of the siginfo type used by an architecture. */ enum linux_siginfo_extra_field_values diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp index e75e639..2261ac8 100644 --- a/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp @@ -43,7 +43,8 @@ proc check_core_file {core_filename saved_gcspr} { if [gdb_test "core $core_filename" \ [multi_line \ "Core was generated by .*\\." \ - "Program terminated with signal SIGSEGV, Segmentation fault\\." \ + "Program terminated with signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ "#0 function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \ "$decimal.*__asm__ volatile \\(\"ret\\\\n\"\\);"] \ "load core file"] { @@ -90,7 +91,8 @@ gdb_test "continue" \ [multi_line \ "Continuing\\." \ "($hex\r\n)?" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ "function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \ {.*__asm__ volatile \("ret\\n"\);}] \ "continue to SIGSEGV" diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs.exp b/gdb/testsuite/gdb.arch/aarch64-gcs.exp index 3309059..ad73b41 100644 --- a/gdb/testsuite/gdb.arch/aarch64-gcs.exp +++ b/gdb/testsuite/gdb.arch/aarch64-gcs.exp @@ -51,17 +51,22 @@ gdb_test "continue" \ ".*\r\nBreakpoint \[0-9\]+, handler \\(sig=10\\) at .*aarch64-gcs.c.*handler_gcspr = get_gcspr \\(\\);" \ "continue to signal handler" +gdb_test_no_output "set \$gcspr_in_handler = \$gcspr" \ + "save gcspr value in handler for later" # Select the frame above the <signal handler called> frame, which makes GDB # unwind the gcspr from the signal frame GCS context. gdb_test "frame 2" "#2 ($hex in )?\\S+ \\(.*\\) (at|from) \\S+.*" \ "reached frame 2" gdb_test "print \$gcspr" ". = \\(void \\*\\) $hex" "gcspr in frame level 2" +gdb_test "print \$gcspr == \$gcspr_in_handler + 8" ". = 1" \ + "gcspr unwound from signal context is correct" gdb_test "continue" \ [multi_line \ "Continuing\\." \ "" \ - "Program received signal SIGSEGV, Segmentation fault\\." \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ "normal_function2 \\(\\) at .*aarch64-gcs.c:$decimal" \ "${decimal}\\s+__asm__ volatile \\(\"ret\\\\n\"\\);"] \ "continue to SIGSEGV" @@ -80,6 +85,9 @@ if ![runto normal_function0] { gdb_test_no_output "set \$gcspr = 0xbadc0ffee" "set bogus gcspr value" # Continue to make sure that the value was actually written to the register. +# The SIGSEGV isn't a GCS error because the problem isn't that the GCS entry +# doesn't match the return address, but rather that that GCSPR is pointing +# to an invalid address. gdb_test "continue" \ [multi_line \ "Continuing\\." \ |