[v4] aarch64: Add support for GCS to AArch64 linker

Message ID 20241014101743.3222246-1-yury.khrustalev@arm.com
State Changes Requested
Headers
Series [v4] aarch64: Add support for GCS to AArch64 linker |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Yury Khrustalev Oct. 14, 2024, 10:17 a.m. UTC
  From: Srinath Parvathaneni <srinath.parvathaneni@arm.com>

This patch adds support for Guarded Control Stack in AArch64 linker.

This patch implements the following:
1) Defines GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit for GCS in
GNU_PROPERTY_AARCH64_FEATURE_1_AND macro.

2) Adds readelf support to read and print the GNU properties
in AArch64.

Displaying notes found in: .note.gnu.property
[      ]+Owner[        ]+Data size[    ]+Description
  GNU                  0x00000010      NT_GNU_PROPERTY_TYPE_0
      Properties: AArch64 feature: GCS

3) Adds support for the "-z gcs" linker option and document all the values
allowed with this option (-z gcs[=always|never|implicit]) where "-z gcs" is
equivalent to "-z gcs=always" and when this option is not passed in the
command line, it defaults to "implicit".

4) Adds support for the "-z gcs-report" linker option and document all the
values allowed with this option (-z gcs-report[=none|warning|error]) where
"-z gcs-report" is equivalent to "-z gcs-report=none" and when this option
is not passed in the command line, it defaults to "none".

The ABI changes adding GNU_PROPERTY_AARCH64_FEATURE_1_GCS to the
GNU property GNU_PROPERTY_AARCH64_FEATURE_1_AND is merged into main and
can be found in [1].

[1] https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst

Co-authored-by: Matthieu Longo <matthieu.longo@arm.com>
Co-authored-by: Yury Khrustalev <yury.khrustalev@arm.com>

---
Changes in v4:
 - Improved parsing of linker command line options (COMPILE_TIME_STRLEN macro).
 - Renamed aarch64_bti_pac_info into aarch64_gnu_prop_info.
 - Fixed type missmatch warning / error for gcs_type parameter.
 - Removed prefix "experimental" from GCS-related linker flags.

Link to v3: https://inbox.sourceware.org/binutils/20240911130344.1230746-1-srinath.parvathaneni@arm.com/

Regression tested on the aarch64-none-linux-gnu target and no regressions
was found.

Is this OK for trunk? I do not have commit rights, if OK, can someone commit
on my behalf?

---
 bfd/elfnn-aarch64.c                         | 93 ++++++++++++++++-----
 bfd/elfxx-aarch64.c                         | 37 +++++++-
 bfd/elfxx-aarch64.h                         | 36 ++++++--
 binutils/readelf.c                          |  4 +
 include/elf/common.h                        |  1 +
 ld/emultempl/aarch64elf.em                  | 58 +++++++++++--
 ld/testsuite/ld-aarch64/aarch64-elf.exp     | 23 +++++
 ld/testsuite/ld-aarch64/property-bti-pac1.d |  2 +-
 ld/testsuite/ld-aarch64/property-bti-pac1.s | 14 ++++
 ld/testsuite/ld-aarch64/property-gcs.s      | 25 ++++++
 ld/testsuite/ld-aarch64/property-gcs1.d     |  6 ++
 ld/testsuite/ld-aarch64/property-gcs10.d    |  6 ++
 ld/testsuite/ld-aarch64/property-gcs11.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs12.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs13.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs14.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs15.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs16.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs17.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs18.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs19.d    |  6 ++
 ld/testsuite/ld-aarch64/property-gcs2.d     | 11 +++
 ld/testsuite/ld-aarch64/property-gcs2.s     | 33 ++++++++
 ld/testsuite/ld-aarch64/property-gcs20.d    |  6 ++
 ld/testsuite/ld-aarch64/property-gcs21.d    |  6 ++
 ld/testsuite/ld-aarch64/property-gcs22.d    | 11 +++
 ld/testsuite/ld-aarch64/property-gcs3.d     | 11 +++
 ld/testsuite/ld-aarch64/property-gcs4.d     | 11 +++
 ld/testsuite/ld-aarch64/property-gcs5.d     | 11 +++
 ld/testsuite/ld-aarch64/property-gcs6.d     | 12 +++
 ld/testsuite/ld-aarch64/property-gcs7.d     |  6 ++
 ld/testsuite/ld-aarch64/property-gcs8.d     | 11 +++
 ld/testsuite/ld-aarch64/property-gcs9.d     | 12 +++
 33 files changed, 506 insertions(+), 34 deletions(-)
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs.s
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs1.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs10.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs11.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs12.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs13.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs14.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs15.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs16.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs17.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs18.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs19.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs2.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs2.s
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs20.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs21.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs22.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs3.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs4.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs5.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs6.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs7.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs8.d
 create mode 100644 ld/testsuite/ld-aarch64/property-gcs9.d
  

Comments

