]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
aarch64: merge of Object Attributes v2 during linkage
authorMatthieu Longo <matthieu.longo@arm.com>
Thu, 17 Apr 2025 09:23:08 +0000 (10:23 +0100)
committerMatthieu Longo <matthieu.longo@arm.com>
Thu, 22 Jan 2026 10:11:17 +0000 (10:11 +0000)
This patch adds support to AArch64 backend to process AEABI Build
Attributes and raise any compatibility issue.

AArch64 backend declares 2 vendor subsections, and their associated tags:
- aeabi_feature_and_bits: contains tags that describe the same optional
  bits as the GNU_PROPERTY_AARCH64_FEATURE_1_AND. For now, the following
  attributes are recognized:
    - Tag_Feature_BTI: means that all the executable sections are
      compatible with Branch Target Identification (BTI) mechanism.
    - Tag_Feature_PAC: means that all the executable sections have been
      protected with Return Address Signing.
    - Tag_Feature_GCS: means that all the executable sections are
      compatible with the Guarded Control Stack (GCS) extension.
- aeabi_pauthabi: contains information about the Pointer Authentication
  Signing schema when the object uses an extension to ELF, PAUTHABI64,
  which is currently not supported by GCC toolchain. The pointers that
  are signed as well as the modifiers and key used for each type of
  pointer are known as the signing schema. The support of this
  subsection is there for completeness with the AEABI Build Attributes
  document, and allows readelf to dump the data nicely, and the linker
  to detect a use of a signing schema, and error.
    - Tag_PAuth_Paltform: the platform vendor id.
    - Tag_PAuth_Schema: the version numner of the schema.

For backward-compatibilty purpose, AArch64 backend translates
GNU_PROPERTY_AARCH64_FEATURE_1_AND in input files to its OAv2 equivalents.
The frozen set of OAv2 is populated with values derived from command-line
options for BTI (-z force-bti) and GCS (-z gcs=*).
It also reports incompatibilities for BTI and GCS, and set BTI PLT type
depending on the OAv2 merge result.
Regarding incompatibilities, only the ones detected in objects constituting
the output link unit will be reported. Supports for detecting incompatibilities
in shared objects might be a future work to bring it in pair with the GNU
properties merge. However, since OAv2 are translated to GNU properties,
detection will still happen so this feature seems redundant and of little
value given the backward compatibility support for GNU properties is
required (see next paragraph).
Finally, it translates OAv2s in subsection "aeabi_feature_and_bits" to
GNU_PROPERTY_AARCH64_FEATURE_1_AND as GNU properties are required for
the dynamic linker (it does not understand OAv2s yet).

bfd/elf-attrs.c
bfd/elf-attrs.h
bfd/elfnn-aarch64.c
bfd/elfxx-aarch64.c
bfd/elfxx-aarch64.h

index 03914169483b44312fcaae16b5513bc8931f5605..c012ba4956cc39309e278080dbf84a787728e975 100644 (file)
@@ -1143,12 +1143,12 @@ gnu_testing_merge_subsection (const char *subsec_name)
 }
 
 /* Merge policy Integer-AND: apply bitwise AND between REF and RHS.  */
