aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiago Jung Bauermann <thiago.bauermann@linaro.org>2025-05-01 21:24:01 -0300
committerThiago Jung Bauermann <thiago.bauermann@linaro.org>2025-08-29 18:35:58 -0300
commita5ae00c47eb678462c5d2ed9c3b31fc59bb4ba45 (patch)
treed4dd687cf978b9bf489e37cb94089cccb31eafb0
parentff62d39aa4999f44ac79360dee6357fc69d4e322 (diff)
downloadbinutils-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.c83
-rw-r--r--gdb/linux-tdep.h4
-rw-r--r--gdb/testsuite/gdb.arch/aarch64-gcs-core.exp6
-rw-r--r--gdb/testsuite/gdb.arch/aarch64-gcs.exp10
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\\." \