Richard Earnshaw (lists) Oct. 24, 2024, 3:25 p.m. UTC | #1
On 14/10/2024 11:17, Yury Khrustalev wrote:
> From: Srinath Parvathaneni <srinath.parvathaneni@arm.com>
> 
> This patch adds support for Guarded Control Stack in AArch64 linker.
> 
> This patch implements the following:
> 1) Defines GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit for GCS in
> GNU_PROPERTY_AARCH64_FEATURE_1_AND macro.
> 
> 2) Adds readelf support to read and print the GNU properties
> in AArch64.
> 
> Displaying notes found in: .note.gnu.property
> [      ]+Owner[        ]+Data size[    ]+Description
>   GNU                  0x00000010      NT_GNU_PROPERTY_TYPE_0
>       Properties: AArch64 feature: GCS
> 
> 3) Adds support for the "-z gcs" linker option and document all the values
> allowed with this option (-z gcs[=always|never|implicit]) where "-z gcs" is
> equivalent to "-z gcs=always" and when this option is not passed in the
> command line, it defaults to "implicit".
> 
> 4) Adds support for the "-z gcs-report" linker option and document all the
> values allowed with this option (-z gcs-report[=none|warning|error]) where
> "-z gcs-report" is equivalent to "-z gcs-report=none" and when this option
> is not passed in the command line, it defaults to "none".
> 
> The ABI changes adding GNU_PROPERTY_AARCH64_FEATURE_1_GCS to the
> GNU property GNU_PROPERTY_AARCH64_FEATURE_1_AND is merged into main and
> can be found in [1].
> 
> [1] https://github.com/ARM-software/abi-aa/blob/main/sysvabi64/sysvabi64.rst
> 
> Co-authored-by: Matthieu Longo <matthieu.longo@arm.com>
> Co-authored-by: Yury Khrustalev <yury.khrustalev@arm.com>
> 
> ---
> Changes in v4:
>  - Improved parsing of linker command line options (COMPILE_TIME_STRLEN macro).
>  - Renamed aarch64_bti_pac_info into aarch64_gnu_prop_info.
>  - Fixed type missmatch warning / error for gcs_type parameter.
>  - Removed prefix "experimental" from GCS-related linker flags.
> 
> Link to v3: https://inbox.sourceware.org/binutils/20240911130344.1230746-1-srinath.parvathaneni@arm.com/
> 
> Regression tested on the aarch64-none-linux-gnu target and no regressions
> was found.
> 
> Is this OK for trunk? I do not have commit rights, if OK, can someone commit
> on my behalf?
> 
> ---
>  bfd/elfnn-aarch64.c                         | 93 ++++++++++++++++-----
>  bfd/elfxx-aarch64.c                         | 37 +++++++-
>  bfd/elfxx-aarch64.h                         | 36 ++++++--
>  binutils/readelf.c                          |  4 +
>  include/elf/common.h                        |  1 +
>  ld/emultempl/aarch64elf.em                  | 58 +++++++++++--
>  ld/testsuite/ld-aarch64/aarch64-elf.exp     | 23 +++++
>  ld/testsuite/ld-aarch64/property-bti-pac1.d |  2 +-
>  ld/testsuite/ld-aarch64/property-bti-pac1.s | 14 ++++
>  ld/testsuite/ld-aarch64/property-gcs.s      | 25 ++++++
>  ld/testsuite/ld-aarch64/property-gcs1.d     |  6 ++
>  ld/testsuite/ld-aarch64/property-gcs10.d    |  6 ++
>  ld/testsuite/ld-aarch64/property-gcs11.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs12.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs13.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs14.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs15.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs16.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs17.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs18.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs19.d    |  6 ++
>  ld/testsuite/ld-aarch64/property-gcs2.d     | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs2.s     | 33 ++++++++
>  ld/testsuite/ld-aarch64/property-gcs20.d    |  6 ++
>  ld/testsuite/ld-aarch64/property-gcs21.d    |  6 ++
>  ld/testsuite/ld-aarch64/property-gcs22.d    | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs3.d     | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs4.d     | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs5.d     | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs6.d     | 12 +++
>  ld/testsuite/ld-aarch64/property-gcs7.d     |  6 ++
>  ld/testsuite/ld-aarch64/property-gcs8.d     | 11 +++
>  ld/testsuite/ld-aarch64/property-gcs9.d     | 12 +++
>  33 files changed, 506 insertions(+), 34 deletions(-)
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs.s
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs1.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs10.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs11.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs12.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs13.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs14.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs15.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs16.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs17.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs18.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs19.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs2.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs2.s
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs20.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs21.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs22.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs3.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs4.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs5.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs6.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs7.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs8.d
>  create mode 100644 ld/testsuite/ld-aarch64/property-gcs9.d
> 
> diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
> index 94c9a01069b..e4c2f558844 100644
> --- a/bfd/elfnn-aarch64.c
> +++ b/bfd/elfnn-aarch64.c
> @@ -2546,6 +2546,12 @@ struct elf_aarch64_obj_tdata
>       GNU_PROPERTY_AARCH64_FEATURE_1_BTI.  */
>    int no_bti_warn;
>  
> +  /* Mark output with GCS based on -z gcs.  */
> +  aarch64_gcs_type gcs_type;
> +  /* Report linker warning/error for -z gcs-report based on
> +     -z gcs.  */
> +  aarch64_gcs_report gcs_report;
> +
>    /* PLT type based on security.  */
>    aarch64_plt_type plt_type;
>  };
> @@ -5024,7 +5030,7 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
>  			       int fix_erratum_835769,
>  			       erratum_84319_opts fix_erratum_843419,
>  			       int no_apply_dynamic_relocs,
> -			       aarch64_bti_pac_info bp_info)
> +			       aarch64_gnu_prop_info gnu_prop_info)
>  {
>    struct elf_aarch64_link_hash_table *globals;
>  
> @@ -5041,7 +5047,7 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
>    elf_aarch64_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
>    elf_aarch64_tdata (output_bfd)->no_wchar_size_warning = no_wchar_warn;
>  
> -  switch (bp_info.bti_type)
> +  switch (gnu_prop_info.bti_type)
>      {
>      case BTI_WARN:
>        elf_aarch64_tdata (output_bfd)->no_bti_warn = 0;
> @@ -5052,8 +5058,26 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
>      default:
>        break;
>      }
> -  elf_aarch64_tdata (output_bfd)->plt_type = bp_info.plt_type;
> -  setup_plt_values (link_info, bp_info.plt_type);
> +
> +  switch (gnu_prop_info.gcs_type)
> +    {
> +    case GCS_ALWAYS:
> +      elf_aarch64_tdata (output_bfd)->gnu_and_prop
> +	|= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
> +      break;
> +    case GCS_NEVER:
> +      elf_aarch64_tdata (output_bfd)->gnu_and_prop
> +	&= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
> +      break;
> +
> +    default:
> +      break;
> +    }
> +
> +  elf_aarch64_tdata (output_bfd)->gcs_type = gnu_prop_info.gcs_type;
> +  elf_aarch64_tdata (output_bfd)->gcs_report = gnu_prop_info.gcs_report;
> +  elf_aarch64_tdata (output_bfd)->plt_type = gnu_prop_info.plt_type;
> +  setup_plt_values (link_info, gnu_prop_info.plt_type);
>  }
>  
>  static bfd_vma
> @@ -10619,7 +10643,12 @@ static bfd *
>  elfNN_aarch64_link_setup_gnu_properties (struct bfd_link_info *info)
>  {
>    uint32_t prop = elf_aarch64_tdata (info->output_bfd)->gnu_and_prop;
> -  bfd *pbfd = _bfd_aarch64_elf_link_setup_gnu_properties (info, &prop);
> +  aarch64_gcs_report gcs_report
> +    = elf_aarch64_tdata (info->output_bfd)->gcs_report;
> +  aarch64_gcs_type gcs_type
> +    = elf_aarch64_tdata (info->output_bfd)->gcs_type;
> +  bfd *pbfd = _bfd_aarch64_elf_link_setup_gnu_properties (info, &prop,
> +							  gcs_report, gcs_type);
>    elf_aarch64_tdata (info->output_bfd)->gnu_and_prop = prop;
>    elf_aarch64_tdata (info->output_bfd)->plt_type
>      |= (prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) ? PLT_BTI : 0;
> @@ -10638,30 +10667,54 @@ elfNN_aarch64_merge_gnu_properties (struct bfd_link_info *info,
>  {
>    uint32_t prop
>      = elf_aarch64_tdata (info->output_bfd)->gnu_and_prop;
> +  aarch64_gcs_report gcs_report
> +    = elf_aarch64_tdata (info->output_bfd)->gcs_report;
> +  aarch64_gcs_type gcs_type
> +    = elf_aarch64_tdata (info->output_bfd)->gcs_type;
>  
> -  /* If output has been marked with BTI using command line argument, give out
> -     warning if necessary.  */
>    /* Properties are merged per type, hence only check for warnings when merging
>       GNU_PROPERTY_AARCH64_FEATURE_1_AND.  */
> -  if (((aprop && aprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
> +  if ((aprop && aprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
>  	|| (bprop && bprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND))
> -      && (prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
> -      && (!elf_aarch64_tdata (info->output_bfd)->no_bti_warn))
>      {
> -      if ((aprop && !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
> -	   || !aprop)
> +      /* If output has been marked with BTI using command line argument, give
> +	 out warning if necessary.  */
> +      if ((prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
> +	  && (!elf_aarch64_tdata (info->output_bfd)->no_bti_warn))
>  	{
> -	  _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti when "
> -				"all inputs do not have BTI in NOTE section."),
> -			      abfd);
> +	  if ((aprop && !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
> +	      || !aprop)
> +	    {
> +	      _bfd_error_handler (_("%pB: warning: BTI turned on by -z "
> +				  "force-bti when all inputs do not have BTI "
> +				  "in NOTE section."), abfd);
> +	    }
> +	  if ((bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
> +	      || !bprop)
> +	    {
> +	      _bfd_error_handler (_("%pB: warning: BTI turned on by -z "
> +				  "force-bti when all inputs do not have BTI "
> +				  "in NOTE section."), bbfd);

I realise this is just moving some code, but the grammar of this sentence is misleading.  "all inputs do not" suggests that *no* input file is correct, when we really mean "some input files are incorrect".  See below for a suggested wording that could be used here.

> +	    }
>  	}
> -      if ((bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
> -	   || !bprop)
> +
> +      /* If output has been marked with GCS using -z gcs and input
> +	 is missing GCS marking throw warning/error on
> +	 -z gcs-report=warning/error.  */
> +      if ((prop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS) && gcs_report != GCS_NONE)
>  	{
> -	  _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti when "
> -				"all inputs do not have BTI in NOTE section."),
> -			      bbfd);
> +	  if ((aprop && !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
> +	      || !aprop)
> +	    _bfd_aarch64_elf_check_gcs_report (gcs_report, abfd);
> +	  if ((bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
> +	      || !bprop)
> +	    _bfd_aarch64_elf_check_gcs_report (gcs_report, bbfd);
>  	}
> +
> +      if (gcs_type == GCS_NEVER && aprop != NULL)
> +	aprop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
> +      if (gcs_type == GCS_NEVER && bprop != NULL)
> +	bprop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
>      }
>  
>    return  _bfd_aarch64_elf_merge_gnu_properties (info, abfd, aprop,
> diff --git a/bfd/elfxx-aarch64.c b/bfd/elfxx-aarch64.c
> index d1279adc2e4..abe1ea68874 100644
> --- a/bfd/elfxx-aarch64.c
> +++ b/bfd/elfxx-aarch64.c
> @@ -702,7 +702,9 @@ _bfd_aarch64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_ty
>     GPROP accordingly.  */
>  bfd *
>  _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
> -					    uint32_t *gprop)
> +					    uint32_t *gprop,
> +					    aarch64_gcs_report gcs_report,
> +					    aarch64_gcs_type gcs_type)
>  {
>    asection *sec;
>    bfd *pbfd;
> @@ -738,6 +740,11 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
>  	    _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti "
>  				  "when all inputs do not have BTI in NOTE "
>  				  "section."), ebfd);
> +
> +      if ((gnu_prop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
> +	  && !(prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
> +	_bfd_aarch64_elf_check_gcs_report (gcs_report, ebfd);
> +

Rather than accumulating the property over all the input bfds and then reporting something at the end, perhaps with a misleading attribution of the source of the problem, I think it would be better to check this once per BFD and report each input that fails to meet the necessary criteria.  That way users get a complete report of the extent of the problem rather than having to go fix one input file for each run of the linker.  You might want to put a limit on the total number of such errors, though (perhaps 20) and then at the end print a count of the total number of incompatible inputs.

>        prop->u.number |= gnu_prop;
>        prop->pr_kind = property_number;
>  
> @@ -765,6 +772,14 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
>  	  elf_section_type (sec) = SHT_NOTE;
>  	}
>      }
> +  else if (ebfd != NULL && gcs_type == GCS_NEVER)
> +    {
> +      prop = _bfd_elf_get_property (ebfd, GNU_PROPERTY_AARCH64_FEATURE_1_AND,
> +				    4);
> +      prop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
> +      if (prop->u.number == 0)
> +	prop->pr_kind = property_remove;
> +    }
>  
>    pbfd = _bfd_elf_link_setup_gnu_properties (info);
>  
> @@ -785,7 +800,8 @@ _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
>  	    {
>  	      gnu_prop = (p->property.u.number
>  			  & (GNU_PROPERTY_AARCH64_FEATURE_1_PAC
> -			      | GNU_PROPERTY_AARCH64_FEATURE_1_BTI));
> +			      | GNU_PROPERTY_AARCH64_FEATURE_1_BTI
> +			      | GNU_PROPERTY_AARCH64_FEATURE_1_GCS));
>  	      break;
>  	    }
>  	  else if (GNU_PROPERTY_AARCH64_FEATURE_1_AND < p->property.pr_type)
> @@ -922,3 +938,20 @@ _bfd_aarch64_elf_link_fixup_gnu_properties
>  	}
>      }
>  }
> +
> +/* Check AArch64 GCS report.  */
> +void
> +_bfd_aarch64_elf_check_gcs_report (aarch64_gcs_report gcs_report, bfd *ebfd)
> +{
> +  if (gcs_report == GCS_WARN)
> +    _bfd_error_handler (_("%pB: warning: GCS turned on by -z gcs "
> +			"on the output when all inputs do not have GCS in NOTE "
> +			"section."), ebfd);

The grammar of this message needs improving.  Is ebfd the input or output bfd?  Really we want to tell the user which inputs are lacking the required property rather than just a generic (one of all your inputs lacks it).  Assuming we do have the right bfd for the diagnostic, something like

%pB: warning: GCS is required by -z gcs, but input file lacks the necessary property note.

> +  else if (gcs_report == GCS_ERROR)
> +    {
> +      _bfd_error_handler (_("%pB: error: GCS turned on by -z gcs "
> +			  "on the output when all inputs do not have GCS in "
> +			  "NOTE section."), ebfd);
> +      _exit (EXIT_FAILURE);

Similar issue with the message.  Also, since the parent function iterates over all the input bfds, I think it would be better for this function to return true/false here and for the parent to exit once all input bfds have been examined.

> +    }
> +}
> diff --git a/bfd/elfxx-aarch64.h b/bfd/elfxx-aarch64.h
> index f21e5ee5efc..4852d33be3e 100644
> --- a/bfd/elfxx-aarch64.h
> +++ b/bfd/elfxx-aarch64.h
> @@ -46,6 +46,27 @@ typedef enum
>    BTI_WARN	= 1,  /* BTI is enabled with -z force-bti.  */
>  } aarch64_enable_bti_type;
>  
> +/* To indicate whether GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit is
> +   enabled/disabled on the output when -z gcs linker
> +   command line option is passed.  */
> +typedef enum
> +{
> +  GCS_NEVER	= 0,  /* gcs is disabled on output.  */
> +  GCS_IMPLICIT  = 1,  /* gcs is deduced from input object.  */
> +  GCS_ALWAYS	= 2,  /* gsc is enabled on output.  */
> +} aarch64_gcs_type;
> +
> +/* To indicate whether to generate linker warning/errors for
> +   -z gcs-report when -z gcs=always is passed.  */
> +typedef enum
> +{
> +  GCS_NONE	= 0,  /* Does not emit any warning/error messages.  */
> +  GCS_WARN	= 1,  /* Emit warning when the input objects are missing gcs
> +			 markings and output have gcs marking.  */
> +  GCS_ERROR	= 2,  /* Emit error when the input objects are missing gcs
> +			 markings and output have gcs marking.  */
> +} aarch64_gcs_report;
> +
>  /* A structure to encompass all information coming from BTI or PAC
>     related command line options.  This involves the "PLT_TYPE" to determine
>     which version of PLTs to pick and "BTI_TYPE" to determine if
> @@ -54,7 +75,9 @@ typedef struct
>  {
>    aarch64_plt_type plt_type;
>    aarch64_enable_bti_type bti_type;
> -} aarch64_bti_pac_info;
> +  aarch64_gcs_type gcs_type;
> +  aarch64_gcs_report gcs_report;
> +} aarch64_gnu_prop_info;

I think these data structures and enumerations need a bit more commentary to make it explicit that absent properties all take on the zero values as a default.  That would then make the code in bfd_elfNN_aarch64_set_options() easier to understand (though it's still worth a comment there as well, I think).

>  
>  /* An enum to define what kind of erratum fixes we should apply.  This gives the
>     user a bit more control over the sequences we generate.  */
> @@ -67,11 +90,11 @@ typedef enum
>  
>  extern void bfd_elf64_aarch64_set_options
>    (bfd *, struct bfd_link_info *, int, int, int, int, erratum_84319_opts, int,
> -   aarch64_bti_pac_info);
> +   aarch64_gnu_prop_info);
>  
>  extern void bfd_elf32_aarch64_set_options
>    (bfd *, struct bfd_link_info *, int, int, int, int, erratum_84319_opts, int,
> -   aarch64_bti_pac_info);
> +   aarch64_gnu_prop_info);
>  
>  /* AArch64 stub generation support for ELF64.  Called from the linker.  */
>  extern int elf64_aarch64_setup_section_lists
> @@ -146,8 +169,9 @@ _bfd_aarch64_elf_write_core_note (bfd *, char *, int *, int, ...);
>  #define elf_backend_write_core_note	_bfd_aarch64_elf_write_core_note
>  
>  extern bfd *
> -_bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *,
> -					    uint32_t *);
> +_bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *, uint32_t *,
> +					    aarch64_gcs_report,
> +					    aarch64_gcs_type);

