[v0,14/15] ld's BAs merge saga, step two: AArch64 backend's support for AEABI-specific BAs

Message ID 20250310175131.1217374-15-matthieu.longo@arm.com
State New
Headers
Series AArch64 AEABI build attributes (a.k.a. object attributes v2) |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

Matthieu Longo March 10, 2025, 5:51 p.m. UTC
  - declare known subsections and tags for AEABI build attributes.
- define AArch64 backend handlers for:
  - obj_attr_subsection_v2_match_known
  - obj_attr_v2_default_value
  - obj_attr_v2_tag_merge
  - setup_build_attributes
  - translate_relevant_gnu_props_to_obj_attrs
  - translate_relevant_obj_attrs_to_gnu_props
- set global build attributes from command-line options for BTI and GCS.
- translate GNU_PROPERTY_AARCH64_FEATURE_1_AND in input files to its BAs
  equivalents.
- report incompatibilities for BTI and GCS.
- set BTI PLT type depending on the merge result.
- translate BAs in subsection "aeabi_feature_and_bits" to
  GNU_PROPERTY_AARCH64_FEATURE_1_AND as GNU properties are required for
  the runtime linker (it does not understand BAs yet).
---
 bfd/elfnn-aarch64.c | 101 ++++++++++++++-
 bfd/elfxx-aarch64.c | 306 ++++++++++++++++++++++++++++++++++++++++++--
 bfd/elfxx-aarch64.h |  36 ++++++
 3 files changed, 426 insertions(+), 17 deletions(-)
  

Patch

diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 21fc99c4c98..9bb36fd550c 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -5023,27 +5023,54 @@  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;
 
+  /* The global list of build attributes used to save requested features from
+     the command-line options.  */
+  obj_attr_subsection_v2 *attrs_subsection =
+    _bfd_elf_obj_attr_subsection_v2_init ("aeabi_feature_and_bits",
+      SUBSEC_PUBLIC, true, ULEB128);
+
   /* Note: gnu_property_aarch64_feature_1_and was initialized to 0 by
      bfd_zalloc().  */
   if (sw_protections->plt_type & PLT_BTI)
-    elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
-      |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+    {
+      elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
+	|= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+      _bfd_aarch64_record_obj_attr_v2 (attrs_subsection, Tag_Feature_BTI, true);
+    }
+
+  /* Note: Contrarilly to PLT_BTI, (sw_protections->plt_type & PLT_PAC) == true
+     does not mean that Tag_Feature_PAC should also be set to true.  The PAC
+     build attribute is only there to mirror the existing GNU properties.
+     Adding a property for PAC was in retrospect a mistake as it does not carry
+     valuable information.  The only use it does have is informational: if the
+     property is set on the output ELF object, then someone went to the trouble
+     of enabling it on all the input objects.  */
 
   switch (sw_protections->gcs_type)
     {
     case GCS_ALWAYS:
-      elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
-	|= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      {
+	elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
+	  |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+	_bfd_aarch64_record_obj_attr_v2 (attrs_subsection, Tag_Feature_GCS, true);
+      }
       break;
     case GCS_NEVER:
-      elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
-	&= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      {
+	elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
+	  &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+	_bfd_aarch64_record_obj_attr_v2 (attrs_subsection, Tag_Feature_GCS, false);
+      }
       break;
     case GCS_IMPLICIT:
       /* GCS feature on the output bfd will be deduced from input objects.  */
       break;
     }
 
+  if (attrs_subsection->size > 0)
+    LINKED_LIST_APPEND(obj_attr_subsection_v2) (
+      &elf_obj_attr_subsections (output_bfd), attrs_subsection);
+
   elf_aarch64_tdata (output_bfd)->sw_protections = *sw_protections;
   /* Inherit the value from '-z gcs-report' if the option '-z gcs-report-dynamic'
      was not set on the command line.  However, the inheritance mechanism is
@@ -10395,7 +10422,8 @@  elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
   return true;
 }
 
-/* Check if BTI enabled PLTs are needed.  Returns the type needed.  */
+/* Check if BTI-enabled (and/or PAC-enabled) PLTs are needed.
+   Returns the type needed.  */
 static aarch64_plt_type
 get_plt_type (bfd *abfd)
 {
@@ -10515,6 +10543,41 @@  elfNN_aarch64_backend_symbol_processing (bfd *abfd, asymbol *sym)
     sym->flags |= BSF_KEEP;
 }
 
