diff mbox series

[4/8] GDB: aarch64-linux: GCS support in Linux signals

Message ID 20250608010338.2234530-5-thiago.bauermann@linaro.org
State New
Headers show
Series AArch64 Guarded Control Stack support | expand

Commit Message

Thiago Jung Bauermann June 8, 2025, 1:03 a.m. UTC
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     | 83 ++++++++++++++++++++++++++++++++----
 gdb/arch/aarch64-gcs-linux.h |  4 ++
 gdb/doc/gdb.texinfo          |  8 ++++
 3 files changed, 86 insertions(+), 9 deletions(-)

Comments

Schimpe, Christina June 11, 2025, 11:29 a.m. UTC | #1
Hi Thiago, 

Please see my feedback below.

> -----Original Message-----
> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Sent: Sunday, June 8, 2025 3:03 AM
> To: gdb-patches@sourceware.org
> Subject: [PATCH 4/8] 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.
> ---
>  gdb/aarch64-linux-tdep.c     | 83 ++++++++++++++++++++++++++++++++----
>  gdb/arch/aarch64-gcs-linux.h |  4 ++
>  gdb/doc/gdb.texinfo          |  8 ++++
>  3 files changed, 86 insertions(+), 9 deletions(-)
> 
> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index
> ce213bb482b9..24fb151311c4 100644
> --- a/gdb/aarch64-linux-tdep.c
> +++ b/gdb/aarch64-linux-tdep.c
> @@ -165,6 +165,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
> @@ -206,6 +207,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  {
> @@ -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 GCSPR 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 ())
> +    {
> +      /* 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")); @@ -2838,9 +2903,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 @@ -
> 2857,6 +2919,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/arch/aarch64-gcs-linux.h b/gdb/arch/aarch64-gcs-linux.h index
> 9366caa7289a..853e748faf2d 100644
> --- a/gdb/arch/aarch64-gcs-linux.h
> +++ b/gdb/arch/aarch64-gcs-linux.h
> @@ -41,4 +41,8 @@ struct user_gcs
> 
>  #endif /* GCS_MAGIC */
> 
> +#ifndef SEGV_CPERR
> +#define SEGV_CPERR 10 /* Control protection error.  */ #endif

For CET shadow stack we see the same si code.
Would it make sense to make this part of the patch generic ? 

Christina 
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
Luis Machado June 13, 2025, 4:33 p.m. UTC | #2
On 6/8/25 02:03, Thiago Jung Bauermann wrote:
> 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     | 83 ++++++++++++++++++++++++++++++++----
>  gdb/arch/aarch64-gcs-linux.h |  4 ++
>  gdb/doc/gdb.texinfo          |  8 ++++
>  3 files changed, 86 insertions(+), 9 deletions(-)
> 
> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
> index ce213bb482b9..24fb151311c4 100644
> --- a/gdb/aarch64-linux-tdep.c
> +++ b/gdb/aarch64-linux-tdep.c
> @@ -165,6 +165,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
> @@ -206,6 +207,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
>  {
> @@ -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 GCSPR from the GCS signal frame"

Naming is hard, but would this read better?

"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 ())

This checks tdep->has_gcs(), but assumes Linux-specific gcs_feature_enabled is
available. I'm not sure if it would be possible to have gcs but not have the
Linux-specific gcs registers. But the logic seems like it would allow that.

Are there any concerns with that situation here?

> +    {
> +      /* 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"));
> @@ -2838,9 +2903,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
> @@ -2857,6 +2919,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/arch/aarch64-gcs-linux.h b/gdb/arch/aarch64-gcs-linux.h
> index 9366caa7289a..853e748faf2d 100644
> --- a/gdb/arch/aarch64-gcs-linux.h
> +++ b/gdb/arch/aarch64-gcs-linux.h
> @@ -41,4 +41,8 @@ struct user_gcs
>  
>  #endif /* GCS_MAGIC */
>  
> +#ifndef SEGV_CPERR
> +#define SEGV_CPERR 10 /* Control protection error.  */
> +#endif
> +
>  #endif /* ARCH_AARCH64_GCS_LINUX_H */
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 03f419e90436..1b35fa029884 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -26994,6 +26994,14 @@ information, see the
>  @uref{https://www.kernel.org/doc/html/latest/arch/arm64/gcs.html,ignored,
>  documentation} in the Linux kernel.
>  
> +To aid debugging, @value{GDBN} will note when SIGSEGV signals are generated
> +as a result of a Guarded Control Stack error:
> +
> +@smallexample
> +Program received signal SIGSEGV, Segmentation fault
> +Guarded Control Stack error.
> +@end smallexample
> +
>  @node x86
>  @subsection x86
>
Thiago Jung Bauermann June 14, 2025, 2:03 a.m. UTC | #3
Hello Christina,

"Schimpe, Christina" <christina.schimpe@intel.com> writes:

> Please see my feedback below.

Thanks!

>> -----Original Message-----
>> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> Sent: Sunday, June 8, 2025 3:03 AM
>> To: gdb-patches@sourceware.org
>> Subject: [PATCH 4/8] GDB: aarch64-linux: GCS support in Linux signals
>> 
>> a/gdb/arch/aarch64-gcs-linux.h b/gdb/arch/aarch64-gcs-linux.h index
>> 9366caa7289a..853e748faf2d 100644
>> --- a/gdb/arch/aarch64-gcs-linux.h
>> +++ b/gdb/arch/aarch64-gcs-linux.h
>> @@ -41,4 +41,8 @@ struct user_gcs
>> 
>>  #endif /* GCS_MAGIC */
>> 
>> +#ifndef SEGV_CPERR
>> +#define SEGV_CPERR 10 /* Control protection error.  */ #endif
>
> For CET shadow stack we see the same si code.
> Would it make sense to make this part of the patch generic ? 

Indeed, I hadn't noticed. For v2 I moved the definition to
gdb/linux-tdep.h.
Thiago Jung Bauermann June 14, 2025, 3:38 a.m. UTC | #4
Luis Machado <luis.machado@arm.com> writes:

> On 6/8/25 02:03, Thiago Jung Bauermann wrote:
>> @@ -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 GCSPR from the GCS signal frame"
>
> Naming is hard, but would this read better?
>
> "Failed to read the GCS pointer from the GCS signal frame"

I agree. I adopted your wording.

>> +			   " 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 ())
>
> This checks tdep->has_gcs(), but assumes Linux-specific gcs_feature_enabled is
> available. I'm not sure if it would be possible to have gcs but not have the
> Linux-specific gcs registers. But the logic seems like it would allow that.
>
> Are there any concerns with that situation here?

Christina had a similar concern. Since this patch series only supports
GCS in Linux userspace programs, it shouldn't happen in GDB that a
target description has org.gnu.gdb.aarch64.gcs.linux but not
org.gnu.gdb.aarch64.gcs or vice-versa. The patches I sent already check
that if ….gcs.linux is present then ….gcs needs to be as well. For the
other way around, for v2 I'm adding the following check to
aarch64_linux_init_abi:

  if (tdep->has_gcs () && !tdep->has_gcs_linux ())
    error (_("Incomplete GCS support in the target: missing Linux feature"));

In addition, I'm changing the if condition above to check for
tdep->has_gcs_linux () instead of tdep->has_gcs ().

>> +    {
>> +      /* 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));
>>  }
>>
diff mbox series

Patch

diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index ce213bb482b9..24fb151311c4 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -165,6 +165,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
@@ -206,6 +207,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
 {
@@ -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 GCSPR 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 ())
+    {
+      /* 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"));
@@ -2838,9 +2903,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
@@ -2857,6 +2919,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/arch/aarch64-gcs-linux.h b/gdb/arch/aarch64-gcs-linux.h
index 9366caa7289a..853e748faf2d 100644
--- a/gdb/arch/aarch64-gcs-linux.h
+++ b/gdb/arch/aarch64-gcs-linux.h
@@ -41,4 +41,8 @@  struct user_gcs
 
 #endif /* GCS_MAGIC */
 
+#ifndef SEGV_CPERR
+#define SEGV_CPERR 10 /* Control protection error.  */
+#endif
+
 #endif /* ARCH_AARCH64_GCS_LINUX_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 03f419e90436..1b35fa029884 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -26994,6 +26994,14 @@  information, see the
 @uref{https://www.kernel.org/doc/html/latest/arch/arm64/gcs.html,ignored,
 documentation} in the Linux kernel.
 
+To aid debugging, @value{GDBN} will note when SIGSEGV signals are generated
+as a result of a Guarded Control Stack error:
+
+@smallexample
+Program received signal SIGSEGV, Segmentation fault
+Guarded Control Stack error.
+@end smallexample
+
 @node x86
 @subsection x86