I'm a bit concerned that the arguments to this function might grow out of hand if we start adding multiple properties.  My initial thought was to simply pass the tdata structure directly, but i don't think that will work without some significant code restructuring, since the tdata type is defined directly in elfnn-aarch64.c, but the setup function is in elfxx-aarch64.c.

So perhaps we should have a sub-structure within that, something like "gnu_property_options" that is defined in a suitable aarch64 header and that contains (initially) both of these values.  Then we can pass this one parameter to convey all the relevant settings

  struct elf_aarch64_gnu_property_options *property_opts = &(elf_aarch64_tdata (abfd).gnu_property_options);
  bfd *pbfd = _bfd_aarch64_elf_link_setup_gnu_properties (info, &prop, property_opts);

>  
>  extern enum elf_property_kind
>  _bfd_aarch64_elf_parse_gnu_properties (bfd *, unsigned int,
> @@ -157,6 +181,8 @@ extern bool
>  _bfd_aarch64_elf_merge_gnu_properties (struct bfd_link_info *, bfd *,
>  				       elf_property *, elf_property *,
>  				       uint32_t);
> +extern void
> +_bfd_aarch64_elf_check_gcs_report (aarch64_gcs_report, bfd *);
>  
>  extern void
>  _bfd_aarch64_elf_link_fixup_gnu_properties (struct bfd_link_info *,
> diff --git a/binutils/readelf.c b/binutils/readelf.c
> index 0f8dc1b9716..f905a10b194 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -21246,6 +21246,10 @@ decode_aarch64_feature_1_and (unsigned int bitmask)
>  	  printf ("PAC");
>  	  break;
>  
> +	case GNU_PROPERTY_AARCH64_FEATURE_1_GCS:
> +	  printf ("GCS");
> +	  break;
> +
>  	default:
>  	  printf (_("<unknown: %x>"), bit);
>  	  break;
> diff --git a/include/elf/common.h b/include/elf/common.h
> index c9920e7731a..c4eb33bf308 100644
> --- a/include/elf/common.h
> +++ b/include/elf/common.h
> @@ -1037,6 +1037,7 @@
>  
>  #define GNU_PROPERTY_AARCH64_FEATURE_1_BTI	(1U << 0)
>  #define GNU_PROPERTY_AARCH64_FEATURE_1_PAC	(1U << 1)
> +#define GNU_PROPERTY_AARCH64_FEATURE_1_GCS	(1U << 2)
>  
>  /* Values used in GNU .note.ABI-tag notes (NT_GNU_ABI_TAG).  */
>  #define GNU_ABI_TAG_LINUX	0
> diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
> index aa2859d4f73..ba536b0e379 100644
> --- a/ld/emultempl/aarch64elf.em
> +++ b/ld/emultempl/aarch64elf.em
> @@ -36,6 +36,15 @@ static erratum_84319_opts fix_erratum_843419 = ERRAT_NONE;
>  static int no_apply_dynamic_relocs = 0;
>  static aarch64_plt_type plt_type = PLT_NORMAL;
>  static aarch64_enable_bti_type bti_type = BTI_NONE;
> +static aarch64_gcs_type gcs_type = GCS_IMPLICIT;
> +static aarch64_gcs_report gcs_report = GCS_NONE;
> +
> +#define COMPILE_TIME_STRLEN(s) \
> +  (sizeof(s) - 1)
> +#define EGR  "gcs-report"
> +#define EG   "gcs"
> +#define EGR_LEN COMPILE_TIME_STRLEN (EGR)
> +#define EG_LEN  COMPILE_TIME_STRLEN (EG)
>  
>  static void
>  gld${EMULATION_NAME}_before_parse (void)
> @@ -321,9 +330,11 @@ aarch64_elf_create_output_section_statements (void)
>        return;
>      }
>  
> -  aarch64_bti_pac_info bp_info;
> -  bp_info.plt_type = plt_type;
> -  bp_info.bti_type = bti_type;
> +  aarch64_gnu_prop_info gnu_prop_info;
> +  gnu_prop_info.plt_type = plt_type;
> +  gnu_prop_info.bti_type = bti_type;
> +  gnu_prop_info.gcs_type = gcs_type;
> +  gnu_prop_info.gcs_report = gcs_report;
>  
>    bfd_elf${ELFSIZE}_aarch64_set_options (link_info.output_bfd, &link_info,
>  				 no_enum_size_warning,
> @@ -331,7 +342,7 @@ aarch64_elf_create_output_section_statements (void)
>  				 pic_veneer,
>  				 fix_erratum_835769, fix_erratum_843419,
>  				 no_apply_dynamic_relocs,
> -				 bp_info);
> +				 gnu_prop_info);
>  
>    stub_file = lang_add_input_file ("linker stubs",
>  				   lang_input_file_is_fake_enum,
> @@ -398,16 +409,51 @@ PARSE_AND_LIST_OPTIONS='
>    fprintf (file, _("  --no-apply-dynamic-relocs    Do not apply link-time values for dynamic relocations\n"));
>    fprintf (file, _("  -z force-bti                  Turn on Branch Target Identification mechanism and generate PLTs with BTI. Generate warnings for missing BTI on inputs\n"));
>    fprintf (file, _("  -z pac-plt                    Protect PLTs with Pointer Authentication.\n"));
> +  fprintf (file, _("\
> +  -z gcs[=always|never|implicit]		Turn on Guarded Control Stack(gcs) mechanism on the output.\n\
> +							implicit(default): deduce gcs from input objects.\n\
> +							always: always marks the output with gcs.\n\
> +							never: never marks the output with gcs.\n"));
> +  fprintf (file, _("\
> +  -z gcs-report[=none|warning|error]	Emit warning/error on mismatch of gcs marking between input objects and ouput.\n\
> +							none (default): Does not emit any warning/error messages.\n\
> +							warning: Emit warning when the input objects are missing gcs markings\n\
> +								 and output have gcs marking.\n\
> +							error: Emit error when the input objects are missing gcs markings\n\
> +							       and output have gcs marking.\n"));
> +
>  '
>  
>  PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
> -      else if (strcmp (optarg, "force-bti") == 0)
> +     else if (strcmp (optarg, "force-bti") == 0)
>  	{
>  	  plt_type |= PLT_BTI;
>  	  bti_type = BTI_WARN;
>  	}
> -      else if (strcmp (optarg, "pac-plt") == 0)
> +     else if (strcmp (optarg, "pac-plt") == 0)
>  	plt_type |= PLT_PAC;
> +     else if (strncmp (optarg, EGR, EGR_LEN) == 0)
> +	{
> +	  if (strlen (optarg) == EGR_LEN || strcmp (optarg + EGR_LEN, "=none") == 0)
> +	    gcs_report = GCS_NONE;
> +	  else if (strcmp (optarg + EGR_LEN, "=warning") == 0)
> +	    gcs_report = GCS_WARN;
> +	  else if (strcmp (optarg + EGR_LEN, "=error") == 0)
> +	    gcs_report = GCS_ERROR;
> +	  else
> +	    einfo (_("%P: error: unrecognized: `%s'\''\n"), optarg);
> +	}
> +     else if (strncmp (optarg, EG, EG_LEN) == 0)
> +	{
> +	  if (strlen (optarg) == EG_LEN || strcmp (optarg + EG_LEN, "=always") == 0)
> +	    gcs_type = GCS_ALWAYS;
> +	  else if (strcmp (optarg + EG_LEN, "=never") == 0)
> +	    gcs_type = GCS_NEVER;
> +	  else if (strcmp (optarg + EG_LEN, "=implicit") == 0)
> +	    gcs_type = GCS_IMPLICIT;
> +	  else
> +	    einfo (_("%P: error: unrecognized: `%s'\''\n"), optarg);
> +	}
>  '
>  PARSE_AND_LIST_ARGS_CASE_Z="$PARSE_AND_LIST_ARGS_CASE_Z $PARSE_AND_LIST_ARGS_CASE_Z_AARCH64"
>  
> diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp
> index c7d97f3ced9..d5a208f6768 100644
> --- a/ld/testsuite/ld-aarch64/aarch64-elf.exp
> +++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp
> @@ -487,3 +487,26 @@ if { [supports_dt_relr] } {
>  if { ![skip_sframe_tests] } {
>    run_dump_test "sframe-simple-1"
>  }
> +
> +run_dump_test "property-gcs1"
> +run_dump_test "property-gcs2"
> +run_dump_test "property-gcs3"
> +run_dump_test "property-gcs4"
> +run_dump_test "property-gcs5"
> +run_dump_test "property-gcs6"
> +run_dump_test "property-gcs7"
> +run_dump_test "property-gcs8"
> +run_dump_test "property-gcs9"
> +run_dump_test "property-gcs10"
> +run_dump_test "property-gcs11"
> +run_dump_test "property-gcs12"
> +run_dump_test "property-gcs13"
> +run_dump_test "property-gcs14"
> +run_dump_test "property-gcs15"
> +run_dump_test "property-gcs16"
> +run_dump_test "property-gcs17"
> +run_dump_test "property-gcs18"
> +run_dump_test "property-gcs19"
> +run_dump_test "property-gcs20"
> +run_dump_test "property-gcs21"
> +run_dump_test "property-gcs22"

We should be able to do this using a dir listing of the dump files.  See, eg: ld-z80/z80.exp.  Then if more tests are added there's no need to keep manually adding them to the exp file.


> diff --git a/ld/testsuite/ld-aarch64/property-bti-pac1.d b/ld/testsuite/ld-aarch64/property-bti-pac1.d
> index 59fa695165a..c28a0cbf850 100644
> --- a/ld/testsuite/ld-aarch64/property-bti-pac1.d
> +++ b/ld/testsuite/ld-aarch64/property-bti-pac1.d
> @@ -8,4 +8,4 @@
>  Displaying notes found in: .note.gnu.property
>  [ 	]+Owner[ 	]+Data size[ 	]+Description
>    GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> -      Properties: AArch64 feature: BTI, PAC
> +      Properties: AArch64 feature: BTI, PAC, GCS
> diff --git a/ld/testsuite/ld-aarch64/property-bti-pac1.s b/ld/testsuite/ld-aarch64/property-bti-pac1.s
> index 414c9277f1d..42156917d58 100644
> --- a/ld/testsuite/ld-aarch64/property-bti-pac1.s
> +++ b/ld/testsuite/ld-aarch64/property-bti-pac1.s
> @@ -12,6 +12,20 @@ _start:
>  	.long 5f - 2f		/* data length */
>  	.long 5			/* note type */
>  0:	.asciz "GNU"		/* vendor name */
> +1:
> +	.p2align 3
> +2:	.long 0xc0000000	/* pr_type.  */
> +	.long 4f - 3f		/* pr_datasz.  */
> +3:
> +	.long 0x4		/* GCS.  */
> +4:
> +	.p2align 3
> +5:
> +	.p2align 3
> +	.long 1f - 0f		/* name length */
> +	.long 5f - 2f		/* data length */
> +	.long 5			/* note type */
> +0:	.asciz "GNU"		/* vendor name */
>  1:
>  	.p2align 3
>  2:	.long 0xc0000000	/* pr_type.  */
> diff --git a/ld/testsuite/ld-aarch64/property-gcs.s b/ld/testsuite/ld-aarch64/property-gcs.s
> new file mode 100644
> index 00000000000..bc7e66e8933
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs.s
> @@ -0,0 +1,25 @@
> +	.text
> +	.globl _start
> +	.type _start,@function
> +_start:
> +	mov x1, #2
> +.ifndef __mult__
> +	bl foo
> +.endif
> +.ifdef __property_gcs__
> +	.section ".note.gnu.property", "a"
> +	.p2align 3
> +	.long 1f - 0f		/* name length */
> +	.long 5f - 2f		/* data length */
> +	.long 5			/* note type */
> +0:	.asciz "GNU"		/* vendor name */
> +1:
> +	.p2align 3
> +2:	.long 0xc0000000	/* pr_type.  */
> +	.long 4f - 3f		/* pr_datasz.  */
> +3:
> +	.long 0x4		/* GCS.  */
> +4:
> +	.p2align 3
> +5:
> +.endif
> diff --git a/ld/testsuite/ld-aarch64/property-gcs1.d b/ld/testsuite/ld-aarch64/property-gcs1.d
> new file mode 100644
> index 00000000000..c724ac56ca3
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs1.d
> @@ -0,0 +1,6 @@
> +#name: GNU Property (input without gcs)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -shared
> +#readelf: -n
> diff --git a/ld/testsuite/ld-aarch64/property-gcs10.d b/ld/testsuite/ld-aarch64/property-gcs10.d
> new file mode 100644
> index 00000000000..3a2c992f974
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs10.d
> @@ -0,0 +1,6 @@
> +#name: GNU Property (input without gcs output forced with gcs=always gcs-report=error)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs=always -z gcs-report=error
> +#error: .*property-gcs.*: error: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
> diff --git a/ld/testsuite/ld-aarch64/property-gcs11.d b/ld/testsuite/ld-aarch64/property-gcs11.d
> new file mode 100644
> index 00000000000..094b9be2582
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs11.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs12.d b/ld/testsuite/ld-aarch64/property-gcs12.d
> new file mode 100644
> index 00000000000..355544775f3
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs12.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs=always)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs=always
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs13.d b/ld/testsuite/ld-aarch64/property-gcs13.d
> new file mode 100644
> index 00000000000..c6543ba168c
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs13.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs gcs-report=none)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs -z gcs-report=none
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs14.d b/ld/testsuite/ld-aarch64/property-gcs14.d
> new file mode 100644
> index 00000000000..1c612b5c9bd
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs14.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs gcs-report=warning)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs -z gcs-report=warning
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs15.d b/ld/testsuite/ld-aarch64/property-gcs15.d
> new file mode 100644
> index 00000000000..7e46e8eae2f
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs15.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs gcs-report=error)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs -z gcs-report=error
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs16.d b/ld/testsuite/ld-aarch64/property-gcs16.d
> new file mode 100644
> index 00000000000..c20cbe531be
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs16.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs=always gcs-report=none)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs=always -z gcs-report=none
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs17.d b/ld/testsuite/ld-aarch64/property-gcs17.d
> new file mode 100644
> index 00000000000..2ee3f103701
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs17.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs=always gcs-report=warning)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs=always -z gcs-report=warning
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs18.d b/ld/testsuite/ld-aarch64/property-gcs18.d
> new file mode 100644
> index 00000000000..6c45d77c179
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs18.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs=always gcs-report=error)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs=always -z gcs-report=error
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs19.d b/ld/testsuite/ld-aarch64/property-gcs19.d
> new file mode 100644
> index 00000000000..588af48859b
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs19.d
> @@ -0,0 +1,6 @@
> +#name: GNU Property (input without gcs output forced with gcs=never)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs=never
> +#readelf: -n
> diff --git a/ld/testsuite/ld-aarch64/property-gcs2.d b/ld/testsuite/ld-aarch64/property-gcs2.d
> new file mode 100644
> index 00000000000..ed545a180b3
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs2.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -shared
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs2.s b/ld/testsuite/ld-aarch64/property-gcs2.s
> new file mode 100644
> index 00000000000..6db7d8396c8
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs2.s
> @@ -0,0 +1,33 @@
> +	.text
> +	.global	foo
> +	.type	foo, %function
> +foo:
> +	sub	sp, sp, #16
> +	mov	w0, 9
> +	str	w0, [sp, 12]
> +	ldr	w0, [sp, 12]
> +	add	w0, w0, 4
> +	str	w0, [sp, 12]
> +	nop
> +	add	sp, sp, 16
> +	ret
> +	.size	foo, .-foo
> +	.global	bar
> +	.type	bar, %function
> +.ifdef __property_gcs__
> +	.section ".note.gnu.property", "a"
> +	.p2align 3
> +	.long 1f - 0f		/* name length */
> +	.long 5f - 2f		/* data length */
> +	.long 5			/* note type */
> +0:	.asciz "GNU"		/* vendor name */
> +1:
> +	.p2align 3
> +2:	.long 0xc0000000	/* pr_type.  */
> +	.long 4f - 3f		/* pr_datasz.  */
> +3:
> +	.long 0x4		/* GCS.  */
> +4:
> +	.p2align 3
> +5:
> +.endif
> diff --git a/ld/testsuite/ld-aarch64/property-gcs20.d b/ld/testsuite/ld-aarch64/property-gcs20.d
> new file mode 100644
> index 00000000000..779e845f680
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs20.d
> @@ -0,0 +1,6 @@
> +#name: GNU Property (input without gcs output forced with gcs=implicit)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs=implicit
> +#readelf: -n
> diff --git a/ld/testsuite/ld-aarch64/property-gcs21.d b/ld/testsuite/ld-aarch64/property-gcs21.d
> new file mode 100644
> index 00000000000..dbfb5ec0264
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs21.d
> @@ -0,0 +1,6 @@
> +#name: GNU Property (input with gcs output forced with gcs=never)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs=never
> +#readelf: -n
> diff --git a/ld/testsuite/ld-aarch64/property-gcs22.d b/ld/testsuite/ld-aarch64/property-gcs22.d
> new file mode 100644
> index 00000000000..dd92399fc4f
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs22.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input with gcs output forced with gcs=implicit)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
> +#ld: -z gcs=implicit
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs3.d b/ld/testsuite/ld-aarch64/property-gcs3.d
> new file mode 100644
> index 00000000000..0ea2d7ed79c
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs3.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input without gcs output forced with gcs)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs4.d b/ld/testsuite/ld-aarch64/property-gcs4.d
> new file mode 100644
> index 00000000000..0669d64b530
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs4.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input without gcs output forced with gcs=always)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs=always
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs5.d b/ld/testsuite/ld-aarch64/property-gcs5.d
> new file mode 100644
> index 00000000000..df8643a9d18
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs5.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input without gcs output forced with gcs gcs-report=none)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs -z gcs-report=none
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs6.d b/ld/testsuite/ld-aarch64/property-gcs6.d
> new file mode 100644
> index 00000000000..3646e8a58ff
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs6.d
> @@ -0,0 +1,12 @@
> +#name: GNU Property (input without gcs output forced with gcs gcs-report=warning)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs -z gcs-report=warning
> +#readelf: -n
> +#warning: .*property-gcs.*: warning: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs7.d b/ld/testsuite/ld-aarch64/property-gcs7.d
> new file mode 100644
> index 00000000000..ba58fe24ad2
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs7.d
> @@ -0,0 +1,6 @@
> +#name: GNU Property (input without gcs output forced with gcs gcs-report=error)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs -z gcs-report=error
> +#error: .*property-gcs.*: error: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
> diff --git a/ld/testsuite/ld-aarch64/property-gcs8.d b/ld/testsuite/ld-aarch64/property-gcs8.d
> new file mode 100644
> index 00000000000..f442b71aee3
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs8.d
> @@ -0,0 +1,11 @@
> +#name: GNU Property (input without gcs output forced with gcs=always gcs-report=none)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs=always -z gcs-report=none
> +#readelf: -n
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS
> diff --git a/ld/testsuite/ld-aarch64/property-gcs9.d b/ld/testsuite/ld-aarch64/property-gcs9.d
> new file mode 100644
> index 00000000000..82b93ec0850
> --- /dev/null
> +++ b/ld/testsuite/ld-aarch64/property-gcs9.d
> @@ -0,0 +1,12 @@
> +#name: GNU Property (input without gcs output forced with gcs=always gcs-report=warning)
> +#source: property-gcs.s
> +#alltargets: [check_shared_lib_support] *linux*
> +#as: -march=armv9.4-a+gcs -defsym __mult__=0
> +#ld: -z gcs=always -z gcs-report=warning
> +#readelf: -n
> +#warning: .*property-gcs.*: warning: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
> +
> +Displaying notes found in: .note.gnu.property
> +[ 	]+Owner[ 	]+Data size[ 	]+Description
> +  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
> +      Properties: AArch64 feature: GCS