+/* Implement elf_backend_setup_build_attributes for AArch64.  */
+static bfd *
+elfNN_aarch64_link_setup_build_attributes (struct bfd_link_info *info)
+{
+  bfd *pbfd = _bfd_aarch64_elf_link_setup_build_attributes (info);
+
+  struct elf_aarch64_obj_tdata * tdata = elf_aarch64_tdata (info->output_bfd);
+
+  /* When BTI is forced on the command line, information flows from plt_type to
+     the frozen build attributes (a.k.a FROZEN), so plt_type has already been
+     set and FROZEN don't have any effect on plt_type.
+     Whereas if BTI is inferred from the input bfds, information flows from
+     output build attributes to plt_type.  If the property GNU_PROPERTY_AARCH64
+     _FEATURE_1_BTI has been set on all the input bfds, then BTI is set on the
+     output bfd and plt_type is updated accordingly.
+
+     Important note: this is not true for GNU_PROPERTY_AARCH64_FEATURE_1_PAC.
+     See more explanation in bfd_elfNN_aarch64_set_options.  */
+  obj_attr_subsection_v2 *aeabi_feature_and_bits_subsec =
+    obj_attr_subsection_v2_find_by_name (
+      elf_obj_attr_subsections (info->output_bfd).first_,
+      "aeabi_feature_and_bits", true);
+  if (aeabi_feature_and_bits_subsec != NULL)
+    {
+      obj_attr_v2 *attr_bti = obj_attr_v2_find_by_tag
+	(aeabi_feature_and_bits_subsec, Tag_Feature_BTI, true);
+      if (attr_bti && attr_bti->vals.uint_val == 1)
+	tdata->sw_protections.plt_type |= PLT_BTI;
+    }
+
+  setup_plt_values (info, tdata->sw_protections.plt_type);
+
+  return pbfd;
+}
+
 /* Implement elf_backend_setup_gnu_properties for AArch64.  It serves as a
    wrapper function for _bfd_aarch64_elf_link_setup_gnu_properties to account
    for the effect of GNU properties of the output_bfd.  */
@@ -10732,9 +10795,27 @@  const struct elf_size_info elfNN_aarch64_size_info =
 #define elf_backend_symbol_processing		\
   elfNN_aarch64_backend_symbol_processing
 
+#define elf_backend_setup_build_attributes	\
+  elfNN_aarch64_link_setup_build_attributes
+
 #define elf_backend_setup_gnu_properties	\
   elfNN_aarch64_link_setup_gnu_properties
 
+#define elf_backend_translate_relevant_gnu_props_to_obj_attrs \
+  _bfd_aarch64_translate_relevant_gnu_props_to_obj_attrs
+
+#define elf_backend_translate_relevant_obj_attrs_to_gnu_props \
+  _bfd_aarch64_translate_relevant_obj_attrs_to_gnu_props
+
+#define elf_backend_obj_attr_subsection_v2_match_known \
+  _bfd_aarch64_obj_attr_subsection_v2_match_known
+
+#define elf_backend_obj_attr_v2_default_value	\
+  _bfd_aarch64_obj_attr_v2_default_value
+
+#define elf_backend_obj_attr_v2_tag_merge	\
+  _bfd_aarch64_obj_attr_v2_tag_merge
+
 #define elf_backend_merge_gnu_properties	\
   elfNN_aarch64_merge_gnu_properties
 
@@ -10775,6 +10856,12 @@  const struct elf_size_info elfNN_aarch64_size_info =
 #define elf_backend_obj_attrs_section_type	SHT_AARCH64_ATTRIBUTES
 #undef	elf_backend_obj_attrs_version
 #define elf_backend_obj_attrs_version		2
+/* Object attributes v2 specific values.  */
+#undef	elf_backend_obj_attr_v2_known_subsections
+#define elf_backend_obj_attr_v2_known_subsections \
+  aarch64_obj_attr_v2_known_subsections
+#undef	elf_backend_obj_attr_v2_known_subsections_size
+#define elf_backend_obj_attr_v2_known_subsections_size 2
 
 #include "elfNN-target.h"
 
diff --git a/bfd/elfxx-aarch64.c b/bfd/elfxx-aarch64.c
index 45a02058e81..31810f9eb19 100644
--- a/bfd/elfxx-aarch64.c
+++ b/bfd/elfxx-aarch64.c
@@ -21,6 +21,7 @@ 
 #include "sysdep.h"
 #include "bfd.h"
 #include "elf-bfd.h"