-static obj_attr_v2_merge_result_t
-obj_attr_v2_merge_AND (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
-                      const bfd *abfd ATTRIBUTE_UNUSED,
-                      const obj_attr_subsection_v2_t *subsec,
-                      const obj_attr_v2_t *ref, const obj_attr_v2_t *rhs,
-                      const obj_attr_v2_t *frozen ATTRIBUTE_UNUSED)
+obj_attr_v2_merge_result_t
+_bfd_obj_attr_v2_merge_AND (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                           const bfd *abfd ATTRIBUTE_UNUSED,
+                           const obj_attr_subsection_v2_t *subsec,
+                           const obj_attr_v2_t *ref, const obj_attr_v2_t *rhs,
+                           const obj_attr_v2_t *frozen ATTRIBUTE_UNUSED)
 {
   BFD_ASSERT (subsec->encoding == OA_ENC_ULEB128);
 
@@ -1290,7 +1290,7 @@ oav2_attr_merge (const struct bfd_link_info *info,
       if (lhs->tag <= 1)
        {
          if (policy == SUBSECTION_TESTING_MERGE_AND_POLICY)
-           res = obj_attr_v2_merge_AND (info, abfd, subsec, lhs, rhs, frozen);
+           res = _bfd_obj_attr_v2_merge_AND (info, abfd, subsec, lhs, rhs, frozen);
          else if (policy == SUBSECTION_TESTING_MERGE_OR_POLICY)
            res = obj_attr_v2_merge_OR (info, abfd, subsec, lhs, rhs, frozen);
          else if (policy == SUBSECTION_TESTING_MERGE_ADD_POLICY)
index c604195dd984e05f9bb5d7904fbced4b751d4cdc..d6d3b59ab357eb638f59a6c45ae0caa0c026e719 100644 (file)
@@ -20,6 +20,7 @@
 
 #pragma once
 
+#include "hidden.h"
 #include <stdint.h>
 #include "hidden.h"
 
@@ -211,3 +212,14 @@ typedef struct {
      error.  */
   enum obj_attr_v2_merge_result_reason reason;
 } obj_attr_v2_merge_result_t;
+
+/* Re-usable merge policies.  */
+/* For now, only AND-merge is used by AArch64 backend.  Additional policies
+   (Integer-OR, String-ADD) are part of the GNU testing namespace.  If they
+   appear to be usefull for a backend at some point, they should be exposed
+   to the backend here below.  */
+extern obj_attr_v2_merge_result_t
+_bfd_obj_attr_v2_merge_AND (const struct bfd_link_info *, const bfd *,
+                           const obj_attr_subsection_v2_t *,
+                           const obj_attr_v2_t *, const obj_attr_v2_t *,
+                           const obj_attr_v2_t *) ATTRIBUTE_HIDDEN;
index 75e16d0bc15d739b6edd40e9b6c2112de3ab3ad8..78bab74c197d6c85f6488fae1f7266f872d1b09b 100644 (file)
@@ -5040,17 +5040,36 @@ 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 object attributes used to save requested features from
+     the command-line options.  */
+  obj_attr_subsection_v2_t *attrs_subsection
+    = bfd_elf_obj_attr_subsection_v2_init (xstrdup ("aeabi_feature_and_bits"),
+                                           OA_SUBSEC_PUBLIC, true,
+                                           OA_ENC_ULEB128);
+
   uint32_t gnu_property_aarch64_feature_1_and = 0;
   aarch64_feature_marking_report gcs_report;
   aarch64_feature_marking_report gcs_report_dynamic;
 
   if (sw_protections->plt_type & PLT_BTI)
-    gnu_property_aarch64_feature_1_and |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+    {
+      gnu_property_aarch64_feature_1_and |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+      _bfd_aarch64_oav2_record (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
+     object 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:
       gnu_property_aarch64_feature_1_and |= GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      _bfd_aarch64_oav2_record (attrs_subsection, Tag_Feature_GCS, true);
 
       /* The default diagnostic level with '-z gcs=always' is 'warning'.  */
       if (sw_protections->gcs_report == MARKING_UNSET)
@@ -5067,6 +5086,7 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
 
     case GCS_NEVER:
       gnu_property_aarch64_feature_1_and &= ~GNU_PROPERTY_AARCH64_FEATURE_1_GCS;
+      _bfd_aarch64_oav2_record (attrs_subsection, Tag_Feature_GCS, false);
 
       /* Markings are ignored, so no diagnostic messages can be emitted.  */
       gcs_report = MARKING_NONE;
@@ -5102,6 +5122,10 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
       abort ();
     }
 
+  if (attrs_subsection->size > 0)
+    LINKED_LIST_APPEND (obj_attr_subsection_v2_t)
+      (&elf_obj_attr_subsections (output_bfd), attrs_subsection);
+
   elf_aarch64_tdata (output_bfd)->gnu_property_aarch64_feature_1_and
     = gnu_property_aarch64_feature_1_and;
 
@@ -10462,7 +10486,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)
 {
@@ -10582,6 +10607,42 @@ elfNN_aarch64_backend_symbol_processing (bfd *abfd, asymbol *sym)
     sym->flags |= BSF_KEEP;
 }
 
+/* Implement elf_backend_setup_object_attributes for AArch64.  */
+static bfd *
+elfNN_aarch64_link_setup_object_attributes (struct bfd_link_info *info)
+{
+  bfd *pbfd = _bfd_aarch64_elf_link_setup_object_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 object attributes (a.k.a FROZEN), so plt_type has already been
+     set and FROZEN doesn't have any effect on plt_type.
+     Whereas if BTI is inferred from the input bfds, information flows from
+     output object 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.  */
+  const obj_attr_subsection_v2_t *aeabi_feature_and_bits_subsec
+    = bfd_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)
+    {
+      const obj_attr_v2_t *attr_bti
+       = _bfd_obj_attr_v2_find_by_tag (aeabi_feature_and_bits_subsec,
+                                       Tag_Feature_BTI, true);
+      if (attr_bti && attr_bti->val.uint == 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.  */
@@ -10597,7 +10658,7 @@ elfNN_aarch64_link_setup_gnu_properties (struct bfd_link_info *info)
      outprop 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.  */
-  struct elf_aarch64_obj_tdata * tdata = elf_aarch64_tdata (info->output_bfd);
+  struct elf_aarch64_obj_tdata *tdata = elf_aarch64_tdata (info->output_bfd);
   uint32_t outprop = tdata->gnu_property_aarch64_feature_1_and;
   if (outprop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
     tdata->sw_protections.plt_type |= PLT_BTI;
@@ -10799,9 +10860,24 @@ static const struct elf_size_info elfNN_aarch64_size_info =
 #define elf_backend_symbol_processing          \
   elfNN_aarch64_backend_symbol_processing
 
+#define elf_backend_setup_object_attributes    \
+  elfNN_aarch64_link_setup_object_attributes
+
 #define elf_backend_setup_gnu_properties       \
   elfNN_aarch64_link_setup_gnu_properties
 
+#define elf_backend_translate_gnu_props_to_obj_attrs \
+  _bfd_aarch64_translate_gnu_props_to_obj_attrs
+
+#define elf_backend_translate_obj_attrs_to_gnu_props \
+  _bfd_aarch64_translate_obj_attrs_to_gnu_props
+
+#define elf_backend_obj_attr_v2_default_value  \
+  _bfd_aarch64_oav2_default_value
+
+#define elf_backend_obj_attr_v2_merge  \
+  _bfd_aarch64_oav2_attr_merge
+
 #define elf_backend_merge_gnu_properties       \
   elfNN_aarch64_merge_gnu_properties
 
index 85d2e4228109918e45eb0bb09c42e012d5a8338f..8352545b7b96caa7e7f60ce70a4ebc0981c7d065 100644 (file)
@@ -936,6 +936,322 @@ const known_subsection_v2_t aarch64_obj_attr_v2_known_subsections[2] =
   },
 };
 
+/* Record the pair (TAG, VALUE) into SUBSEC.  */
+void
+_bfd_aarch64_oav2_record (obj_attr_subsection_v2_t *subsec,
+                         Tag_Feature_Set feature_tag,
+                         uint32_t value)
+{
+  union obj_attr_value_v2 data;
+  data.uint = value;
+  obj_attr_v2_t *attr = bfd_elf_obj_attr_v2_init (feature_tag, data);
+  LINKED_LIST_APPEND (obj_attr_v2_t) (subsec, attr);
+}
+
+/* Wrapper around the recording of the pair (TAG, VALUE) into SUBSEC called from
+   a context of translation from GNU properties.  */
+static void
+obj_attr_v2_record_tag_value (obj_attr_subsection_v2_t *subsec,
+                             Tag_Feature_Set tag,
+                             bool value)
+{
+  obj_attr_v2_t *attr;
+  attr = _bfd_obj_attr_v2_find_by_tag (subsec, tag, false);
+  if (attr != NULL)
+    {
+      if (attr->val.uint != value)
+       {
+         /* 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 the second case occurs.  */
+         uint32_t merged_val = attr->val.uint | value;
+         _bfd_aarch64_oav2_record (subsec, tag, merged_val);
+       }
+      /* else: nothing to do.  */
+    }
+  else
+    _bfd_aarch64_oav2_record (subsec, tag, value);
+}
+
+/* Translate the relevant GNU properties in P to their Object Attributes v2
+   equivalents.  */
+void
+_bfd_aarch64_translate_gnu_props_to_obj_attrs
+  (const bfd *abfd, const elf_property_list *p)
+{
+  if (p->property.pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
+    {
+      const elf_property *prop = &p->property;
+      BFD_ASSERT (prop->pr_kind == property_number);
+
+      obj_attr_subsection_v2_t *subsec = bfd_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
+           (xstrdup ("aeabi_feature_and_bits"), OA_SUBSEC_PUBLIC, true,
+            OA_ENC_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;
+
+      obj_attr_v2_record_tag_value (subsec, Tag_Feature_BTI, bti_bit);
+      obj_attr_v2_record_tag_value (subsec, Tag_Feature_PAC, pac_bit);
+      obj_attr_v2_record_tag_value (subsec, Tag_Feature_GCS, gcs_bit);
+
+      if (new_subsec)
+       LINKED_LIST_APPEND (obj_attr_subsection_v2_t)
+         (&elf_obj_attr_subsections (abfd), subsec);
+    }
+}
+
+/* Translate relevant Object Attributes v2 in SUBSEC to GNU properties.  */
+void
+_bfd_aarch64_translate_obj_attrs_to_gnu_props
+  (bfd *abfd, const obj_attr_subsection_v2_t *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 (const obj_attr_v2_t *attr = subsec->first;
+          attr != NULL;
+          attr = attr->next)
+       {
+         if (attr->tag == Tag_Feature_BTI && attr->val.uint == 1)
+           gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+         else if (attr->tag == Tag_Feature_PAC && attr->val.uint == 1)
+           gnu_property_aarch64_features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+         else if (attr->tag == Tag_Feature_GCS && attr->val.uint == 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 the given ATTR is managed by the backend, and if that is the
+   case, initialize ATTR with its default value coming from the known tag
+   registry.
+   True if the default value for the tag is managed by the backend, and was
+   initialized.  False otherwise.  */
+bool
+_bfd_aarch64_oav2_default_value
+  (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+   const obj_attr_info_t *tag_info ATTRIBUTE_UNUSED,
+   const obj_attr_subsection_v2_t *subsec ATTRIBUTE_UNUSED,
+   obj_attr_v2_t *attr ATTRIBUTE_UNUSED)
+{
+  /* 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;
+}
+
+/* Merge the values from LHS, RHS, FROZEN, and return the merge result.  LHS,
+   RHS and FROZEN correspond to an attribute from SUBSEC with the same tag, but
+   from three different contexts:
+    - LHS corresponds to the global merge result.
+    - RHS corresponds to the new value that is merged into the global merge
+      result.
+    - FROZEN corresponds to the value coming from some configuration context
+      (usually a command-line option) and which is immutable for the whole
+      merge process.  */
+obj_attr_v2_merge_result_t
+_bfd_aarch64_oav2_attr_merge (const struct bfd_link_info *info,
+                             const bfd *abfd,
+                             const obj_attr_subsection_v2_t *subsec,
+                             const obj_attr_v2_t *lhs, const obj_attr_v2_t *rhs,
+                             const obj_attr_v2_t *frozen)
+{
+  obj_attr_v2_merge_result_t res = {
+    .merge = false,
+    .val.uint = 0,
+    .reason = OAv2_MERGE_OK,
+  };
+
+  /* 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 == OA_ENC_ULEB128 && subsec->optional);
+      const obj_attr_info_t *attr_info
+       = _bfd_obj_attr_v2_find_known_by_tag (get_elf_backend_data (abfd),
+                                             subsec->name, lhs->tag);
+      if (attr_info == NULL)
+       {
+         info->callbacks->einfo
+           (_("%pB: warning: cannot merge unknown tag 'Tag_unknown_%u' "
+              "(=0x%x) in subsection '%s'.\n"),
+            abfd, rhs->tag, rhs->val.uint, subsec->name);
+         res.reason = OAv2_MERGE_UNSUPPORTED;
+         return res;
+       }
+
+      /* For now, there is no different between the tags of this section, all
+        will be merged in the same way.  */
+      res = _bfd_obj_attr_v2_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->val.uint == 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->val.uint == 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.val.uint |= frozen->val.uint;
+    }
+  else
+    res.reason = OAv2_MERGE_UNSUPPORTED;
+
+  return res;
+}
+
+/* Check for incompatibilities with PAuthABI attributes.  */
+static bool
+aarch64_check_pauthabi_attributes (const struct bfd_link_info *info)
+{
+  /* The subsection "aeabi_pauthabi" contains information about the Pointer
+     Authentication Signing schema when the object uses an extension to ELF,
+     PAUTHABI64, which is today only supported by LLVM, and not supported by
+     GCC 15 toolchain or ealier ones.  There is no plan to add support for it
+     in the future.  The pointers that are signed as well as the modifiers and
+     key used for each type of pointer are known as the signing schema.
+     The AEABI Build attributes specification defines the following tuple values
+     of (Tag_Pauth_Platform, Tag_Pauth_Schema):
+       - The tuple (0, 0) is obtained when both attributes are explicitly set to
+        0 or are implicitly set to 0 due to the rules for setting default values
+        for public tags.  This represents an ELF file which makes no use of the
+        PAuthABI extension.
+       - The tuple (0, 1) is reserved for the "Invalid" platform.  ELF files with
+        an "Invalid" platform are incompatible with the PAuth ABI Extension.
+       - The tuples (0, N) where N > 1 are reserved.
+       - The tuples (M, N) where M is the id of one of the registered platforms
+        defined in PAuthABI64, represents a valid signing schema.  (M, 0)
+        represents a schema version of 0 for platform M.
+     Given that the GNU linker does not support PAuthABI, it cannot do anything
+     with values others than (0, 0) or (0, 1).
+     The check below enforces either that the output object has either no
+     subsection "aeabi_pauthabi", or the tuple is set to (0, 0) and (0, 1).  */
+
+  obj_attr_subsection_v2_t *subsecs
+    = elf_obj_attr_subsections (info->output_bfd).first;
+  const obj_attr_subsection_v2_t *subsec
+    = bfd_obj_attr_subsection_v2_find_by_name (subsecs, "aeabi_pauthabi", true);
+  if (subsec == NULL)
+    return true;
+
+  int platform_id = 0;
+  int version_id = 0;
+
+  const obj_attr_v2_t *attr
+    = _bfd_obj_attr_v2_find_by_tag (subsec, Tag_PAuth_Platform, true);
+  if (attr != NULL)
+    platform_id = attr->val.uint;
+
+  attr = _bfd_obj_attr_v2_find_by_tag (subsec, Tag_PAuth_Schema, true);
+  if (attr != NULL)
+    version_id = attr->val.uint;
+
+  if (! ((platform_id == 0 && version_id == 0)
+      || (platform_id == 0 && version_id == 1)))
+    {
+      info->callbacks->einfo
+       (_("%Xerror: the GNU linker does not support PAuthABI.  Any value "
+          "different from (platform = 0, schema = 0) or (platform = 0, schema "
+          "= 1) is not supported.\n"));
+      return false;
+    }
+
+  return true;
+}
+
+/* Merge the AEABI Build Attributes present in the input BFDs, raise any
+   compatibility issue, and write the merge result to OBFD.
+
+   AArch64 backend declares two vendor subsections, and their associated tags:
+    - aeabi_feature_and_bits: contains tags that describe the same optional
+      bits as the GNU_PROPERTY_AARCH64_FEATURE_1_AND.  For now, the following
+      attributes are recognized:
+       - Tag_Feature_BTI: means that all the executable sections are
+         compatible with Branch Target Identification (BTI) mechanism.
+       - Tag_Feature_PAC: means that all the executable sections have been
+         protected with Return Address Signing.
+       - Tag_Feature_GCS: means that all the executable sections are
+         compatible with the Guarded Control Stack (GCS) extension.
+    - aeabi_pauthabi: contains information about the Pointer Authentication
+      Signing schema when the object uses an extension to ELF, PAUTHABI64,
+      which is currently not supported by GCC toolchain.  The pointers that
+      are signed as well as the modifiers and key used for each type of pointer
+      are known as the signing schema.  The support of this subsection is there
+      for completeness with the AEABI Build Attributes document, and allows
+      readelf to dump the data nicely, and the linker to detect a use of a
+      signing schema, and error.
+       - Tag_PAuth_Paltform: the platform vendor id.
+       - Tag_PAuth_Schema: the version numner of the schema.
+
+    For backward-compatibilty purpose, AArch64 backend translates
+    GNU_PROPERTY_AARCH64_FEATURE_1_AND in input files to its OAv2 equivalents.
+    The frozen set of OAv2 is populated with values derived from command-line
+    options for BTI (-z force-bti) and GCS (-z gcs=*).
+    It also reports incompatibilities for BTI and GCS, and set BTI PLT type
+    depending on the OAv2 merge result.
+    Regarding incompatibilities, only the ones detected in objects constituting
+    the output link unit will be reported.  Supports for detecting incompatibi-
+    -lities in shared objects might be a future work to bring it in pair with
+    thenGNU properties merge.  However, since OAv2 are translated to GNU
+    properties, detection will still happen so this feature seems redundant and
+    of little value given the backward compatibility support for GNU properties
+    is required (see next paragraph).
+    Finally, it translates OAv2s in subsection "aeabi_feature_and_bits" to
+    GNU_PROPERTY_AARCH64_FEATURE_1_AND as GNU properties are required for
+    the dynamic linker (it does not understand OAv2s yet).  */
+bfd *
+_bfd_aarch64_elf_link_setup_object_attributes (struct bfd_link_info *info)
+{
+  bfd *pbfd = _bfd_elf_link_setup_object_attributes (info);
+
+  /* Check PAuthABI compatibility.  */
+  if (! aarch64_check_pauthabi_attributes (info))
+    return NULL;
+
+  /* Set the flag marking whether the merge of object attributes was done so
+     that setup_gnu_properties does not raise the same errors/warning again.  */
+  elf_aarch64_tdata (info->output_bfd)->oa_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.  */
@@ -956,22 +1272,32 @@ _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 object attributes, once the merge
+          of the OAs is done, some of the OAs 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
         property was found, _bfd_elf_get_property will create one.  */
-      elf_property *prop =
-       _bfd_elf_get_property (res.pbfd,
-                              GNU_PROPERTY_AARCH64_FEATURE_1_AND,
-                              4);
+      elf_property *prop
+       _bfd_elf_get_property (res.pbfd,
+                                GNU_PROPERTY_AARCH64_FEATURE_1_AND,
+                                4);
 
       /* Check for a feature mismatch and report issue (if any) before this
         information get lost as the value of ebfd will be overriden with
@@ -1171,7 +1497,8 @@ _bfd_aarch64_elf_check_bti_report (const struct bfd_link_info *info,
 {
   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)->oa_merge_done
+      || tdata->sw_protections.bti_report == MARKING_NONE)
     return;
 
   ++tdata->n_bti_issues;
@@ -1207,7 +1534,8 @@ _bfd_aarch64_elf_check_gcs_report (const struct bfd_link_info *info,
     }
   else
     {
-      if (tdata->sw_protections.gcs_report == MARKING_NONE)
+      if (elf_aarch64_tdata (info->output_bfd)->oa_merge_done
+         || tdata->sw_protections.gcs_report == MARKING_NONE)
        return;
       ++tdata->n_gcs_issues;
       if (tdata->n_gcs_issues > GNU_PROPERTY_ISSUES_MAX)
index 43d58d2a849ab952d07059b9ff06a5f476a83bcc..a312427bc75210337b10e26b28e1f8d49b03a22b 100644 (file)
@@ -107,6 +107,9 @@ struct elf_aarch64_obj_tdata
   /* Software protections options.  */
   struct aarch64_protection_opts sw_protections;
 
+  /* The merge of object attributes already occured.  */
+  bool oa_merge_done;
+
   /* Number of reported BTI issues.  */
   int n_bti_issues;
 
@@ -245,6 +248,34 @@ _bfd_aarch64_obj_attrs_version_enc (obj_attr_version_t) ATTRIBUTE_HIDDEN;
 extern const known_subsection_v2_t aarch64_obj_attr_v2_known_subsections[2]
   ATTRIBUTE_HIDDEN;
 
+extern bfd *
+_bfd_aarch64_elf_link_setup_object_attributes (struct bfd_link_info *)
+  ATTRIBUTE_HIDDEN;
+
+extern void
+_bfd_aarch64_oav2_record (obj_attr_subsection_v2_t *, Tag_Feature_Set, uint32_t)
+  ATTRIBUTE_HIDDEN;
+
+extern void
+_bfd_aarch64_translate_gnu_props_to_obj_attrs
+  (const bfd *, const elf_property_list *) ATTRIBUTE_HIDDEN;
+
+extern void
+_bfd_aarch64_translate_obj_attrs_to_gnu_props
+  (bfd *, const obj_attr_subsection_v2_t *) ATTRIBUTE_HIDDEN;
+
+extern bool
+_bfd_aarch64_oav2_default_value (const struct bfd_link_info *,
+                                const obj_attr_info_t *,
+                                const obj_attr_subsection_v2_t *,
+                                obj_attr_v2_t *) ATTRIBUTE_HIDDEN;
+
+extern obj_attr_v2_merge_result_t
+_bfd_aarch64_oav2_attr_merge (const struct bfd_link_info *, const bfd *,
+                             const obj_attr_subsection_v2_t *,
+                             const obj_attr_v2_t *, const obj_attr_v2_t *,
+                             const obj_attr_v2_t *) ATTRIBUTE_HIDDEN;
+
 extern bfd *
 _bfd_aarch64_elf_link_setup_gnu_properties (struct bfd_link_info *)
   ATTRIBUTE_HIDDEN;