Do we need tests for links with more than one input file, in order to check that merging is done correctly? It looks like all these tests are just testing a single input bfd.

R.
  

Patch

diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 94c9a01069b..e4c2f558844 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -2546,6 +2546,12 @@  struct elf_aarch64_obj_tdata
      GNU_PROPERTY_AARCH64_FEATURE_1_BTI.  */
   int no_bti_warn;
 
+  /* Mark output with GCS based on -z gcs.  */
+  aarch64_gcs_type gcs_type;
+  /* Report linker warning/error for -z gcs-report based on
+     -z gcs.  */
+  aarch64_gcs_report gcs_report;
+
   /* PLT type based on security.  */
   aarch64_plt_type plt_type;
 };
@@ -5024,7 +5030,7 @@  bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
 			       int fix_erratum_835769,
 			       erratum_84319_opts fix_erratum_843419,
 			       int no_apply_dynamic_relocs,
-			       aarch64_bti_pac_info bp_info)
+			       aarch64_gnu_prop_info gnu_prop_info)
 {
   struct elf_aarch64_link_hash_table *globals;
 
@@ -5041,7 +5047,7 @@  bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
   elf_aarch64_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
   elf_aarch64_tdata (output_bfd)->no_wchar_size_warning = no_wchar_warn;
 
-  switch (bp_info.bti_type)
+  switch (gnu_prop_info.bti_type)
     {
     case BTI_WARN:
       elf_aarch64_tdata (output_bfd)->no_bti_warn = 0;
@@ -5052,8 +5058,26 @@  bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
     default:
       break;
     }
-  elf_aarch64_tdata (output_bfd)->plt_type = bp_info.plt_type;
-  setup_plt_values (link_info, bp_info.plt_type);
+
+  switch (gnu_prop_info.gcs_type)
+    {
+    case GCS_ALWAYS:
+      elf_aarch64_tdata (output_bfd)->gnu_and_prop
+	|= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      break;
+    case GCS_NEVER:
+      elf_aarch64_tdata (output_bfd)->gnu_and_prop
+	&= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      break;
+
+    default:
+      break;
+    }
+
+  elf_aarch64_tdata (output_bfd)->gcs_type = gnu_prop_info.gcs_type;
+  elf_aarch64_tdata (output_bfd)->gcs_report = gnu_prop_info.gcs_report;
+  elf_aarch64_tdata (output_bfd)->plt_type = gnu_prop_info.plt_type;
+  setup_plt_values (link_info, gnu_prop_info.plt_type);
 }
 
 static bfd_vma