+#include "elf/aarch64.h"
 #include "elfxx-aarch64.h"
 #include "libbfd.h"
 #include <stdarg.h>
@@ -868,6 +869,279 @@  _bfd_aarch64_elf_check_gnu_properties_linked_dynamic_objects (
 	pbfd);
 }
 
+/* Note: this array has to be sorted.  */
+static const known_tag_v2 known_tags_aeabi_feature_and_bits [] =
+{
+  {Tag_Feature_BTI, OBJ_ATTR_TAG_v2(Feature_BTI), .default_value.uint_val = 0},
+  {Tag_Feature_PAC, OBJ_ATTR_TAG_v2(Feature_PAC), .default_value.uint_val = 0},
+  {Tag_Feature_GCS, OBJ_ATTR_TAG_v2(Feature_GCS), .default_value.uint_val = 0},
+};
+/* This is a required subsection to use PAuthABI (which is currently
+   unsupported by GCC). A value of 0 for any the tags below means that
+   the user did not permit this entity to use the PAuthABI.
+   Note: this array has to be sorted.  */
+static const known_tag_v2 known_tags_aeabi_pauthabi [] =
+{
+  {Tag_PAuth_Platform, OBJ_ATTR_TAG_v2(PAuth_Platform), .default_value.uint_val = 0},
+  {Tag_PAuth_Schema, OBJ_ATTR_TAG_v2(PAuth_Schema), .default_value.uint_val = 0},
+};
+
+/* Note: this array is exported by the backend, and needs to be sorted.  */
+const known_subsection_v2 aarch64_obj_attr_v2_known_subsections[] =
+{
+  {
+    .subsec_name = "aeabi_feature_and_bits",
+    .known_tags = known_tags_aeabi_feature_and_bits,
+    .optional = true,
+    .encoding = ULEB128,
+    .len = sizeof (known_tags_aeabi_feature_and_bits) / sizeof (known_tag_v2),
+  },
+  {
+    .subsec_name = "aeabi_pauthabi",
+    .known_tags = known_tags_aeabi_pauthabi,
+    .optional = false,
+    .encoding = ULEB128,
+    .len = sizeof (known_tags_aeabi_pauthabi) / sizeof (known_tag_v2),
+  },
+};
+
+void
+_bfd_aarch64_record_obj_attr_v2 (obj_attr_subsection_v2 *subsec,
+				 Tag_Feature_XXX feature_tag,
+				 uint32_t value)
+{
+  union obj_attr_value_v2 data;
+  data.uint_val = value;
+  obj_attr_v2 *attr = _bfd_elf_obj_attr_v2_init (feature_tag, data);
+  LINKED_LIST_APPEND(obj_attr_v2) (subsec, attr);
+}
+
+/* Translate the relevant GNU properties to object attributes v2.  */
+void
+_bfd_aarch64_translate_relevant_gnu_props_to_obj_attrs (
+  bfd *abfd,
+  elf_property_list *p)
+{
+  void
+  _record_tag_value (obj_attr_subsection_v2 *subsec,
+		     Tag_Feature_XXX tag,
+		     bool value)
+  {
+    obj_attr_v2 *attr;
+    attr = obj_attr_v2_find_by_tag (subsec, tag, false);
+    if (attr != NULL)
+      {
+	if (attr->vals.uint_val != value)
+	  {
+	    /* translate_relevant_gnu_props_to_obj_attrs is only called when
+	       parsing .note.gnu.property section, and this happens before the
+	       parsing of the build attributes.
+	       If we find an existing value for the given object attributes and
+	       this value is different from the new one, it can mean two things:
+		 - either the values are conflicting, and we need to raise an
+		 error.
+		 - either there are several GNU properties AARCH64_FEATURE_1_AND
+		 which were recorded, but its final value is the result of the
+		 merge of those separate values.
+	       For now, only case 2 occurs.  */
+	    uint32_t merged_val = attr->vals.uint_val | value;
+	    _bfd_aarch64_record_obj_attr_v2 (subsec, tag, merged_val);
+	  }
+	// else: nothing to do
+      }
+    else
+      _bfd_aarch64_record_obj_attr_v2 (subsec, tag, value);
+  }
+
+  if (p->property.pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
+    {
+      elf_property *prop = &p->property;
+      BFD_ASSERT (prop->pr_kind == property_number);
+
+      obj_attr_subsection_v2 *subsec = obj_attr_subsection_v2_find_by_name
+	(elf_obj_attr_subsections (abfd).first_, "aeabi_feature_and_bits", false);
+
+      bool new_subsec = false;
+      if (subsec == NULL)
+	{
+	  subsec = _bfd_elf_obj_attr_subsection_v2_init (
+	    "aeabi_feature_and_bits", SUBSEC_PUBLIC, true, ULEB128);
+	  new_subsec = true;
+	}
+
+      bool bti_bit = prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+      bool pac_bit = prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+      bool gcs_bit = prop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+
+      _record_tag_value (subsec, Tag_Feature_BTI, bti_bit);
+      _record_tag_value (subsec, Tag_Feature_PAC, pac_bit);
+      _record_tag_value (subsec, Tag_Feature_GCS, gcs_bit);
+
+      if (new_subsec)
+	LINKED_LIST_APPEND(obj_attr_subsection_v2) (
+	  &elf_obj_attr_subsections (abfd), subsec);
+    }
+}
+
+void
+_bfd_aarch64_translate_relevant_obj_attrs_to_gnu_props (
+  bfd *abfd,
+  obj_attr_subsection_v2 *subsec)
+{
+  /* Note: there is no need to create the GNU properties section here.  It will
+     be handled later by setup_gnu_properties.  */
+
+  if (strcmp (subsec->name, "aeabi_feature_and_bits") == 0)
+    {
+      uint32_t gnu_property_aarch64_features = 0;
+
+      for (obj_attr_v2 *attr = subsec->first_; attr != NULL; attr = attr->next)
+	{
+	  if (attr->tag == Tag_Feature_BTI && attr->vals.uint_val == 1)
+	    gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+	  else if (attr->tag == Tag_Feature_PAC && attr->vals.uint_val == 1)
+	    gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+	  else if (attr->tag == Tag_Feature_GCS && attr->vals.uint_val == 1)
+	    gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+	}
+
+      /* Note: _bfd_elf_get_property find the existing property, or create one.
+	 The insertion is already done by it.  */
+      elf_property *prop =
+	_bfd_elf_get_property (abfd, GNU_PROPERTY_AARCH64_FEATURE_1_AND, 4);
+      prop->u.number |= gnu_property_aarch64_features;
+      prop->pr_kind = property_number;
+    }
+}
+
+/* Check whether a subsection is known, and if so, whether the current
+   properties of the subsection match the expected ones.
+   Return True if the subsection is known from the backend, OR all the
+   properties of the subsection match the expected.  False otherwise.  */
+bool
+_bfd_aarch64_obj_attr_subsection_v2_match_known (struct bfd_link_info *info,
+						 bfd *abfd,
+						 obj_attr_subsection_v2 *subsec)
+{
+  const known_subsection_v2 *subsec_info =
+    identify_subsection (get_elf_backend_data (abfd), subsec->name);
+
+  bool match = true;
+  if (subsec_info == NULL)
+    return match;
+
+  if (subsec_info->encoding != subsec->encoding)
+    {
+      info->callbacks->einfo (_("%X%pB: error: encoding property of subsection "
+	"'%s' was incorrectly set. Got '%s', expected '%s'.\n"),
+	abfd, subsec->name,
+	obj_attr_encoding_v2_to_string (subsec->encoding),
+	obj_attr_encoding_v2_to_string (subsec_info->encoding));
+      match = false;
+    }
+  if (subsec_info->optional != subsec->optional)
+    {
+      info->callbacks->einfo (_("%X%pB: error: optional property of subsection "
+	"'%s' was incorrectly set. Got '%s', expected '%s'.\n"),
+	abfd, subsec->name,
+	obj_attr_subsection_v2_optional_to_string (subsec->optional),
+	obj_attr_subsection_v2_optional_to_string (subsec_info->optional));
+      match = false;
+    }
+  return match;
+}
+
+/* True if the default value for the tag is managed by the backend.
+   False otherwise.  */
+bool
+_bfd_aarch64_obj_attr_v2_default_value (struct bfd_link_info *info,
+					const known_tag_v2 *tag_info,
+					obj_attr_subsection_v2 *subsec,
+					obj_attr_v2 *attr)
+{
+  (void) info;
+  (void) tag_info;
+  (void) subsec;
+  (void) attr;
+
+  /* For now, there is no default value set by the backend. The default BTI and
+     GCS values are set by the respective command-line options '-z force-bti'
+     and '-z gcs'.  */
+
+  return false;
+}
+
+/* True, the tag needs to be merged, False don't merge.  */
+obj_attr_v2_merge_result
+_bfd_aarch64_obj_attr_v2_tag_merge (struct bfd_link_info *info,
+				    bfd *abfd,
+				    obj_attr_subsection_v2 *subsec,
+				    obj_attr_v2 *lhs, obj_attr_v2 *rhs,
+				    obj_attr_v2 *frozen)
+{
+  obj_attr_v2_merge_result res = {
+    .merge = false,
+    .vals.uint_val = 0,
+    .reason = NONE,
+  };
+
+  /* No need to list required sections here, they are handled separately as
+     they require a perfect one-to-one match for all the tag values.  */
+  if (strcmp (subsec->name, "aeabi_feature_and_bits") == 0)
+    {
+      BFD_ASSERT (subsec->encoding == ULEB128 && subsec->optional);
+      const known_tag_v2 *tag_info =
+	known_obj_attr_v2_find_by_tag (get_elf_backend_data (abfd),
+	  subsec->name, lhs->tag);
+      if (tag_info == NULL)
+	{
+	  info->callbacks->einfo (_("%pB: warning: cannot merge unknown tag "
+	    "Tag_unknown_%u (=0x%x) in subsection '%s'.\n"), abfd, rhs->tag,
+	    rhs->vals.uint_val, subsec->name);
+	  res.reason = UNSUPPORTED;
+	  return res;
+	}
+
+      /* For now, there is no different between the tags of this section, all
+	 will be merged in the same way.  */
+      res = obj_attr_v2_tag_merge_AND (info, abfd, subsec, lhs, rhs, frozen);
+
+      const aarch64_protection_opts *sw_protections
+	= &elf_aarch64_tdata (info->output_bfd)->sw_protections;
+      aarch64_feature_marking_report bti_report = sw_protections->bti_report;
+      aarch64_feature_marking_report gcs_report = sw_protections->gcs_report;
+
+      if ((rhs->tag == Tag_Feature_BTI) && (bti_report != MARKING_NONE)
+       && (sw_protections->plt_type & PLT_BTI) && (rhs->vals.uint_val == 0))
+	_bfd_aarch64_elf_check_bti_report (info, abfd);
+
+      if ((rhs->tag == Tag_Feature_GCS) && (gcs_report != MARKING_NONE)
+       && (sw_protections->gcs_type == GCS_ALWAYS) && (rhs->vals.uint_val == 0))
+	_bfd_aarch64_elf_check_gcs_report (info, abfd);
+
+      /* Make sure that frozen bits don't disappear from REF when it will be
+	 compared to the next file.  */
+      if (frozen != NULL)
+	res.vals.uint_val |= frozen->vals.uint_val;
+    }
+  else
+    res.reason = UNSUPPORTED;
+
+  return res;
+}
+
+bfd *
+_bfd_aarch64_elf_link_setup_build_attributes (struct bfd_link_info *info)
+{
+  bfd *pbfd = _bfd_elf_link_setup_build_attributes (info);
+
+  /* Set the flag marking whether the merge of build attributes was done so
+     that setup_gnu_properties does not raise the same errors/warning again.  */
+  elf_aarch64_tdata (info->output_bfd)->ba_merge_done = true;
+
+  return pbfd;
+}
+
 /* Find the first input bfd with GNU property and merge it with GPROP.  If no
    such input is found, add it to a new section at the last input.  Update
    GPROP accordingly.  */
@@ -888,14 +1162,24 @@  _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *info)
 
 	 Note: If there is no .gnu.note.property section, we might think that
 	 elf_properties (res.pbfd) is always NULL.  However, this is not always
