[PATCH v1] aarch64: silence GCS warnings on shared libraries for -z gcs=implicit
Matthieu Longo
matthieu.longo@arm.com
Tue Jan 6 09:42:27 GMT 2026
On 24/12/2025 22:35, Alice Carlotti wrote:
> On Tue, Dec 23, 2025 at 02:02:18PM +0000, Matthieu Longo wrote:
>> Dynamic library incompatibilities should not be reported when
>> '-z gcs=implicit' is used and no '-z gcs-report-dynamic' option is
>> provided. However, '-z gcs=always' continues to default to reporting
>> these as warnings.
>>
>> Binary Linux distributions do not rebuild all packages from scratch
>> when rolling out a new feature or creating a new release; only
>> modified packages get rebuilt. In the context of GCS deployment, this
>> meant that some packages were rebuilt with GCS enabled while their
>> dependencies were not yet GCS-compatible, resulting in warnings. These
>> warnings caused build failures for packages that treat linker warnings
>> as errors.
>>
>> This patch preserves the existing inheritance of the value from
>> '-z gcs-report' if the option '-z gcs-report-dynamic' is not set on
>> the command line, but it forces the value of '-z gcs-report-dynamic'
>> to 'none' when '-z gcs=implicit' is used.
>> It also adapts the existing tests for '-z gcs=implicit' to reflect
>> this change, and adds new tests covering cases with no report option
>> provided, or with '-z gcs-report-dynamic' explicitly set.
>
> This new logic should address the reported problem, but it seems to be patching
> behaviour for a specific set of options without considering the overall
> consistency of the interface. Compare
>
> -z gcs=always/never, -z gcs-report=...
> - then gcs-report-dynamic inherits from gcs-report (capped at WARN);
>
> whereas
>
> -z gcs=implicit, -z gcs-report=...
> - then gcs-report-dynamic is set to NONE.
>
> I don't know what would be better though - should we disable all inheritance
> from gcs-report? Or use an UNSET default for gcs-report and only inherit from
> an explicit option? Or is the proposed inconsistency actually a good choice?
>
As a summary of the current behavior, GCC does not provide any GCS flags to ld, and relies on the default value of '-z gcs', i.e. 'implicit'. Since 'implicit' is the default, GCS reports also have to be emitted by default, i.e. the default value of '-z gcs-report' is currently set to 'warning'. The distributions are happy of this behavior.
Now, the complaint at the origin of this patch was the requirement of setting '-z gcs-report-dynamic=none', which defaulted to 'warning' due to the inheritance from '-z gcs-report'. The distributions want to avoid setting any additional flag.
I agree with you that the preservation of the existing inheritance makes the interface awkward.
The second approach, as explained above, does not match the requirements of the distributions.
Keeping the inconsistency might be acceptable, but your point is fair. As a user, this new behavior looks confusing.
The first approach, i.e. disabling all inheritance from '-z gcs-report' for '-z gcs-report-dynamic', and setting the default value to 'none', seems the way to go.
@Richard Earnshaw:
Do you have any objection to Alice's proposal ?
>> ---
>> bfd/elfnn-aarch64.c | 29 ++++++++++++++++---
>> ld/ld.texi | 4 ++-
>> .../ld-aarch64/protections/gcs-dynamic-3-a.d | 15 +++-------
>> .../ld-aarch64/protections/gcs-dynamic-3-b.d | 6 ++--
>> .../ld-aarch64/protections/gcs-dynamic-3-c.d | 6 ++--
>> .../ld-aarch64/protections/gcs-dynamic-3-d.d | 12 ++++++++
>> .../ld-aarch64/protections/gcs-dynamic-3-e.d | 12 ++++++++
>> .../ld-aarch64/protections/gcs-dynamic-3-f.d | 12 ++++++++
>> .../ld-aarch64/protections/gcs-dynamic-3-g.d | 12 ++++++++
>> .../ld-aarch64/protections/gcs-dynamic-3-h.d | 12 ++++++++
>> 10 files changed, 98 insertions(+), 22 deletions(-)
>> create mode 100644 ld/testsuite/ld-aarch64/protections/gcs-dynamic-3-d.d
>> create mode 100644 ld/testsuite/ld-aarch64/protections/gcs-dynamic-3-e.d
>> create mode 100644 ld/testsuite/ld-aarch64/protections/gcs-dynamic-3-f.d
>> create mode 100644 ld/testsuite/ld-aarch64/protections/gcs-dynamic-3-g.d
>> create mode 100644 ld/testsuite/ld-aarch64/protections/gcs-dynamic-3-h.d
>>
>> diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
>> index b104e40d1ab..8304f51997c 100644
>> --- a/bfd/elfnn-aarch64.c
>> +++ b/bfd/elfnn-aarch64.c
>> @@ -5061,10 +5061,31 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
>> libraries, '-z gcs-report-dynamic=error' will have to be specified
>> explicitly. */
>> if (sw_protections->gcs_report_dynamic == MARKING_UNSET)
>> - elf_aarch64_tdata (output_bfd)->sw_protections.gcs_report_dynamic
>> - = (sw_protections->gcs_report == MARKING_ERROR)
>> - ? MARKING_WARN
>> - : sw_protections->gcs_report;
>> + {
>> + aarch64_feature_marking_report *gcs_report_dynamic
>> + = &elf_aarch64_tdata (output_bfd)->sw_protections.gcs_report_dynamic;
>
> This seems like an unnecessarily indirect way to set the struct member. It
> would be clearer to assign to non-pointer aarch64_feature_marking_report value,
> and then copy that into the destination struct at the the end.
>
Fixed in the next revision as below.
diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 8304f51997c..aae7ba084f7 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -5062,8 +5062,7 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
explicitly. */
if (sw_protections->gcs_report_dynamic == MARKING_UNSET)
{
- aarch64_feature_marking_report *gcs_report_dynamic
- = &elf_aarch64_tdata (output_bfd)->sw_protections.gcs_report_dynamic;
+ aarch64_feature_marking_report gcs_report_dynamic;
/* Dynamic library incompatibilities must not be reported when
'-z gcs=implicit' is used and no '-z gcs-report-dynamic' option is
@@ -5079,12 +5078,15 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
warnings caused build failures for packages that treat linker warnings
as errors. */
if (sw_protections->gcs_type == GCS_IMPLICIT)
- *gcs_report_dynamic = MARKING_NONE;
+ gcs_report_dynamic = MARKING_NONE;
else
- *gcs_report_dynamic
+ gcs_report_dynamic
= (sw_protections->gcs_report == MARKING_ERROR)
? MARKING_WARN
: sw_protections->gcs_report;
+
+ elf_aarch64_tdata (output_bfd)->sw_protections.gcs_report_dynamic
+ = gcs_report_dynamic;
}
elf_aarch64_tdata (output_bfd)->n_bti_issues = 0;
>> +
>> + /* Dynamic library incompatibilities must not be reported when
>> + '-z gcs=implicit' is used and no '-z gcs-report-dynamic' option is
>> + provided. Instead '-z gcs-report-dynamic' must default to
>> + 'MARKING_NONE'. However, '-z gcs=always' continues to default to
>> + reporting these as warnings.
>> +
>> + Binary Linux distributions do not rebuild all packages from scratch
>> + when rolling out a new feature or creating a new release; only modified
>> + packages get rebuilt. In the context of GCS deployment, this meant
>> + that some packages were rebuilt with GCS enabled while their
>> + dependencies were not yet GCS-compatible, resulting in warnings. These
>> + warnings caused build failures for packages that treat linker warnings
>> + as errors. */
>> + if (sw_protections->gcs_type == GCS_IMPLICIT)
>> + *gcs_report_dynamic = MARKING_NONE;
>> + else
>> + *gcs_report_dynamic
>> + = (sw_protections->gcs_report == MARKING_ERROR)
>> + ? MARKING_WARN
>> + : sw_protections->gcs_report;
>> + }
>>
>> elf_aarch64_tdata (output_bfd)->n_bti_issues = 0;
>> elf_aarch64_tdata (output_bfd)->n_gcs_issues = 0;
>> diff --git a/ld/ld.texi b/ld/ld.texi
>> index 8ff65ec64a7..49a5a95ab9e 100644
>> --- a/ld/ld.texi
>> +++ b/ld/ld.texi
>> @@ -8397,7 +8397,9 @@ omitted, it inherits the value of @samp{-z gcs-report}. However, the inherited
>> value is capped to @samp{warning} as some user might want to only report errors
>> in the currently built module, and not the shared dependencies. It is therefore
>> necessary to use an explicit @samp{-z gcs-report-dynamic=error} option if you
>> -want the linker to error on GCS issues in the shared libraries.
>> +want the linker to error on GCS issues in the shared libraries. Additionally,
>> +if @samp{-z gcs=implicit} is provided and the option is omitted, it will default
>> +to @samp{none}.
>> @itemize
>> @item @samp{none} disables any warning messages.
>> @item @samp{warning} emits warning messages when dynamic objects are missing
More information about the Binutils
mailing list