@@ -10619,7 +10643,12 @@  static bfd *
 elfNN_aarch64_link_setup_gnu_properties (struct bfd_link_info *info)
 {
   uint32_t prop = elf_aarch64_tdata (info->output_bfd)->gnu_and_prop;
-  bfd *pbfd = _bfd_aarch64_elf_link_setup_gnu_properties (info, &prop);
+  aarch64_gcs_report gcs_report
+    = elf_aarch64_tdata (info->output_bfd)->gcs_report;
+  aarch64_gcs_type gcs_type
+    = elf_aarch64_tdata (info->output_bfd)->gcs_type;
+  bfd *pbfd = _bfd_aarch64_elf_link_setup_gnu_properties (info, &prop,
+							  gcs_report, gcs_type);
   elf_aarch64_tdata (info->output_bfd)->gnu_and_prop = prop;
   elf_aarch64_tdata (info->output_bfd)->plt_type
     |= (prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) ? PLT_BTI : 0;
@@ -10638,30 +10667,54 @@  elfNN_aarch64_merge_gnu_properties (struct bfd_link_info *info,
 {
   uint32_t prop
     = elf_aarch64_tdata (info->output_bfd)->gnu_and_prop;
+  aarch64_gcs_report gcs_report
+    = elf_aarch64_tdata (info->output_bfd)->gcs_report;
+  aarch64_gcs_type gcs_type
+    = elf_aarch64_tdata (info->output_bfd)->gcs_type;
 
-  /* If output has been marked with BTI using command line argument, give out
-     warning if necessary.  */
   /* Properties are merged per type, hence only check for warnings when merging
      GNU_PROPERTY_AARCH64_FEATURE_1_AND.  */
-  if (((aprop && aprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
+  if ((aprop && aprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
 	|| (bprop && bprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND))
-      && (prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
-      && (!elf_aarch64_tdata (info->output_bfd)->no_bti_warn))
     {
-      if ((aprop && !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
-	   || !aprop)
+      /* If output has been marked with BTI using command line argument, give
+	 out warning if necessary.  */
+      if ((prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
+	  && (!elf_aarch64_tdata (info->output_bfd)->no_bti_warn))
 	{
-	  _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti when "
-				"all inputs do not have BTI in NOTE section."),
-			      abfd);
+	  if ((aprop && !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
+	      || !aprop)
+	    {
+	      _bfd_error_handler (_("%pB: warning: BTI turned on by -z "
+				  "force-bti when all inputs do not have BTI "
+				  "in NOTE section."), abfd);
+	    }
+	  if ((bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
+	      || !bprop)
+	    {
+	      _bfd_error_handler (_("%pB: warning: BTI turned on by -z "
+				  "force-bti when all inputs do not have BTI "
+				  "in NOTE section."), bbfd);
+	    }
 	}
-      if ((bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
-	   || !bprop)
+
+      /* If output has been marked with GCS using -z gcs and input
+	 is missing GCS marking throw warning/error on
+	 -z gcs-report=warning/error.  */
+      if ((prop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS) && gcs_report != GCS_NONE)
 	{
-	  _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti when "
-				"all inputs do not have BTI in NOTE section."),
-			      bbfd);
+	  if ((aprop && !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+	      || !aprop)
+	    _bfd_aarch64_elf_check_gcs_report (gcs_report, abfd);
+	  if ((bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+	      || !bprop)
+	    _bfd_aarch64_elf_check_gcs_report (gcs_report, bbfd);
 	}
+
+      if (gcs_type == GCS_NEVER && aprop != NULL)
+	aprop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      if (gcs_type == GCS_NEVER && bprop != NULL)
+	bprop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
     }
 
   return  _bfd_aarch64_elf_merge_gnu_properties (info, abfd, aprop,
diff --git a/bfd/elfxx-aarch64.c b/bfd/elfxx-aarch64.c
index d1279adc2e4..abe1ea68874 100644
--- a/bfd/elfxx-aarch64.c
+++ b/bfd/elfxx-aarch64.c
@@ -702,7 +702,9 @@  _bfd_aarch64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_ty
    GPROP accordingly.  */
 bfd *
 _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
-					    uint32_t *gprop)
+					    uint32_t *gprop,
+					    aarch64_gcs_report gcs_report,
+					    aarch64_gcs_type gcs_type)
 {
   asection *sec;
   bfd *pbfd;
@@ -738,6 +740,11 @@  _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
 	    _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti "
 				  "when all inputs do not have BTI in NOTE "
 				  "section."), ebfd);
+
+      if ((gnu_prop & GNU_PROPERTY_AARCH64_FEATURE_1_GCS)
+	  && !(prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS))
+	_bfd_aarch64_elf_check_gcs_report (gcs_report, ebfd);
+
       prop->u.number |= gnu_prop;
       prop->pr_kind = property_number;
 
@@ -765,6 +772,14 @@  _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
 	  elf_section_type (sec) = SHT_NOTE;
 	}
     }
+  else if (ebfd != NULL && gcs_type == GCS_NEVER)
+    {
+      prop = _bfd_elf_get_property (ebfd, GNU_PROPERTY_AARCH64_FEATURE_1_AND,
+				    4);
+      prop->u.number &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      if (prop->u.number == 0)
+	prop->pr_kind = property_remove;
+    }
 
   pbfd = _bfd_elf_link_setup_gnu_properties (info);
 
@@ -785,7 +800,8 @@  _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info,
 	    {
 	      gnu_prop = (p->property.u.number
 			  & (GNU_PROPERTY_AARCH64_FEATURE_1_PAC
-			      | GNU_PROPERTY_AARCH64_FEATURE_1_BTI));
+			      | GNU_PROPERTY_AARCH64_FEATURE_1_BTI
+			      | GNU_PROPERTY_AARCH64_FEATURE_1_GCS));
 	      break;
 	    }
 	  else if (GNU_PROPERTY_AARCH64_FEATURE_1_AND < p->property.pr_type)
@@ -922,3 +938,20 @@  _bfd_aarch64_elf_link_fixup_gnu_properties
 	}
     }
 }
+
+/* Check AArch64 GCS report.  */
+void
+_bfd_aarch64_elf_check_gcs_report (aarch64_gcs_report gcs_report, bfd *ebfd)
+{
+  if (gcs_report == GCS_WARN)
+    _bfd_error_handler (_("%pB: warning: GCS turned on by -z gcs "
+			"on the output when all inputs do not have GCS in NOTE "
+			"section."), ebfd);
+  else if (gcs_report == GCS_ERROR)
+    {
+      _bfd_error_handler (_("%pB: error: GCS turned on by -z gcs "
+			  "on the output when all inputs do not have GCS in "
+			  "NOTE section."), ebfd);
+      _exit (EXIT_FAILURE);
+    }
+}
diff --git a/bfd/elfxx-aarch64.h b/bfd/elfxx-aarch64.h
index f21e5ee5efc..4852d33be3e 100644
--- a/bfd/elfxx-aarch64.h
+++ b/bfd/elfxx-aarch64.h
@@ -46,6 +46,27 @@  typedef enum
   BTI_WARN	= 1,  /* BTI is enabled with -z force-bti.  */
 } aarch64_enable_bti_type;
 