-	 true.  In PR23900: old linkers were treating .note.gnu.property as a
-	 generic note section, so old objects might contain properties inside
-	 .note instead of .note.gnu.property. In this case, the section won't be
-	 detected but the properties are still parsed. Consequently,
-	 elf_properties (res.pbfd) is populated and different from NULL (see
-	 https://sourceware.org/bugzilla/show_bug.cgi?id=23900 for more
-	 details).  */
-      if (res.sec == NULL && elf_properties (res.pbfd) == NULL)
+	 true for the following reasons:
+	 - PR23900: old linkers were treating .note.gnu.property as a generic
+	   note section, so old objects might contain properties inside .note
+	   instead of .note.gnu.property. In this case, the section won't be
+	   detected but the properties are still parsed. Consequently,
+	   elf_properties (res.pbfd) is populated and different from NULL (see
+	   https://sourceware.org/bugzilla/show_bug.cgi?id=23900 for more
+	   details).
+	 - since the introduction of the build attributes, once the merge
+	   of the BAs is done, some of the BAs can be translated to GNU
+	   properties like GNU_PROPERTY_AARCH64_FEATURE_1_AND.  In this case,
+	   we need to check explicitly for the presence of the GNU properties
+	   that might be added by the BAs merge.  */
+      if (res.sec == NULL
+       && (elf_properties (res.pbfd) == NULL
+	|| _bfd_elf_find_property (elf_properties (res.pbfd),
+				   GNU_PROPERTY_AARCH64_FEATURE_1_AND,
+				   NULL)))
 	_bfd_aarch64_elf_create_gnu_property_section (info, res.pbfd);
 
       /* Merge the found input property with output properties. Note: if no
@@ -1110,7 +1394,8 @@  _bfd_aarch64_elf_check_bti_report (struct bfd_link_info *info, bfd *ebfd)
 {
   struct elf_aarch64_obj_tdata *tdata = elf_aarch64_tdata (info->output_bfd);
 
-  if (tdata->sw_protections.bti_report == MARKING_NONE)
+  if (elf_aarch64_tdata (info->output_bfd)->ba_merge_done
+   || tdata->sw_protections.bti_report == MARKING_NONE)
     return;
 
   ++tdata->n_bti_issues;
@@ -1144,7 +1429,8 @@  _bfd_aarch64_elf_check_gcs_report (struct bfd_link_info *info, bfd *ebfd)
     }
   else
     {
-      if (tdata->sw_protections.gcs_report == MARKING_NONE)
+      if (elf_aarch64_tdata (info->output_bfd)->ba_merge_done
+       || tdata->sw_protections.gcs_report == MARKING_NONE)
 	return;
       ++tdata->n_gcs_issues;
       if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX)
diff --git a/bfd/elfxx-aarch64.h b/bfd/elfxx-aarch64.h
index 506f4a93c9d..c30f41b30c0 100644
--- a/bfd/elfxx-aarch64.h
+++ b/bfd/elfxx-aarch64.h
@@ -107,6 +107,9 @@  struct elf_aarch64_obj_tdata
   /* Software protections options.  */
   struct aarch64_protection_opts sw_protections;
 
