[1/2] aarch64: Convert GCS policy states to an enum

Message ID 20260327174434.2852296-1-adhemerval.zanella@linaro.org (mailing list archive)
State Changes Requested
Headers
Series [1/2] aarch64: Convert GCS policy states to an enum |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Adhemerval Zanella Netto March 27, 2026, 5:44 p.m. UTC
  This patch replaces the raw preprocessor macros used for GCS policies
with a strictly typed 'aarch64_gcs_mode' enum.

A new header, 'aarch64/dl-gcs.h', is introduced to centralize the GCS
mode definitions and enforce internal mode typing.

The '_dl_aarch64_gcs' variable is updated to the new 32-bit enum type,
and dl-start.S is adjusted to load and test 32-bit w registers 64-bit
ones.

Checked on aarch64-linux-gnu.
---
 sysdeps/aarch64/dl-gcs.c                      | 25 +++---------
 sysdeps/aarch64/dl-gcs.h                      | 38 +++++++++++++++++++
 sysdeps/aarch64/dl-start.S                    |  6 +--
 sysdeps/aarch64/ldsodefs.h                    |  1 +
 .../unix/sysv/linux/aarch64/dl-procruntime.c  |  2 +-
 sysdeps/unix/sysv/linux/aarch64/libc-start.h  |  2 +-
 6 files changed, 50 insertions(+), 24 deletions(-)
 create mode 100644 sysdeps/aarch64/dl-gcs.h
  

Comments

Yury Khrustalev March 30, 2026, 9:24 a.m. UTC | #1
Hello Adhemerval,

On Fri, Mar 27, 2026 at 02:44:23PM -0300, Adhemerval Zanella wrote:
> This patch replaces the raw preprocessor macros used for GCS policies
> with a strictly typed 'aarch64_gcs_mode' enum.
> 
> A new header, 'aarch64/dl-gcs.h', is introduced to centralize the GCS
> mode definitions and enforce internal mode typing.
> 
> The '_dl_aarch64_gcs' variable is updated to the new 32-bit enum type,
> and dl-start.S is adjusted to load and test 32-bit w registers 64-bit
> ones.

Thanks, this patch makes code more readable, some comments below.

> 
> Checked on aarch64-linux-gnu.
>
> ...
> 
> diff --git a/sysdeps/aarch64/dl-gcs.c b/sysdeps/aarch64/dl-gcs.c
> index 4961ad75eb..213ed01382 100644
> --- a/sysdeps/aarch64/dl-gcs.c
> +++ b/sysdeps/aarch64/dl-gcs.c
> @@ -18,18 +18,6 @@
>  #include <unistd.h>
>  #include <ldsodefs.h>
>  
> -/* GCS is disabled.  */
> -#define GCS_POLICY_DISABLED 0
> -
> -/* Enable GCS, abort if unmarked binary is found.  */
> -#define GCS_POLICY_ENFORCED 1
> -
> -/* Optionally enable GCS if all startup dependencies are marked.  */
> -#define GCS_POLICY_OPTIONAL 2
> -
> -/* Override binary marking and always enabled GCS.  */
> -#define GCS_POLICY_OVERRIDE 3
> -

OK

>  static void
>  fail (struct link_map *l, const char *program)
>  {
> @@ -96,7 +84,7 @@ check_gcs (struct link_map *l, const char *program, bool enforced,
>    /* Binary is not marked but GSC is optional: disable GCS.  */
>    else
>      {
> -      GL(dl_aarch64_gcs) = 0;
> +      GL(dl_aarch64_gcs) = AARCH64_GCS_POLICY_DISABLED;

OK

>        return false;
>      }
>    __builtin_unreachable ();
> @@ -124,16 +112,15 @@ check_gcs_depends (struct link_map *l, const char *program, bool enforced,
>  void
>  _dl_gcs_check (struct link_map *l, const char *program, int dlopen_mode)
>  {
> -  unsigned long policy = GL (dl_aarch64_gcs);
> -  switch (policy)
> +  switch (GL(dl_aarch64_gcs))
>      {
> -    case GCS_POLICY_DISABLED:
> -    case GCS_POLICY_OVERRIDE:
> +    case AARCH64_GCS_POLICY_DISABLED:
> +    case AARCH64_GCS_POLICY_OVERRIDE:
>        return;
> -    case GCS_POLICY_ENFORCED:
> +    case AARCH64_GCS_POLICY_ENFORCED:
>        check_gcs_depends (l, program, true, dlopen_mode);
>        return;
> -    case GCS_POLICY_OPTIONAL:
> +    case AARCH64_GCS_POLICY_OPTIONAL:
>        check_gcs_depends (l, program, false, dlopen_mode);
>        return;
>      default:

OK

> diff --git a/sysdeps/aarch64/dl-gcs.h b/sysdeps/aarch64/dl-gcs.h
> new file mode 100644
> index 0000000000..bee3c94432
> --- /dev/null
> +++ b/sysdeps/aarch64/dl-gcs.h
>
> ...
>
> +#include <verify.h>
> +
> +typedef enum
> +{
> +  /* GCS is disabled.  */
> +  AARCH64_GCS_POLICY_DISABLED = 0,
> +  /* Enable GCS, abort if unmarked binary is found.  */
> +  AARCH64_GCS_POLICY_ENFORCED = 1,
> +  /* Optionally enable GCS if all startup dependencies are marked.  */
> +  AARCH64_GCS_POLICY_OPTIONAL = 2,
> +  /* Override binary marking and always enabled GCS.  */
> +  AARCH64_GCS_POLICY_OVERRIDE = 3
> +} aarch64_gcs_mode;

OK

> +
> +/* dl-start.S assumes aarch64_gcs_mode is representable as uint32_t.  */
> +verify (sizeof (aarch64_gcs_mode) == 4);

Can it ever be something else?

> diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S
> index c278485cd3..78b30b709e 100644
> --- a/sysdeps/aarch64/dl-start.S
> +++ b/sysdeps/aarch64/dl-start.S
> @@ -35,8 +35,8 @@ ENTRY (_start)
>  	/* Use GL(dl_aarch64_gcs) to set the shadow stack status.  */
>  	adrp	x16, _rtld_local
>  	add	x16, x16, :lo12:_rtld_local
> -	ldr	x22, [x16, GL_DL_AARCH64_GCS_OFFSET]
> -	cbz	x22, L(skip_gcs_enable)
> +	ldr	w22, [x16, GL_DL_AARCH64_GCS_OFFSET]
> +	cbz	w22, L(skip_gcs_enable)

OK

>  
>  	/* Enable GCS before user code runs.  Note that IFUNC resolvers and
>  	   LD_AUDIT hooks may run before, but should not create threads.  */
> @@ -53,7 +53,7 @@ ENTRY (_start)
>  	cbnz	w0, L(failed_gcs_enable)
>  	/* Check if we need to lock GCS features.  */
>  	/* If the aarch64_gcs tunable is either 0 or 2 do not lock GCS.  */
> -	tst	x22, #-3
> +	tst	w22, #-3

OK

>  	beq	L(skip_gcs_enable)
>  	mov	x0, PR_LOCK_SHADOW_STACK_STATUS
>  	/* Lock everything including future operations.  */
> diff --git a/sysdeps/aarch64/ldsodefs.h b/sysdeps/aarch64/ldsodefs.h
> index 03b35ce20a..d29569593a 100644
> --- a/sysdeps/aarch64/ldsodefs.h
> +++ b/sysdeps/aarch64/ldsodefs.h
> @@ -21,6 +21,7 @@
>  
>  #include <elf.h>
>  #include <cpu-features.h>
> +#include <dl-gcs.h>
>  
>  struct La_aarch64_regs;
>  struct La_aarch64_retval;

OK

> diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> index 1f3b58d0fc..d49bb6cf5d 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> @@ -24,7 +24,7 @@
>  # if !defined PROCINFO_DECL && defined SHARED
>    ._dl_aarch64_gcs
>  # else
> -PROCINFO_CLASS unsigned long _dl_aarch64_gcs
> +PROCINFO_CLASS aarch64_gcs_mode _dl_aarch64_gcs

Is this change not back-portable to previous releases? If so, I'd like
to avoid making it.

>  # endif
>  # ifndef PROCINFO_DECL
>  = 0
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc-start.h b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
> index 4ccd13741b..293c8a90b2 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc-start.h
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
> @@ -54,7 +54,7 @@ aarch64_libc_setup_tls (void)
>  
>    _rtld_main_check (main_map, _dl_argv[0]);
>  
> -  uint64_t gcs = GL (dl_aarch64_gcs);
> +  aarch64_gcs_mode gcs = GL(dl_aarch64_gcs);

OK

>    if (gcs != GCS_POLICY_DISABLED)

Should this be the new enum instead of old macro GCS_POLICY_DISABLED?

>      {
>        int ret;
> -- 
> 2.43.0
>
  
Adhemerval Zanella Netto March 30, 2026, 6:26 p.m. UTC | #2
On 30/03/26 06:24, Yury Khrustalev wrote:
> Hello Adhemerval,
> 
> On Fri, Mar 27, 2026 at 02:44:23PM -0300, Adhemerval Zanella wrote:
>> This patch replaces the raw preprocessor macros used for GCS policies
>> with a strictly typed 'aarch64_gcs_mode' enum.
>>
>> A new header, 'aarch64/dl-gcs.h', is introduced to centralize the GCS
>> mode definitions and enforce internal mode typing.
>>
>> The '_dl_aarch64_gcs' variable is updated to the new 32-bit enum type,
>> and dl-start.S is adjusted to load and test 32-bit w registers 64-bit
>> ones.
> 
> Thanks, this patch makes code more readable, some comments below.
> 
>>
>> Checked on aarch64-linux-gnu.
>>
>> ...
>>
>> diff --git a/sysdeps/aarch64/dl-gcs.c b/sysdeps/aarch64/dl-gcs.c
>> index 4961ad75eb..213ed01382 100644
>> --- a/sysdeps/aarch64/dl-gcs.c
>> +++ b/sysdeps/aarch64/dl-gcs.c
>> @@ -18,18 +18,6 @@
>>  #include <unistd.h>
>>  #include <ldsodefs.h>
>>  
>> -/* GCS is disabled.  */
>> -#define GCS_POLICY_DISABLED 0
>> -
>> -/* Enable GCS, abort if unmarked binary is found.  */
>> -#define GCS_POLICY_ENFORCED 1
>> -
>> -/* Optionally enable GCS if all startup dependencies are marked.  */
>> -#define GCS_POLICY_OPTIONAL 2
>> -
>> -/* Override binary marking and always enabled GCS.  */
>> -#define GCS_POLICY_OVERRIDE 3
>> -
> 
> OK
> 
>>  static void
>>  fail (struct link_map *l, const char *program)
>>  {
>> @@ -96,7 +84,7 @@ check_gcs (struct link_map *l, const char *program, bool enforced,
>>    /* Binary is not marked but GSC is optional: disable GCS.  */
>>    else
>>      {
>> -      GL(dl_aarch64_gcs) = 0;
>> +      GL(dl_aarch64_gcs) = AARCH64_GCS_POLICY_DISABLED;
> 
> OK
> 
>>        return false;
>>      }
>>    __builtin_unreachable ();
>> @@ -124,16 +112,15 @@ check_gcs_depends (struct link_map *l, const char *program, bool enforced,
>>  void
>>  _dl_gcs_check (struct link_map *l, const char *program, int dlopen_mode)
>>  {
>> -  unsigned long policy = GL (dl_aarch64_gcs);
>> -  switch (policy)
>> +  switch (GL(dl_aarch64_gcs))
>>      {
>> -    case GCS_POLICY_DISABLED:
>> -    case GCS_POLICY_OVERRIDE:
>> +    case AARCH64_GCS_POLICY_DISABLED:
>> +    case AARCH64_GCS_POLICY_OVERRIDE:
>>        return;
>> -    case GCS_POLICY_ENFORCED:
>> +    case AARCH64_GCS_POLICY_ENFORCED:
>>        check_gcs_depends (l, program, true, dlopen_mode);
>>        return;
>> -    case GCS_POLICY_OPTIONAL:
>> +    case AARCH64_GCS_POLICY_OPTIONAL:
>>        check_gcs_depends (l, program, false, dlopen_mode);
>>        return;
>>      default:
> 
> OK
> 
>> diff --git a/sysdeps/aarch64/dl-gcs.h b/sysdeps/aarch64/dl-gcs.h
>> new file mode 100644
>> index 0000000000..bee3c94432
>> --- /dev/null
>> +++ b/sysdeps/aarch64/dl-gcs.h
>>
>> ...
>>
>> +#include <verify.h>
>> +
>> +typedef enum
>> +{
>> +  /* GCS is disabled.  */
>> +  AARCH64_GCS_POLICY_DISABLED = 0,
>> +  /* Enable GCS, abort if unmarked binary is found.  */
>> +  AARCH64_GCS_POLICY_ENFORCED = 1,
>> +  /* Optionally enable GCS if all startup dependencies are marked.  */
>> +  AARCH64_GCS_POLICY_OPTIONAL = 2,
>> +  /* Override binary marking and always enabled GCS.  */
>> +  AARCH64_GCS_POLICY_OVERRIDE = 3
>> +} aarch64_gcs_mode;
> 
> OK
> 
>> +
>> +/* dl-start.S assumes aarch64_gcs_mode is representable as uint32_t.  */
>> +verify (sizeof (aarch64_gcs_mode) == 4);
> 
> Can it ever be something else?
> 
>> diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S
>> index c278485cd3..78b30b709e 100644
>> --- a/sysdeps/aarch64/dl-start.S
>> +++ b/sysdeps/aarch64/dl-start.S
>> @@ -35,8 +35,8 @@ ENTRY (_start)
>>  	/* Use GL(dl_aarch64_gcs) to set the shadow stack status.  */
>>  	adrp	x16, _rtld_local
>>  	add	x16, x16, :lo12:_rtld_local
>> -	ldr	x22, [x16, GL_DL_AARCH64_GCS_OFFSET]
>> -	cbz	x22, L(skip_gcs_enable)
>> +	ldr	w22, [x16, GL_DL_AARCH64_GCS_OFFSET]
>> +	cbz	w22, L(skip_gcs_enable)
> 
> OK
> 
>>  
>>  	/* Enable GCS before user code runs.  Note that IFUNC resolvers and
>>  	   LD_AUDIT hooks may run before, but should not create threads.  */
>> @@ -53,7 +53,7 @@ ENTRY (_start)
>>  	cbnz	w0, L(failed_gcs_enable)
>>  	/* Check if we need to lock GCS features.  */
>>  	/* If the aarch64_gcs tunable is either 0 or 2 do not lock GCS.  */
>> -	tst	x22, #-3
>> +	tst	w22, #-3
> 
> OK
> 
>>  	beq	L(skip_gcs_enable)
>>  	mov	x0, PR_LOCK_SHADOW_STACK_STATUS
>>  	/* Lock everything including future operations.  */
>> diff --git a/sysdeps/aarch64/ldsodefs.h b/sysdeps/aarch64/ldsodefs.h
>> index 03b35ce20a..d29569593a 100644
>> --- a/sysdeps/aarch64/ldsodefs.h
>> +++ b/sysdeps/aarch64/ldsodefs.h
>> @@ -21,6 +21,7 @@
>>  
>>  #include <elf.h>
>>  #include <cpu-features.h>
>> +#include <dl-gcs.h>
>>  
>>  struct La_aarch64_regs;
>>  struct La_aarch64_retval;
> 
> OK
> 
>> diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
>> index 1f3b58d0fc..d49bb6cf5d 100644
>> --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
>> +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
>> @@ -24,7 +24,7 @@
>>  # if !defined PROCINFO_DECL && defined SHARED
>>    ._dl_aarch64_gcs
>>  # else
>> -PROCINFO_CLASS unsigned long _dl_aarch64_gcs
>> +PROCINFO_CLASS aarch64_gcs_mode _dl_aarch64_gcs
> 
> Is this change not back-portable to previous releases? If so, I'd like
> to avoid making it.

Do we intend do backport this fix? This does not seems a good candidate: it does
not fix any inherent and I don't think this would be a requisite for other
fixes.

> 
>>  # endif
>>  # ifndef PROCINFO_DECL
>>  = 0
>> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc-start.h b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
>> index 4ccd13741b..293c8a90b2 100644
>> --- a/sysdeps/unix/sysv/linux/aarch64/libc-start.h
>> +++ b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
>> @@ -54,7 +54,7 @@ aarch64_libc_setup_tls (void)
>>  
>>    _rtld_main_check (main_map, _dl_argv[0]);
>>  
>> -  uint64_t gcs = GL (dl_aarch64_gcs);
>> +  aarch64_gcs_mode gcs = GL(dl_aarch64_gcs);
> 
> OK
> 
>>    if (gcs != GCS_POLICY_DISABLED)
> 
> Should this be the new enum instead of old macro GCS_POLICY_DISABLED?
> 
>>      {
>>        int ret;
>> -- 
>> 2.43.0
>>
  
Yury Khrustalev March 31, 2026, 8:38 a.m. UTC | #3
On Mon, Mar 30, 2026 at 03:26:23PM -0300, Adhemerval Zanella Netto wrote:
>
> ...
>
> >> diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> >> index 1f3b58d0fc..d49bb6cf5d 100644
> >> --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> >> +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> >> @@ -24,7 +24,7 @@
> >>  # if !defined PROCINFO_DECL && defined SHARED
> >>    ._dl_aarch64_gcs
> >>  # else
> >> -PROCINFO_CLASS unsigned long _dl_aarch64_gcs
> >> +PROCINFO_CLASS aarch64_gcs_mode _dl_aarch64_gcs
> > 
> > Is this change not back-portable to previous releases? If so, I'd like
> > to avoid making it.
> 
> Do we intend do backport this fix?

Maybe not this fix but some other fix that depends on this code.

> This does not seems a good candidate: it does
> not fix any inherent and I don't think this would be a requisite for other
> fixes.

Exactly, so I don't really see any reason to make this change at all.
  
Adhemerval Zanella Netto March 31, 2026, 11:52 a.m. UTC | #4
On 31/03/26 05:38, Yury Khrustalev wrote:
> On Mon, Mar 30, 2026 at 03:26:23PM -0300, Adhemerval Zanella Netto wrote:
>>
>> ...
>>
>>>> diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
>>>> index 1f3b58d0fc..d49bb6cf5d 100644
>>>> --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
>>>> +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
>>>> @@ -24,7 +24,7 @@
>>>>  # if !defined PROCINFO_DECL && defined SHARED
>>>>    ._dl_aarch64_gcs
>>>>  # else
>>>> -PROCINFO_CLASS unsigned long _dl_aarch64_gcs
>>>> +PROCINFO_CLASS aarch64_gcs_mode _dl_aarch64_gcs
>>>
>>> Is this change not back-portable to previous releases? If so, I'd like
>>> to avoid making it.
>>
>> Do we intend do backport this fix?
> 
> Maybe not this fix but some other fix that depends on this code.

Which is not the case now, so there is no blocker either.

> 
>> This does not seems a good candidate: it does
>> not fix any inherent and I don't think this would be a requisite for other
>> fixes.
> 
> Exactly, so I don't really see any reason to make this change at all.
> 

This is an internal type improvement, and I am not understanding why you are
blocking this change.
  
Yury Khrustalev March 31, 2026, 12:23 p.m. UTC | #5
On Tue, Mar 31, 2026 at 08:52:26AM -0300, Adhemerval Zanella Netto wrote:
> 
> 
> On 31/03/26 05:38, Yury Khrustalev wrote:
> > On Mon, Mar 30, 2026 at 03:26:23PM -0300, Adhemerval Zanella Netto wrote:
> >>
> >> ...
> >>
> >>>> diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> >>>> index 1f3b58d0fc..d49bb6cf5d 100644
> >>>> --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> >>>> +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> >>>> @@ -24,7 +24,7 @@
> >>>>  # if !defined PROCINFO_DECL && defined SHARED
> >>>>    ._dl_aarch64_gcs
> >>>>  # else
> >>>> -PROCINFO_CLASS unsigned long _dl_aarch64_gcs
> >>>> +PROCINFO_CLASS aarch64_gcs_mode _dl_aarch64_gcs
> >>>
> >>> Is this change not back-portable to previous releases? If so, I'd like
> >>> to avoid making it.
> >>
> >> Do we intend do backport this fix?
> > 
> > Maybe not this fix but some other fix that depends on this code.
> 
> Which is not the case now, so there is no blocker either.

I don't want to have to fix it back later. Is there a reason why this has to
become 32-bit instead of 64-bit?

> 
> > 
> >> This does not seems a good candidate: it does
> >> not fix any inherent and I don't think this would be a requisite for other
> >> fixes.
> > 
> > Exactly, so I don't really see any reason to make this change at all.
> > 
> 
> This is an internal type improvement, and I am not understanding why you are
> blocking this change.

Like I said previously, this patch is a good improvement. I just
requested several changes to be made in v2. Regarding this change:

  -PROCINFO_CLASS unsigned long _dl_aarch64_gcs
  +PROCINFO_CLASS aarch64_gcs_mode _dl_aarch64_gcs

(and the relevant x-reg --> w-reg change in assembly), I think it's not
necessary and we shouldn't do it (unless I'm missing something here).

Otherwise, this patch seems OK, pending the requested changes, I'm not
blocking anything.

Thanks,
Yury
  

Patch

diff --git a/sysdeps/aarch64/dl-gcs.c b/sysdeps/aarch64/dl-gcs.c
index 4961ad75eb..213ed01382 100644
--- a/sysdeps/aarch64/dl-gcs.c
+++ b/sysdeps/aarch64/dl-gcs.c
@@ -18,18 +18,6 @@ 
 #include <unistd.h>
 #include <ldsodefs.h>
 
-/* GCS is disabled.  */
-#define GCS_POLICY_DISABLED 0
-
-/* Enable GCS, abort if unmarked binary is found.  */
-#define GCS_POLICY_ENFORCED 1
-
-/* Optionally enable GCS if all startup dependencies are marked.  */
-#define GCS_POLICY_OPTIONAL 2
-
-/* Override binary marking and always enabled GCS.  */
-#define GCS_POLICY_OVERRIDE 3
-
 static void
 fail (struct link_map *l, const char *program)
 {
@@ -96,7 +84,7 @@  check_gcs (struct link_map *l, const char *program, bool enforced,
   /* Binary is not marked but GSC is optional: disable GCS.  */
   else
     {
-      GL(dl_aarch64_gcs) = 0;
+      GL(dl_aarch64_gcs) = AARCH64_GCS_POLICY_DISABLED;
       return false;
     }
   __builtin_unreachable ();
@@ -124,16 +112,15 @@  check_gcs_depends (struct link_map *l, const char *program, bool enforced,
 void
 _dl_gcs_check (struct link_map *l, const char *program, int dlopen_mode)
 {
-  unsigned long policy = GL (dl_aarch64_gcs);
-  switch (policy)
+  switch (GL(dl_aarch64_gcs))
     {
-    case GCS_POLICY_DISABLED:
-    case GCS_POLICY_OVERRIDE:
+    case AARCH64_GCS_POLICY_DISABLED:
+    case AARCH64_GCS_POLICY_OVERRIDE:
       return;
-    case GCS_POLICY_ENFORCED:
+    case AARCH64_GCS_POLICY_ENFORCED:
       check_gcs_depends (l, program, true, dlopen_mode);
       return;
-    case GCS_POLICY_OPTIONAL:
+    case AARCH64_GCS_POLICY_OPTIONAL:
       check_gcs_depends (l, program, false, dlopen_mode);
       return;
     default:
diff --git a/sysdeps/aarch64/dl-gcs.h b/sysdeps/aarch64/dl-gcs.h
new file mode 100644
index 0000000000..bee3c94432
--- /dev/null
+++ b/sysdeps/aarch64/dl-gcs.h
@@ -0,0 +1,38 @@ 
+/* Internal AArch64 GCS definitions.
+   Copyright (C) 2026 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _DL_GCS_H
+#define _DL_GCS_H
+
+#include <verify.h>
+
+typedef enum
+{
+  /* GCS is disabled.  */
+  AARCH64_GCS_POLICY_DISABLED = 0,
+  /* Enable GCS, abort if unmarked binary is found.  */
+  AARCH64_GCS_POLICY_ENFORCED = 1,
+  /* Optionally enable GCS if all startup dependencies are marked.  */
+  AARCH64_GCS_POLICY_OPTIONAL = 2,
+  /* Override binary marking and always enabled GCS.  */
+  AARCH64_GCS_POLICY_OVERRIDE = 3
+} aarch64_gcs_mode;
+
+/* dl-start.S assumes aarch64_gcs_mode is representable as uint32_t.  */
+verify (sizeof (aarch64_gcs_mode) == 4);
+
+#endif
diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S
index c278485cd3..78b30b709e 100644
--- a/sysdeps/aarch64/dl-start.S
+++ b/sysdeps/aarch64/dl-start.S
@@ -35,8 +35,8 @@  ENTRY (_start)
 	/* Use GL(dl_aarch64_gcs) to set the shadow stack status.  */
 	adrp	x16, _rtld_local
 	add	x16, x16, :lo12:_rtld_local
-	ldr	x22, [x16, GL_DL_AARCH64_GCS_OFFSET]
-	cbz	x22, L(skip_gcs_enable)
+	ldr	w22, [x16, GL_DL_AARCH64_GCS_OFFSET]
+	cbz	w22, L(skip_gcs_enable)
 
 	/* Enable GCS before user code runs.  Note that IFUNC resolvers and
 	   LD_AUDIT hooks may run before, but should not create threads.  */
@@ -53,7 +53,7 @@  ENTRY (_start)
 	cbnz	w0, L(failed_gcs_enable)
 	/* Check if we need to lock GCS features.  */
 	/* If the aarch64_gcs tunable is either 0 or 2 do not lock GCS.  */
-	tst	x22, #-3
+	tst	w22, #-3
 	beq	L(skip_gcs_enable)
 	mov	x0, PR_LOCK_SHADOW_STACK_STATUS
 	/* Lock everything including future operations.  */
diff --git a/sysdeps/aarch64/ldsodefs.h b/sysdeps/aarch64/ldsodefs.h
index 03b35ce20a..d29569593a 100644
--- a/sysdeps/aarch64/ldsodefs.h
+++ b/sysdeps/aarch64/ldsodefs.h
@@ -21,6 +21,7 @@ 
 
 #include <elf.h>
 #include <cpu-features.h>
+#include <dl-gcs.h>
 
 struct La_aarch64_regs;
 struct La_aarch64_retval;
diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
index 1f3b58d0fc..d49bb6cf5d 100644
--- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
+++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
@@ -24,7 +24,7 @@ 
 # if !defined PROCINFO_DECL && defined SHARED
   ._dl_aarch64_gcs
 # else
-PROCINFO_CLASS unsigned long _dl_aarch64_gcs
+PROCINFO_CLASS aarch64_gcs_mode _dl_aarch64_gcs
 # endif
 # ifndef PROCINFO_DECL
 = 0
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc-start.h b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
index 4ccd13741b..293c8a90b2 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc-start.h
+++ b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
@@ -54,7 +54,7 @@  aarch64_libc_setup_tls (void)
 
   _rtld_main_check (main_map, _dl_argv[0]);
 
-  uint64_t gcs = GL (dl_aarch64_gcs);
+  aarch64_gcs_mode gcs = GL(dl_aarch64_gcs);
   if (gcs != GCS_POLICY_DISABLED)
     {
       int ret;