+/* To indicate whether GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit is
+   enabled/disabled on the output when -z gcs linker
+   command line option is passed.  */
+typedef enum
+{
+  GCS_NEVER	= 0,  /* gcs is disabled on output.  */
+  GCS_IMPLICIT  = 1,  /* gcs is deduced from input object.  */
+  GCS_ALWAYS	= 2,  /* gsc is enabled on output.  */
+} aarch64_gcs_type;
+
+/* To indicate whether to generate linker warning/errors for
+   -z gcs-report when -z gcs=always is passed.  */
+typedef enum
+{
+  GCS_NONE	= 0,  /* Does not emit any warning/error messages.  */
+  GCS_WARN	= 1,  /* Emit warning when the input objects are missing gcs
+			 markings and output have gcs marking.  */
+  GCS_ERROR	= 2,  /* Emit error when the input objects are missing gcs
+			 markings and output have gcs marking.  */
+} aarch64_gcs_report;
+
 /* A structure to encompass all information coming from BTI or PAC
    related command line options.  This involves the "PLT_TYPE" to determine
    which version of PLTs to pick and "BTI_TYPE" to determine if
@@ -54,7 +75,9 @@  typedef struct
 {
   aarch64_plt_type plt_type;
   aarch64_enable_bti_type bti_type;
-} aarch64_bti_pac_info;
+  aarch64_gcs_type gcs_type;
+  aarch64_gcs_report gcs_report;
+} aarch64_gnu_prop_info;
 
 /* An enum to define what kind of erratum fixes we should apply.  This gives the
    user a bit more control over the sequences we generate.  */
@@ -67,11 +90,11 @@  typedef enum
 
 extern void bfd_elf64_aarch64_set_options
   (bfd *, struct bfd_link_info *, int, int, int, int, erratum_84319_opts, int,
-   aarch64_bti_pac_info);
+   aarch64_gnu_prop_info);
 
 extern void bfd_elf32_aarch64_set_options
   (bfd *, struct bfd_link_info *, int, int, int, int, erratum_84319_opts, int,
-   aarch64_bti_pac_info);
+   aarch64_gnu_prop_info);
 
 /* AArch64 stub generation support for ELF64.  Called from the linker.  */
 extern int elf64_aarch64_setup_section_lists
@@ -146,8 +169,9 @@  _bfd_aarch64_elf_write_core_note (bfd *, char *, int *, int, ...);
 #define elf_backend_write_core_note	_bfd_aarch64_elf_write_core_note
 
 extern bfd *