+  /* The merge of build attributes already occured.  */
+  bool ba_merge_done;
+
   /* Number of reported BTI issues.  */
   int n_bti_issues;
 
@@ -209,6 +212,39 @@  _bfd_aarch64_elf_write_core_note (bfd *, char *, int *, int, ...);
 #define elf_backend_grok_psinfo		_bfd_aarch64_elf_grok_psinfo
 #define elf_backend_write_core_note	_bfd_aarch64_elf_write_core_note
 
+extern const known_subsection_v2 aarch64_obj_attr_v2_known_subsections[];
+
+extern bfd *
+_bfd_aarch64_elf_link_setup_build_attributes (struct bfd_link_info *);
+
+extern void
+_bfd_aarch64_record_obj_attr_v2 (obj_attr_subsection_v2 *, Tag_Feature_XXX,
+  uint32_t);
+
+extern void
+_bfd_aarch64_translate_relevant_gnu_props_to_obj_attrs (bfd *,
+  elf_property_list *);
+
+extern void
+_bfd_aarch64_translate_relevant_obj_attrs_to_gnu_props (bfd *,
+  obj_attr_subsection_v2 *);
+
+extern bool
+_bfd_aarch64_obj_attr_subsection_v2_match_known (struct bfd_link_info *,
+						 bfd *,
+						 obj_attr_subsection_v2 *);
+
+extern bool
+_bfd_aarch64_obj_attr_v2_default_value (struct bfd_link_info *,
+					const known_tag_v2 *,
+					obj_attr_subsection_v2 *,
+					obj_attr_v2 *);
+
+extern obj_attr_v2_merge_result
+_bfd_aarch64_obj_attr_v2_tag_merge (struct bfd_link_info *, bfd *,
+				    obj_attr_subsection_v2 *, obj_attr_v2 *,
+				    obj_attr_v2 *, obj_attr_v2 *);
+
 extern bfd *
 _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *);