]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
GDB: aarch64-linux: GCS support in Linux signals
authorThiago Jung Bauermann <thiago.bauermann@linaro.org>
Fri, 2 May 2025 00:24:01 +0000 (21:24 -0300)
committerThiago Jung Bauermann <thiago.bauermann@linaro.org>
Wed, 18 Jun 2025 03:21:21 +0000 (00:21 -0300)
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.

gdb/aarch64-linux-tdep.c
gdb/linux-tdep.h
gdb/testsuite/gdb.arch/aarch64-gcs-core.exp
gdb/testsuite/gdb.arch/aarch64-gcs.exp

index ee3f7bfd9928e4648d2d6e9f73b8b68b0305045f..6bd9dfc47d817890e04cfc7d7f4ae528f065180b 100644 (file)
 #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
    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
 {
@@ -246,6 +252,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
@@ -526,6 +539,39 @@ aarch64_linux_read_signal_frame_info (const frame_info_ptr &this_frame,
            signal_frame.zt_section = section;
            signal_frame.zt_available = true;
 
+           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;
          }
@@ -703,6 +749,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));
 }
 
@@ -2486,17 +2545,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");
@@ -2507,13 +2567,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)
@@ -2539,7 +2604,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"));
@@ -2841,9 +2906,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
@@ -2860,6 +2922,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.  */
index 7083635b976c9a33334ce7e0e19189d8bd77706d..0bee4b34845d6e9cc0aaa643f11b3f017da90777 100644 (file)
 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
index ef7e507407a4c032da58b1acaf1bc7c5add30d1f..581abf2e06e9a6c252d696ec45a77d7ec8ce0ecb 100644 (file)
@@ -37,7 +37,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"
@@ -67,7 +68,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"] {
index ea70cc6ef2e99e42cb5c9835fcc9e1b125ea9150..907dcb43e78b0463cd161e2a38fcf27ed46584d1 100644 (file)
@@ -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"