-_bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *,
-					    uint32_t *);
+_bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *, uint32_t *,
+					    aarch64_gcs_report,
+					    aarch64_gcs_type);
 
 extern enum elf_property_kind
 _bfd_aarch64_elf_parse_gnu_properties (bfd *, unsigned int,
@@ -157,6 +181,8 @@  extern bool
 _bfd_aarch64_elf_merge_gnu_properties (struct bfd_link_info *, bfd *,
 				       elf_property *, elf_property *,
 				       uint32_t);
+extern void
+_bfd_aarch64_elf_check_gcs_report (aarch64_gcs_report, bfd *);
 
 extern void
 _bfd_aarch64_elf_link_fixup_gnu_properties (struct bfd_link_info *,
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 0f8dc1b9716..f905a10b194 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -21246,6 +21246,10 @@  decode_aarch64_feature_1_and (unsigned int bitmask)
 	  printf ("PAC");
 	  break;
 
+	case GNU_PROPERTY_AARCH64_FEATURE_1_GCS:
+	  printf ("GCS");
+	  break;
+
 	default:
 	  printf (_("<unknown: %x>"), bit);
 	  break;
diff --git a/include/elf/common.h b/include/elf/common.h
index c9920e7731a..c4eb33bf308 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -1037,6 +1037,7 @@ 
 
 #define GNU_PROPERTY_AARCH64_FEATURE_1_BTI	(1U << 0)
 #define GNU_PROPERTY_AARCH64_FEATURE_1_PAC	(1U << 1)
+#define GNU_PROPERTY_AARCH64_FEATURE_1_GCS	(1U << 2)
 
 /* Values used in GNU .note.ABI-tag notes (NT_GNU_ABI_TAG).  */
 #define GNU_ABI_TAG_LINUX	0
diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
index aa2859d4f73..ba536b0e379 100644
--- a/ld/emultempl/aarch64elf.em
+++ b/ld/emultempl/aarch64elf.em
@@ -36,6 +36,15 @@  static erratum_84319_opts fix_erratum_843419 = ERRAT_NONE;
 static int no_apply_dynamic_relocs = 0;
 static aarch64_plt_type plt_type = PLT_NORMAL;
 static aarch64_enable_bti_type bti_type = BTI_NONE;
+static aarch64_gcs_type gcs_type = GCS_IMPLICIT;
+static aarch64_gcs_report gcs_report = GCS_NONE;
+
+#define COMPILE_TIME_STRLEN(s) \
+  (sizeof(s) - 1)
+#define EGR  "gcs-report"
+#define EG   "gcs"
+#define EGR_LEN COMPILE_TIME_STRLEN (EGR)
+#define EG_LEN  COMPILE_TIME_STRLEN (EG)
 
 static void
 gld${EMULATION_NAME}_before_parse (void)
@@ -321,9 +330,11 @@  aarch64_elf_create_output_section_statements (void)
       return;
     }
 
-  aarch64_bti_pac_info bp_info;
-  bp_info.plt_type = plt_type;
-  bp_info.bti_type = bti_type;
+  aarch64_gnu_prop_info gnu_prop_info;
+  gnu_prop_info.plt_type = plt_type;
+  gnu_prop_info.bti_type = bti_type;
+  gnu_prop_info.gcs_type = gcs_type;
+  gnu_prop_info.gcs_report = gcs_report;
 
   bfd_elf${ELFSIZE}_aarch64_set_options (link_info.output_bfd, &link_info,
 				 no_enum_size_warning,
@@ -331,7 +342,7 @@  aarch64_elf_create_output_section_statements (void)
 				 pic_veneer,
 				 fix_erratum_835769, fix_erratum_843419,
 				 no_apply_dynamic_relocs,
-				 bp_info);
+				 gnu_prop_info);
 
   stub_file = lang_add_input_file ("linker stubs",
 				   lang_input_file_is_fake_enum,
@@ -398,16 +409,51 @@  PARSE_AND_LIST_OPTIONS='
   fprintf (file, _("  --no-apply-dynamic-relocs    Do not apply link-time values for dynamic relocations\n"));
   fprintf (file, _("  -z force-bti                  Turn on Branch Target Identification mechanism and generate PLTs with BTI. Generate warnings for missing BTI on inputs\n"));
   fprintf (file, _("  -z pac-plt                    Protect PLTs with Pointer Authentication.\n"));
+  fprintf (file, _("\
+  -z gcs[=always|never|implicit]		Turn on Guarded Control Stack(gcs) mechanism on the output.\n\
+							implicit(default): deduce gcs from input objects.\n\
+							always: always marks the output with gcs.\n\
+							never: never marks the output with gcs.\n"));
+  fprintf (file, _("\
+  -z gcs-report[=none|warning|error]	Emit warning/error on mismatch of gcs marking between input objects and ouput.\n\
+							none (default): Does not emit any warning/error messages.\n\
+							warning: Emit warning when the input objects are missing gcs markings\n\
+								 and output have gcs marking.\n\
+							error: Emit error when the input objects are missing gcs markings\n\
+							       and output have gcs marking.\n"));
+
 '
 
 PARSE_AND_LIST_ARGS_CASE_Z_AARCH64='
-      else if (strcmp (optarg, "force-bti") == 0)
+     else if (strcmp (optarg, "force-bti") == 0)
 	{
 	  plt_type |= PLT_BTI;
 	  bti_type = BTI_WARN;
 	}
-      else if (strcmp (optarg, "pac-plt") == 0)
+     else if (strcmp (optarg, "pac-plt") == 0)
 	plt_type |= PLT_PAC;
+     else if (strncmp (optarg, EGR, EGR_LEN) == 0)
+	{
+	  if (strlen (optarg) == EGR_LEN || strcmp (optarg + EGR_LEN, "=none") == 0)
+	    gcs_report = GCS_NONE;
+	  else if (strcmp (optarg + EGR_LEN, "=warning") == 0)
+	    gcs_report = GCS_WARN;
+	  else if (strcmp (optarg + EGR_LEN, "=error") == 0)
+	    gcs_report = GCS_ERROR;
+	  else
+	    einfo (_("%P: error: unrecognized: `%s'\''\n"), optarg);
+	}
+     else if (strncmp (optarg, EG, EG_LEN) == 0)
+	{
+	  if (strlen (optarg) == EG_LEN || strcmp (optarg + EG_LEN, "=always") == 0)
+	    gcs_type = GCS_ALWAYS;
+	  else if (strcmp (optarg + EG_LEN, "=never") == 0)
+	    gcs_type = GCS_NEVER;
+	  else if (strcmp (optarg + EG_LEN, "=implicit") == 0)
+	    gcs_type = GCS_IMPLICIT;
+	  else
+	    einfo (_("%P: error: unrecognized: `%s'\''\n"), optarg);
+	}
 '
 PARSE_AND_LIST_ARGS_CASE_Z="$PARSE_AND_LIST_ARGS_CASE_Z $PARSE_AND_LIST_ARGS_CASE_Z_AARCH64"
 
diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp
index c7d97f3ced9..d5a208f6768 100644
--- a/ld/testsuite/ld-aarch64/aarch64-elf.exp
+++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp
@@ -487,3 +487,26 @@  if { [supports_dt_relr] } {
 if { ![skip_sframe_tests] } {
   run_dump_test "sframe-simple-1"
 }
+
+run_dump_test "property-gcs1"
+run_dump_test "property-gcs2"
+run_dump_test "property-gcs3"
+run_dump_test "property-gcs4"
+run_dump_test "property-gcs5"
+run_dump_test "property-gcs6"
+run_dump_test "property-gcs7"
+run_dump_test "property-gcs8"
+run_dump_test "property-gcs9"
+run_dump_test "property-gcs10"
+run_dump_test "property-gcs11"
+run_dump_test "property-gcs12"
+run_dump_test "property-gcs13"
+run_dump_test "property-gcs14"
+run_dump_test "property-gcs15"
+run_dump_test "property-gcs16"
+run_dump_test "property-gcs17"
+run_dump_test "property-gcs18"
+run_dump_test "property-gcs19"
+run_dump_test "property-gcs20"
+run_dump_test "property-gcs21"
+run_dump_test "property-gcs22"
diff --git a/ld/testsuite/ld-aarch64/property-bti-pac1.d b/ld/testsuite/ld-aarch64/property-bti-pac1.d
index 59fa695165a..c28a0cbf850 100644
--- a/ld/testsuite/ld-aarch64/property-bti-pac1.d
+++ b/ld/testsuite/ld-aarch64/property-bti-pac1.d
@@ -8,4 +8,4 @@ 
 Displaying notes found in: .note.gnu.property
 [ 	]+Owner[ 	]+Data size[ 	]+Description
   GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
-      Properties: AArch64 feature: BTI, PAC
+      Properties: AArch64 feature: BTI, PAC, GCS
diff --git a/ld/testsuite/ld-aarch64/property-bti-pac1.s b/ld/testsuite/ld-aarch64/property-bti-pac1.s
index 414c9277f1d..42156917d58 100644
--- a/ld/testsuite/ld-aarch64/property-bti-pac1.s
+++ b/ld/testsuite/ld-aarch64/property-bti-pac1.s
@@ -12,6 +12,20 @@  _start:
 	.long 5f - 2f		/* data length */
 	.long 5			/* note type */
 0:	.asciz "GNU"		/* vendor name */
+1:
+	.p2align 3
+2:	.long 0xc0000000	/* pr_type.  */
+	.long 4f - 3f		/* pr_datasz.  */
+3:
+	.long 0x4		/* GCS.  */
+4:
+	.p2align 3
+5:
+	.p2align 3
+	.long 1f - 0f		/* name length */
+	.long 5f - 2f		/* data length */
+	.long 5			/* note type */
+0:	.asciz "GNU"		/* vendor name */
 1:
 	.p2align 3
 2:	.long 0xc0000000	/* pr_type.  */
diff --git a/ld/testsuite/ld-aarch64/property-gcs.s b/ld/testsuite/ld-aarch64/property-gcs.s
new file mode 100644
index 00000000000..bc7e66e8933
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs.s
@@ -0,0 +1,25 @@ 
+	.text
+	.globl _start
+	.type _start,@function
+_start:
+	mov x1, #2
+.ifndef __mult__
+	bl foo
+.endif
+.ifdef __property_gcs__
+	.section ".note.gnu.property", "a"
+	.p2align 3
+	.long 1f - 0f		/* name length */
+	.long 5f - 2f		/* data length */
+	.long 5			/* note type */
+0:	.asciz "GNU"		/* vendor name */
+1:
+	.p2align 3
+2:	.long 0xc0000000	/* pr_type.  */
+	.long 4f - 3f		/* pr_datasz.  */
+3:
+	.long 0x4		/* GCS.  */
+4:
+	.p2align 3
+5:
+.endif
diff --git a/ld/testsuite/ld-aarch64/property-gcs1.d b/ld/testsuite/ld-aarch64/property-gcs1.d
new file mode 100644
index 00000000000..c724ac56ca3
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs1.d
@@ -0,0 +1,6 @@ 
+#name: GNU Property (input without gcs)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -shared
+#readelf: -n
diff --git a/ld/testsuite/ld-aarch64/property-gcs10.d b/ld/testsuite/ld-aarch64/property-gcs10.d
new file mode 100644
index 00000000000..3a2c992f974
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs10.d
@@ -0,0 +1,6 @@ 
+#name: GNU Property (input without gcs output forced with gcs=always gcs-report=error)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs=always -z gcs-report=error
+#error: .*property-gcs.*: error: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
diff --git a/ld/testsuite/ld-aarch64/property-gcs11.d b/ld/testsuite/ld-aarch64/property-gcs11.d
new file mode 100644
index 00000000000..094b9be2582
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs11.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs12.d b/ld/testsuite/ld-aarch64/property-gcs12.d
new file mode 100644
index 00000000000..355544775f3
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs12.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs=always)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs=always
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs13.d b/ld/testsuite/ld-aarch64/property-gcs13.d
new file mode 100644
index 00000000000..c6543ba168c
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs13.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs gcs-report=none)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs -z gcs-report=none
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs14.d b/ld/testsuite/ld-aarch64/property-gcs14.d
new file mode 100644
index 00000000000..1c612b5c9bd
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs14.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs gcs-report=warning)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs -z gcs-report=warning
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs15.d b/ld/testsuite/ld-aarch64/property-gcs15.d
new file mode 100644
index 00000000000..7e46e8eae2f
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs15.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs gcs-report=error)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs -z gcs-report=error
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs16.d b/ld/testsuite/ld-aarch64/property-gcs16.d
new file mode 100644
index 00000000000..c20cbe531be
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs16.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs=always gcs-report=none)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs=always -z gcs-report=none
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs17.d b/ld/testsuite/ld-aarch64/property-gcs17.d
new file mode 100644
index 00000000000..2ee3f103701
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs17.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs=always gcs-report=warning)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs=always -z gcs-report=warning
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs18.d b/ld/testsuite/ld-aarch64/property-gcs18.d
new file mode 100644
index 00000000000..6c45d77c179
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs18.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs=always gcs-report=error)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs=always -z gcs-report=error
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs19.d b/ld/testsuite/ld-aarch64/property-gcs19.d
new file mode 100644
index 00000000000..588af48859b
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs19.d
@@ -0,0 +1,6 @@ 
+#name: GNU Property (input without gcs output forced with gcs=never)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs=never
+#readelf: -n
diff --git a/ld/testsuite/ld-aarch64/property-gcs2.d b/ld/testsuite/ld-aarch64/property-gcs2.d
new file mode 100644
index 00000000000..ed545a180b3
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs2.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -shared
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs2.s b/ld/testsuite/ld-aarch64/property-gcs2.s
new file mode 100644
index 00000000000..6db7d8396c8
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs2.s
@@ -0,0 +1,33 @@ 
+	.text
+	.global	foo
+	.type	foo, %function
+foo:
+	sub	sp, sp, #16
+	mov	w0, 9
+	str	w0, [sp, 12]
+	ldr	w0, [sp, 12]
+	add	w0, w0, 4
+	str	w0, [sp, 12]
+	nop
+	add	sp, sp, 16
+	ret
+	.size	foo, .-foo
+	.global	bar
+	.type	bar, %function
+.ifdef __property_gcs__
+	.section ".note.gnu.property", "a"
+	.p2align 3
+	.long 1f - 0f		/* name length */
+	.long 5f - 2f		/* data length */
+	.long 5			/* note type */
+0:	.asciz "GNU"		/* vendor name */
+1:
+	.p2align 3
+2:	.long 0xc0000000	/* pr_type.  */
+	.long 4f - 3f		/* pr_datasz.  */
+3:
+	.long 0x4		/* GCS.  */
+4:
+	.p2align 3
+5:
+.endif
diff --git a/ld/testsuite/ld-aarch64/property-gcs20.d b/ld/testsuite/ld-aarch64/property-gcs20.d
new file mode 100644
index 00000000000..779e845f680
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs20.d
@@ -0,0 +1,6 @@ 
+#name: GNU Property (input without gcs output forced with gcs=implicit)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs=implicit
+#readelf: -n
diff --git a/ld/testsuite/ld-aarch64/property-gcs21.d b/ld/testsuite/ld-aarch64/property-gcs21.d
new file mode 100644
index 00000000000..dbfb5ec0264
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs21.d
@@ -0,0 +1,6 @@ 
+#name: GNU Property (input with gcs output forced with gcs=never)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs=never
+#readelf: -n
diff --git a/ld/testsuite/ld-aarch64/property-gcs22.d b/ld/testsuite/ld-aarch64/property-gcs22.d
new file mode 100644
index 00000000000..dd92399fc4f
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs22.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input with gcs output forced with gcs=implicit)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0 -defsym __property_gcs__=1
+#ld: -z gcs=implicit
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs3.d b/ld/testsuite/ld-aarch64/property-gcs3.d
new file mode 100644
index 00000000000..0ea2d7ed79c
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs3.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input without gcs output forced with gcs)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs4.d b/ld/testsuite/ld-aarch64/property-gcs4.d
new file mode 100644
index 00000000000..0669d64b530
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs4.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input without gcs output forced with gcs=always)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs=always
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs5.d b/ld/testsuite/ld-aarch64/property-gcs5.d
new file mode 100644
index 00000000000..df8643a9d18
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs5.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input without gcs output forced with gcs gcs-report=none)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs -z gcs-report=none
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs6.d b/ld/testsuite/ld-aarch64/property-gcs6.d
new file mode 100644
index 00000000000..3646e8a58ff
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs6.d
@@ -0,0 +1,12 @@ 
+#name: GNU Property (input without gcs output forced with gcs gcs-report=warning)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs -z gcs-report=warning
+#readelf: -n
+#warning: .*property-gcs.*: warning: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs7.d b/ld/testsuite/ld-aarch64/property-gcs7.d
new file mode 100644
index 00000000000..ba58fe24ad2
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs7.d
@@ -0,0 +1,6 @@ 
+#name: GNU Property (input without gcs output forced with gcs gcs-report=error)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs -z gcs-report=error
+#error: .*property-gcs.*: error: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
diff --git a/ld/testsuite/ld-aarch64/property-gcs8.d b/ld/testsuite/ld-aarch64/property-gcs8.d
new file mode 100644
index 00000000000..f442b71aee3
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs8.d
@@ -0,0 +1,11 @@ 
+#name: GNU Property (input without gcs output forced with gcs=always gcs-report=none)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs=always -z gcs-report=none
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS
diff --git a/ld/testsuite/ld-aarch64/property-gcs9.d b/ld/testsuite/ld-aarch64/property-gcs9.d
new file mode 100644
index 00000000000..82b93ec0850
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/property-gcs9.d
@@ -0,0 +1,12 @@ 
+#name: GNU Property (input without gcs output forced with gcs=always gcs-report=warning)
+#source: property-gcs.s
+#alltargets: [check_shared_lib_support] *linux*
+#as: -march=armv9.4-a+gcs -defsym __mult__=0
+#ld: -z gcs=always -z gcs-report=warning
+#readelf: -n
+#warning: .*property-gcs.*: warning: GCS turned on by -z gcs on the output when all inputs do not have GCS in NOTE section.
+
+Displaying notes found in: .note.gnu.property
+[ 	]+Owner[ 	]+Data size[ 	]+Description
+  GNU                  0x00000010	NT_GNU_PROPERTY_TYPE_0
+      Properties: AArch64 feature: GCS