[PATCH v6 12/19] Merge of Object Attributes v2 during linkage (generic logic)

Matthieu Longo matthieu.longo@arm.com
Fri Jul 11 11:28:57 GMT 2025


This patch adds all the generic logic to the linker to process OAv2.
The linker is an "advanced" consumer of OAv2. After parsing, it
deduplicates them, merge them, detect any compatibility issues,
and finally translate them to GNU properties.

** Overall design

The OAv2 processing pipeline follows a map-reduce pattern. Obviously,
the actual processing in GNU ld is not multi-threaded, and the
operations are not necessarily executed directly one after another.

* Phase 1, map: successive per-file operations applied on the list of
  compatible input objects.
  1. Parsing of the OAv2 section's data (also used by objcopy).
  2. Translation of relevant GNU properties to OAv2. This is required
     for the backward-compatibility with input objects only marked
     using GNU properties.
  3. Sorting of the subsections and object attributes. Further
     operations rely on the ordering to perform some optimization in
     the processing of the data.
  4. Deduplication of subsections and object attributes, and detection
     of any conflict between duplicated subsections or tags.
  5. Translation of relevant OAv2 to GNU properties for a
     forward-compatibility with the GNU properties merge.

* Phase 2, reduce: OAv2 in input objects are merged together.
  1. Gathering of "frozen" values (=coming from the command-line
     arguments) into a virtual read-only list of subsections and
     attributes.
  2. Merging of OAv2 from an input file and the frozen input.
  3. Merging of the results of step 2 together. Since the OAv2 merge
     is commutative and associative, it can be implemented as a reduce.
     However, GNU ld implements it as an accumulate because it does
     not support multithreading.
  Notes: the two merge phases also perform a marking of unsupported/
  invalid subsections and attributes. This marking can be used for
  debugging, and also more practically to drop unsupported optional
  subsections from the output.

* Phase 3, finalization of the output.
  1. Pruning of the unsupported/invalid subsections and attributes.
  2. Serialization of OAv2 data (also used by objcopy).
  Notes:
   - There is no translation of the merged OAv2 to GNU properties
     at this stage, as the GNU properties merge has already all the
     information that were translated in step 5 of stage 1.
   - The GNU properties are currently required as the runtime linker
     does not understand OAv2 yet.
   - Phase 3 should also include a compatibility check between the
     final merge result of the current link unit and input shared
     objects. I opted for postponing this compatibility check, and
     GNU properties merge will take care of it as it already does.

** Required subsections

Required subsections are processed slightly differently from the
optional subsections, as they cannot be pruned since they are mandatory,
hence an error will be raised by the linker if it is not recognized.

For now, the subsection for PAuth ABI is the only one use case, and
no merge is applied on the values. The values simply need to match.
This implementation choice might be challenged in the future if required
subsections can have the same diversity as optional subsections. If the
case arises, the refactoring to handle this new behavior should consist
in adding a new merge policy MERGE-EQUAL, or something similar. Some "if
required" should be added in the optional subsections merge logic to
error on any missing elements, or mismatch, and messages should also be
rephrased to point out that the error is for a required subsection.

** Important note regarding support for testing

In order to test this generic logic, AArch64's use cases are not
offering enough coverage, so a "GNU testing namespace" which corresponds
to the name of the subsection was introduced. It follows the following
pattern:
  gnu-testing-<XXXXXX>-MERGE-<POLICY>
with:
  - <XXXXXX>: an arbitrary name for your testing subsection.
  - <POLICY>: the name of the merging policy to apply on the values in
    the subsection. The currently supported merge policy are:
      * -MERGE-AND: bitwise AND applied on numerical values.
      * -MERGE-OR: bitwise OR applied on numerical values.
      * -MERGE-ADD: concatenates strings together with a '+' in-between.
    Note: "-MERGE-ADD" does not make really sense, and will very likely
    never be used for a real merge. Its only purpose is to test the
    correct handling of merges with strings.
Any subsection name matching neither names supported by the backend, nor
following the pattern corresponding GNU testing namespace will be considered
unknown and its status set to obj_attr_subsection_v2_unknown. This will
have for consequence the pruning of this subsection.

Additionally, the first two tags in gnu-testing namespace, GNUTestTag_0
and GNUTestTag_1, are known, and so have a name and can be initialized
to the default value ('0' or NULL) depending on the encoding specified
on the subsection. Any tags above 1 will be considered unknown, so will
be default-initialized in the same way but its status will be set to
obj_attr_v2_unknown. This behavior of the testing tags allows to test
the pruning of unknown attributes.
---
 bfd/elf-attrs.c    | 1505 +++++++++++++++++++++++++++++++++++++++++++-
 bfd/elf-attrs.h    |   59 ++
 bfd/elf-bfd.h      |   28 +
 bfd/elfxx-target.h |   24 +
 ld/ldelf.c         |    1 +
 5 files changed, 1614 insertions(+), 3 deletions(-)

diff --git a/bfd/elf-attrs.c b/bfd/elf-attrs.c
index b2cac0da6f2..c5046a4c910 100644
--- a/bfd/elf-attrs.c
+++ b/bfd/elf-attrs.c
@@ -18,6 +18,108 @@
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
    MA 02110-1301, USA.  */
 
+/* Design note regarding the merge of Object Attributes v2 during linkage
+
+   Entry point: _bfd_elf_link_setup_build_attributes
+
+   This patch adds all the generic logic to the linker to process OAv2.
+   The linker is an "advanced" consumer of OAv2. After parsing, it deduplicates
+   them, merge them, detect any compatibility issues, and finally translate them
+   to GNU properties.
+
+   ** Overall design
+
+   The OAv2 processing pipeline follows a map-reduce pattern. Obviously, the
+   actual processing in GNU ld is not multi-threaded, and the operations are not
+   necessarily executed directly one after another.
+
+   * Phase 1, map: successive per-file operations applied on the list of
+     compatible input objects.
+     1. Parsing of the OAv2 section's data (also used by objcopy).
+     2. Translation of relevant GNU properties to OAv2. This is required for the
+        backward-compatibility with input objects only marked using GNU
+        properties.
+     3. Sorting of the subsections and object attributes. Further operations
+        rely on the ordering to perform some optimization in the processing of
+        the data.
+     4. Deduplication of subsections and object attributes, and detection of any
+        conflict between duplicated subsections or tags.
+     5. Translation of relevant OAv2 to GNU properties for a forward
+        -compatibility with the GNU properties merge.
+
+   * Phase 2, reduce: OAv2 in input objects are merged together.
+     1. Gathering of "frozen" values (=coming from the command-line arguments)
+        into a virtual read-only list of subsections and attributes.
+     2. Merging of OAv2 from an input file and the frozen input.
+     3. Merging of the results of step 2 together. Since the OAv2 merge is
+        commutative and associative, it can be implemented as a reduce.
+        However, GNU ld implements it as an accumulate because it does not
+        support multithreading.
+     Notes: the two merge phases also perform a marking of unsupported/invalid
+     subsections and attributes. This marking can be used for debugging, and
+     also more practically to drop unsupported optional subsections from the
+     output.
+
+   * Phase 3, finalization of the output.
+     1. Pruning of the unsupported/invalid subsections and attributes.
+     2. Serialization of OAv2 data (also used by objcopy).
+     Notes:
+      - There is no translation of the merged OAv2 to GNU properties at this
+        stage, as the GNU properties merge has already all the information that
+        were translated in step 5 of stage 1.
+      - The GNU properties are currently required as the runtime linker does
+        not understand OAv2 yet.
+      - Phase 3 should also include a compatibility check between the final
+        merge result of the current link unit and input shared objects. I opted
+        for postponing this compatibility check, and GNU properties merge will
+        take care of it as it already does.
+
+   ** Required subsections
+
+   Required subsections are processed slightly differently from the optional
+   subsections, as they cannot be pruned since they are mandatory, hence an
+   error will be raised by the linker if it is not recognized.
+
+   For now, the subsection for PAuth ABI is the only one use case, and no merge
+   is applied on the values. The values simply need to match.
+   This implementation choice might be challenged in the future if required
+   subsections can have the same diversity as optional subsections. If the case
+   arises, the refactoring to handle this new behavior should consist in adding
+   a new merge policy MERGE-EQUAL, or something similar. Some "if required"
+   should be added in the optional subsections merge logic to error on any
+   missing elements, or mismatch, and messages should also be rephrased to point
+   out that the error is for a required subsection.
+
+   ** Important note regarding support for testing
+
+   In order to test this generic logic, AArch64's use cases are not offering
+   enough coverage, so a "GNU testing namespace" which corresponds to the name
+   of the subsection was introduced. It follows the following pattern:
+     gnu-testing-<XXXXXX>-MERGE-<POLICY>
+   with:
+     - <XXXXXX>: an arbitrary name for your testing subsection.
+     - <POLICY>: the name of the merging policy to apply on the values in the
+       subsection. The currently supported merge policy are:
+         * -MERGE-AND: bitwise AND applied on numerical values.
+         * -MERGE-OR: bitwise OR applied on numerical values.
+         * -MERGE-ADD: concatenates strings together with a '+' in-between.
+       Note: "-MERGE-ADD" does not make really sense, and will very likely never
+       be used for a real merge. Its only purpose is to test the correct
+       handling of merges with strings.
+   Any subsection name matching neither names supported by the backend, nor
+   following the pattern corresponding GNU testing namespace will be considered
+   unknown and its status set to obj_attr_subsection_v2_unknown. This will have
+   for consequence the pruning of this subsection.
+
+   Additionally, the first two tags in gnu-testing namespace, GNUTestTag_0 and
+   GNUTestTag_1, are known, and so have a name and can be initialized to the
+   default value ('0' or NULL) depending on the encoding specified on the
+   subsection. Any tags above 1 will be considered unknown, so will be default
+   -initialized in the same way but its status will be set to obj_attr_v2_unknown.
+   This behavior of the testing tags allows to test the pruning of unknown
+   attributes.  */
+
+
 #include "sysdep.h"
 #include "bfd.h"
 #include "doubly-linked-list.h"
@@ -411,6 +513,98 @@ bfd_elf_set_obj_attr_contents (bfd *abfd, bfd_byte *buffer, bfd_vma size)
     abort ();
 }
 
+/* Structure storing the result of a search in the list of input BFDs.
+   - the pointer to the BFD.
+   - the pointer to the section containing the object attributes.  */
+typedef struct
+{
+  bfd *pbfd;
+  bool has_build_attributes;
+  asection *sec;
+} bfd_search_result_t;
+
+/* Checks whether a BFD contains object attributes, and if so search for the
+   relevant section storing them.  */
+static bool
+bfd_has_build_attributes (bfd *abfd, bfd_search_result_t *res)
+{
+  if (elf_obj_attr_subsections (abfd).size == 0)
+    return false;
+  res->has_build_attributes = true;
+
+  const char *sec_name = get_elf_backend_data (abfd)->obj_attrs_section;
+  if ((res->sec = bfd_get_section_by_name (abfd, sec_name)) == NULL)
+    return false;
+  return true;
+}
+
+/* Returns True if the given BFD is an ELF object with the current backend
+   machine code, non-dynamic (i.e. not a shared library), and has sections.
+   False otherwise.
+   Note: this function is a convenient encapsulation of the predicate used to
+   search for objects containing object attributes in the list of BFDs.  */
+static bool
+bfd_is_non_dynamic_elf_object (struct bfd_link_info *info,
+			       bfd *abfd)
+{
+  const struct elf_backend_data *output_bfd
+    = get_elf_backend_data (info->output_bfd);
+  unsigned int elfclass = output_bfd->s->elfclass;
+  int elf_machine_code = output_bfd->elf_machine_code;
+  return (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+	  && abfd->section_count != 0
+	  && (abfd->flags & DYNAMIC) == 0
+	  && elf_machine_code == get_elf_backend_data (abfd)->elf_machine_code
+	  && elfclass == get_elf_backend_data (abfd)->s->elfclass);
+}
+
+/* Search for the first input object file containing object attributes.  */
+static bfd_search_result_t
+bfd_linear_search_one_with_build_attributes (struct bfd_link_info *info)
+{
+  bfd_search_result_t res = {
+    .pbfd = NULL,
+    .has_build_attributes = false,
+    .sec = NULL,
+  };
+
+  for (bfd *abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
+    if (bfd_is_non_dynamic_elf_object (info, abfd))
+      {
+	res.pbfd = abfd;
+	if (bfd_has_build_attributes (abfd, &res))
+	  break;
+      }
+  return res;
+}
+
+/* Create a build attributes section for the given bfd input.  */
+static asection *
+create_build_attributes_section (struct bfd_link_info *info,
+				 bfd *ebfd)
+{
+  asection *sec;
+  const char *sec_name = get_elf_backend_data (ebfd)->obj_attrs_section;
+  sec = bfd_make_section_with_flags (ebfd,
+				     sec_name,
+				     (SEC_READONLY
+				      | SEC_HAS_CONTENTS
+				      | SEC_DATA));
+  if (sec == NULL)
+    info->callbacks->einfo (
+      _("%F%P: failed to create %s section\n"), sec_name);
+
+  unsigned align = (bfd_get_mach (ebfd) & bfd_mach_aarch64_ilp32) ? 2 : 3;
+  if (!bfd_set_section_alignment (sec, align))
+    info->callbacks->einfo (_("%F%pA: failed to align section\n"), sec);
+
+  elf_section_type (sec) = get_elf_backend_data (ebfd)->obj_attrs_section_type;
+
+  bfd_set_section_size (sec, bfd_elf_obj_attr_size (ebfd));
+
+  return sec;
+}
+
 /* The first two tags in gnu-testing namespace are known, and so have a name and
    can be initialized to the default value ('0' or NULL) depending on the
    encoding specified on the subsection.  Any tags above 1 will be considered
@@ -454,8 +648,8 @@ static known_subsection_v2 obj_attr_v2_known_gnu_subsections[] =
   /* Note for the future: GNU subsections can be added here below.  */
 };
 
-/* Return True if the given subsection name is part of the reserved "gnu-testing"
-   namespace.  */
+/* Return True if the given subsection name is part of the reserved testing
+   namespace, i.e. SUBSEC_NAME begins with "gnu-testing".  */
 static bool
 gnu_testing_namespace (const char *subsec_name)
 {
@@ -555,6 +749,1279 @@ oav2_encoding_to_string (obj_attr_encoding_v2 encoding)
   return (encoding == OA_ENC_ULEB128) ? "ULEB128" : "NTBS";
 }
 
+/* Initialize the given ATTR with its default value coming from the known tag
+   registry.  */
+static void
+oav2_attr_overwrite_with_default (struct bfd_link_info *info,
+				  obj_attr_subsection_v2 *subsec,
+				  obj_attr_v2 *attr)
+{
+  const struct elf_backend_data *be = get_elf_backend_data (info->output_bfd);
+
+  const obj_attr_info_t *tag_info =
+    known_obj_attr_v2_find_by_tag (be, subsec->name, attr->tag);
+  if (tag_info == NULL)
+    {
+      attr->status = obj_attr_v2_unknown;
+      if (subsec->encoding == OA_ENC_ULEB128)
+	attr->vals.uint_val = 0;
+      else
+	attr->vals.string_val = NULL;
+      return;
+    }
+
+  if (be->obj_attr_v2_default_value != NULL
+      && be->obj_attr_v2_default_value (info, tag_info, subsec, attr))
+    {}
+  else if (subsec->encoding == OA_ENC_NTBS)
+    {
+      if (tag_info->default_value.val.string != NULL)
+	{
+	  if (attr->vals.string_val != NULL)
+	    free ((void *) attr->vals.string_val);
+	  attr->vals.string_val = strdup (tag_info->default_value.val.string);
+	}
+      else
+	attr->vals.string_val = NULL;
+    }
+  else
+    attr->vals.uint_val = tag_info->default_value.val.u32;
+}
+
+/* Create a new attribute with the same key (=tag) as ATTR, and initialized with
+   its default value from the known tag registry.  */
+static obj_attr_v2 *
+oav2_attr_default (struct bfd_link_info *info,
+		   obj_attr_subsection_v2 *subsec,
+		   obj_attr_v2 *attr)
+{
+  obj_attr_v2 *new_attr = _bfd_elf_obj_attr_v2_copy (attr, subsec->encoding);
+  oav2_attr_overwrite_with_default (info, subsec, new_attr);
+  return new_attr;
+}
+
+/* The currently supported merge policy in the testing GNU namespace.
+   - bitwise AND: apply bitwise AND.
+   - bitwise OR: apply bitwise OR.
+   - String-ADD: concatenates strings together with a '+' in-between.
+   Note: Such policies should only be used for testing.  */
+typedef enum {
+  SUBSECTION_TESTING_MERGE_UNSUPPORTED = 0,
+  SUBSECTION_TESTING_MERGE_AND_POLICY = 1,
+  SUBSECTION_TESTING_MERGE_OR_POLICY = 2,
+  SUBSECTION_TESTING_MERGE_ADD_POLICY = 3,
+} gnu_testing_merge_policy;
+
+/* Determine which merge policy will be applied to SUBSEC.  The GNU policy are
+   detected from the name of the subsection. It should follow the following
+   pattern: "gnu-testing-XXXXXX-MERGE-<POLICY>".
+   Return one of the known merge policy if recognised, UNSUPPORTED otherwise.  */
+static gnu_testing_merge_policy
+gnu_testing_merge_subsection (const char *subsec_name)
+{
+  if (! gnu_testing_namespace (subsec_name))
+    return SUBSECTION_TESTING_MERGE_UNSUPPORTED;
+
+  size_t subsec_name_len = strlen (subsec_name);
+  if (strcmp ("-MERGE-AND", subsec_name + subsec_name_len - 10) == 0)
+    return SUBSECTION_TESTING_MERGE_AND_POLICY;
+  else if (strcmp ("-MERGE-OR", subsec_name + subsec_name_len - 9) == 0)
+    return SUBSECTION_TESTING_MERGE_OR_POLICY;
+  else if (strcmp ("-MERGE-ADD", subsec_name + subsec_name_len - 10) == 0)
+    return SUBSECTION_TESTING_MERGE_ADD_POLICY;
+  else
+    return SUBSECTION_TESTING_MERGE_UNSUPPORTED;
+}
+
+/* Merge policy Integer-AND: apply bitwise AND between REF and RHS.  */
+obj_attr_v2_merge_result
+obj_attr_v2_tag_merge_AND (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+			   bfd *abfd ATTRIBUTE_UNUSED,
+			   obj_attr_subsection_v2 *subsec,
+			   obj_attr_v2 *ref, obj_attr_v2 *rhs,
+			   obj_attr_v2 *frozen ATTRIBUTE_UNUSED)
+{
+  BFD_ASSERT (subsec->encoding == OA_ENC_ULEB128);
+
+  obj_attr_v2_merge_result res = {
+    .merge = true,
+    .vals.uint_val = 0,
+    .reason = MERGE_OK,
+  };
+
+  uint32_t original_value = ref->vals.uint_val;
+  res.vals.uint_val = (ref->vals.uint_val & rhs->vals.uint_val);
+  res.merge = (res.vals.uint_val != original_value);
+  if (res.vals.uint_val == original_value)
+    res.reason = SAME_VALUE_AS_REF;
+
+  return res;
+}
+
+/* Merge policy Integer-OR: apply bitwise OR between REF and RHS.  */
+static obj_attr_v2_merge_result
+obj_attr_v2_tag_merge_OR (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+			  bfd *abfd ATTRIBUTE_UNUSED,
+			  obj_attr_subsection_v2 *subsec,
+			  obj_attr_v2 *ref, obj_attr_v2 *rhs,
+			  obj_attr_v2 *frozen ATTRIBUTE_UNUSED)
+{
+  BFD_ASSERT (subsec->encoding == OA_ENC_ULEB128);
+
+  obj_attr_v2_merge_result res = {
+    .merge = true,
+    .vals.uint_val = 0,
+    .reason = MERGE_OK,
+  };
+
+  uint32_t original_value = ref->vals.uint_val;
+  res.vals.uint_val = (ref->vals.uint_val | rhs->vals.uint_val);
+  res.merge = (res.vals.uint_val != original_value);
+  if (res.vals.uint_val == original_value)
+    res.reason = SAME_VALUE_AS_REF;
+
+  return res;
+}
+
+/* Merge policy String-ADD: concatenates strings from REF and RHS together
+   adding a '+' character in-between.  */
+static obj_attr_v2_merge_result
+obj_attr_v2_tag_merge_ADD (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+			   bfd *abfd ATTRIBUTE_UNUSED,
+			   obj_attr_subsection_v2 *subsec,
+			   obj_attr_v2 *ref, obj_attr_v2 *rhs,
+			   obj_attr_v2 *frozen)
+{
+  BFD_ASSERT (subsec->encoding == OA_ENC_NTBS);
+
+  size_t frozen_s_size = 0;
+  if (frozen && frozen->vals.string_val)
+    frozen_s_size = strlen (frozen->vals.string_val);
+
+  obj_attr_v2_merge_result res = {
+    .merge = false,
+    .vals.uint_val = 0,
+    .reason = MERGE_OK,
+  };
+
+  if (ref->vals.string_val && rhs->vals.string_val)
+    {
+      res.merge = true;
+      size_t ref_s_size = strlen (ref->vals.string_val);
+      size_t rhs_s_size = strlen (rhs->vals.string_val);
+      char *buffer = malloc (ref_s_size + 1 + rhs_s_size + 1);
+      res.vals.string_val = buffer;
+      memcpy (buffer, ref->vals.string_val, ref_s_size);
+      buffer += ref_s_size;
+      *buffer = '+';
+      ++buffer;
+      memcpy (buffer, rhs->vals.string_val, rhs_s_size + 1);
+    }
+  else if (ref->vals.string_val)
+    {
+      /* Nothing to do, frozen (if not NULL) should already be merged with
+	 it.  */
+      res.reason = SAME_VALUE_AS_REF;
+    }
+  else if (rhs->vals.string_val)
+    {
+      res.merge = true;
+      if (frozen_s_size == 0)
+	{
+	  res.vals.string_val = rhs->vals.string_val;
+	  rhs->vals.string_val = NULL;
+	}
+      else
+	{
+	  size_t rhs_s_size = strlen (rhs->vals.string_val);
+	  char *buffer = malloc (frozen_s_size + 1 + rhs_s_size + 1);
+	  res.vals.string_val = buffer;
+	  memcpy (buffer, frozen->vals.string_val, frozen_s_size);
+	  buffer += frozen_s_size;
+	  *buffer = '+';
+	  ++buffer;
+	  memcpy (buffer, rhs->vals.string_val, rhs_s_size + 1);
+	}
+    }
+  return res;
+}
+
+/* Return the merge result between attributes LHS, RHS and FROZEN.  */
+static obj_attr_v2_merge_result
+oav2_attr_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, bool frozen_as_abfd)
+{
+  obj_attr_v2_merge_result res = {
+    .merge = false,
+    .vals.uint_val = 0,
+    .reason = MERGE_OK,
+  };
+
+  gnu_testing_merge_policy policy;
+
+  if (get_elf_backend_data (abfd)->obj_attr_v2_tag_merge != NULL)
+    {
+      if (frozen_as_abfd)
+	{
+	  obj_attr_v2 *tmp = lhs;
+	  lhs = rhs;
+	  rhs = tmp;
+	}
+      res = get_elf_backend_data (abfd)->obj_attr_v2_tag_merge (info, abfd,
+	subsec, lhs, rhs, frozen);
+    }
+
+  /* Note for the future: the merge of generic object attributes should be
+     added here, between the architecture-specific merge, and the reserved GNU
+     testing namespace.  */
+
+  /* GNU testing merge policies are looked up last. If MERGE_OK is detected, the
+     subsection is considered unmergeable.  */
+  if (! res.merge && (res.reason == UNSUPPORTED || res.reason == MERGE_OK))
+    {
+      if ((policy = gnu_testing_merge_subsection (subsec->name))
+	   != SUBSECTION_TESTING_MERGE_UNSUPPORTED)
+	{
+	  /* Only the first two attributes can be merged, others won't and will
+	     be discarded.  */
+	  if (lhs->tag <= 1)
+	    {
+	      if (policy == SUBSECTION_TESTING_MERGE_AND_POLICY)
+		res = obj_attr_v2_tag_merge_AND (info, abfd, subsec, lhs, rhs,
+		  frozen);
+	      else if (policy == SUBSECTION_TESTING_MERGE_OR_POLICY)
+		res = obj_attr_v2_tag_merge_OR (info, abfd, subsec, lhs, rhs,
+		  frozen);
+	      else if (policy == SUBSECTION_TESTING_MERGE_ADD_POLICY)
+		res = obj_attr_v2_tag_merge_ADD (info, abfd, subsec, lhs, rhs,
+		  frozen);
+	    }
+	  else
+	    res.reason = UNSUPPORTED;
+	}
+    }
+
+  return res;
+}
+
+/* Append a new default-initialized attribute with the same key as AREF to the
+   given subsection.  */
+static void
+oav2_subsection_append_attr_default (struct bfd_link_info *info,
+				     obj_attr_subsection_v2 *s_abfd_missing,
+				     obj_attr_v2 *a_ref)
+{
+  obj_attr_v2 *new_attr = oav2_attr_default (info, s_abfd_missing, a_ref);
+  LINKED_LIST_APPEND(obj_attr_v2) (s_abfd_missing, new_attr);
+}
+
+/* Return a new default-initialized subsection with the same parameters as
+   SUBSEC.  */
+static obj_attr_subsection_v2 *
+oav2_subsection_default_new (struct bfd_link_info *info,
+			     obj_attr_subsection_v2 *subsec)
+{
+  obj_attr_subsection_v2 *new_subsec =
+    _bfd_elf_obj_attr_subsection_v2_init (subsec->name, subsec->scope,
+      subsec->optional, subsec->encoding);
+
+  for (obj_attr_v2 *attr = subsec->first;
+       attr != NULL;
+       attr = attr->next)
+    oav2_subsection_append_attr_default (info, new_subsec, attr);
+
+  return new_subsec;
+}
+
+/* Report missing required attribute with key TAG in subsection SREF.  */
+static void
+report_missing_required_obj_attr (struct bfd_link_info *info,
+				  bfd *abfd,
+				  obj_attr_subsection_v2 *s_ref,
+				  obj_attr_tag_t tag)
+{
+  const struct elf_backend_data *be = get_elf_backend_data (abfd);
+  const char *tag_s = obj_attr_v2_tag_to_string (be, s_ref->name, tag);
+  if (tag_s)
+    info->callbacks->einfo (
+      _("%X%pB: error: missing required object attribute '%s' in subsection "
+	"'%s'\n" ), abfd, tag_s, s_ref->name);
+  else
+    info->callbacks->einfo (
+      _("%X%pB: error: missing required object attribute 'Tag_unknown_%u' in "
+	"subsection '%s'\n" ), abfd, tag, s_ref->name);
+}
+
+/* Report required attribute A_ABFD mismatching with A_REF.  */
+static void
+report_mismatching_required_obj_attr (struct bfd_link_info *info,
+				      bfd *ref_bfd,
+				      bfd *abfd,
+				      obj_attr_subsection_v2 *s_ref,
+				      obj_attr_v2 *a_ref,
+				      obj_attr_v2 *a_abfd)
+{
+  const struct elf_backend_data *be = get_elf_backend_data (abfd);
+  const char* tag_s = obj_attr_v2_tag_to_string (be, s_ref->name, a_ref->tag);
+  if (s_ref->encoding == OA_ENC_ULEB128)
+    {
+      if (tag_s)
+	info->callbacks->einfo (
+	  _("%X%pB, %pB: error: mismatching values 0x%x and 0x%x for "
+	    "required object attribute '%s' in subsection '%s'\n"),
+	  ref_bfd, abfd, a_ref->vals.uint_val, a_abfd->vals.uint_val,
+	  obj_attr_v2_tag_to_string (be, s_ref->name, a_abfd->tag), s_ref->name);
+      else
+	info->callbacks->einfo (
+	  _("%X%pB, %pB: error: mismatching values 0x%x and 0x%x for "
+	    "required object attribute 'Tag_unknown_%u' in subsection '%s'\n"),
+	  ref_bfd, abfd, a_ref->vals.uint_val, a_abfd->vals.uint_val,
+	  a_abfd->tag, s_ref->name);
+    }
+  else
+    {
+      if (tag_s)
+	info->callbacks->einfo (
+	  _("%X%pB, %pB: error: mismatching values '%s' and '%s' for "
+	    "required object attribute '%s' in subsection '%s'\n"),
+	  ref_bfd, abfd, a_ref->vals.string_val, a_abfd->vals.string_val,
+	  obj_attr_v2_tag_to_string (be, s_ref->name, a_abfd->tag), s_ref->name);
+      else
+	info->callbacks->einfo (
+	  _("%X%pB, %pB: error: mismatching values '%s' and '%s' for "
+	    "required object attribute 'Tag_unknown_%u' in subsection '%s'\n"),
+	  ref_bfd, abfd, a_ref->vals.string_val, a_abfd->vals.string_val,
+	  a_abfd->tag, s_ref->name);
+    }
+}
+
+/* Attempt a perfect match between subsections S_REF and S_ABFD, and reports
+   errors for any mismatch.  Return S_REF is the subsections match, NULL
+   otherwise.*/
+static obj_attr_subsection_v2 *
+oav2_subsection_perfect_match (struct bfd_link_info *info,
+			       bfd *ref_bfd, bfd *abfd,
+			       obj_attr_subsection_v2 *s_ref,
+			       obj_attr_subsection_v2 *s_abfd)
+{
+  bool success = true;
+  obj_attr_v2 *a_ref = s_ref->first;
+  obj_attr_v2 *a_abfd = s_abfd->first;
+  while (a_ref != NULL && a_abfd != NULL)
+    {
+      if (a_ref->tag < a_abfd->tag)
+	{
+	  success = false;
+	  report_missing_required_obj_attr (info, abfd, s_ref, a_ref->tag);
+	  a_ref = a_ref->next;
+	}
+      else if (a_ref->tag > a_abfd->tag)
+	{
+	  success = false;
+	  report_missing_required_obj_attr (info, ref_bfd, s_ref, a_abfd->tag);
+	  a_abfd = a_abfd->next;
+	}
+      else
+	{
+	  if (s_ref->encoding == OA_ENC_ULEB128)
+	    {
+	      if (a_ref->vals.uint_val != a_abfd->vals.uint_val)
+		{
+		  success = false;
+		  report_mismatching_required_obj_attr (info, ref_bfd, abfd,
+		    s_ref, a_ref, a_abfd);
+		}
+	    }
+	  else if (s_ref->encoding == OA_ENC_NTBS)
+	    {
+	      if (strcmp (a_ref->vals.string_val, a_abfd->vals.string_val) != 0)
+		{
+		  success = false;
+		  report_mismatching_required_obj_attr (info, ref_bfd, abfd,
+		    s_ref, a_ref, a_abfd);
+		}
+	    }
+	  a_ref = a_ref->next;
+	  a_abfd = a_abfd->next;
+	}
+    }
+
+  for (; a_abfd != NULL; a_abfd = a_abfd->next)
+    {
+      success = false;
+      report_missing_required_obj_attr (info, ref_bfd, s_ref, a_abfd->tag);
+    }
+
+  for (; a_ref != NULL; a_ref = a_ref->next)
+    {
+      success = false;
+      report_missing_required_obj_attr (info, abfd, s_ref, a_ref->tag);
+    }
+
+  return success ? s_ref : NULL;
+}
+
+/* Search for the attribute with TAG into a list of attributes.  The attributes
+   list is assumed to be sorted.  Return the pointer to the attribute with TAG
+   if found, NULL otherwise.  */
+static obj_attr_v2 *
+oav2_search_by_tag (obj_attr_v2 *attr_first, obj_attr_tag_t tag)
+{
+  for (obj_attr_v2* attr = attr_first; attr != NULL; attr = attr->next)
+    {
+      if (attr->tag == tag)
+	return attr;
+      else if (attr->tag > tag)
+	break;
+    }
+  return NULL;
+}
+
+/* Merge optional subsection S_ABFD into S_REF.  S_FROZEN is used to report any
+   issue with the selected configuration, and to force the merge value to a
+   specific value if needed.  */
+static
+obj_attr_subsection_v2 *
+handle_optional_subsection_merge (struct bfd_link_info *info,
+				  bfd *ref_bfd, bfd *abfd,
+				  obj_attr_subsection_v2 *s_ref,
+				  obj_attr_subsection_v2 *s_abfd,
+				  obj_attr_subsection_v2 *s_frozen)
+{
+  (void) info;
+  bool frozen_as_abfd = (ref_bfd == abfd);
+  obj_attr_v2 *a_ref = s_ref->first;
+  obj_attr_v2 *a_abfd = s_abfd->first;
+  obj_attr_v2 *a_frozen_first = (s_frozen != NULL) ? s_frozen->first : NULL;
+  while (a_ref != NULL && a_abfd != NULL)
+    {
+      uint32_t searched_frozen_tag =
+	(a_ref->tag < a_abfd->tag)
+	? a_ref->tag
+	: a_abfd->tag;
+      obj_attr_v2 *a_frozen =
+	oav2_search_by_tag (a_frozen_first, searched_frozen_tag);
+      if (a_frozen != NULL)
+	a_frozen_first = a_frozen;
+
+      if (a_ref->tag < a_abfd->tag)
+	{
+	  obj_attr_v2 *a_default = oav2_attr_default (info, s_abfd, a_ref);
+	  obj_attr_v2_merge_result res =
+	    oav2_attr_merge (info, abfd, s_ref, a_ref, a_default, a_frozen,
+			     frozen_as_abfd);
+	  _bfd_elf_obj_attr_v2_free (a_default, s_ref->encoding);
+	  if (res.merge)
+	    a_ref->vals = res.vals;
+	  else if (res.reason == UNSUPPORTED)
+	    a_ref->status = obj_attr_v2_unknown;
+	  a_ref = a_ref->next;
+	}
+      else if (a_ref->tag > a_abfd->tag)
+	{
+	  obj_attr_v2 *a_default = oav2_attr_default (info, s_ref, a_abfd);
+	  obj_attr_v2_merge_result res =
+	    oav2_attr_merge (info, abfd, s_ref, a_default, a_abfd, a_frozen,
+			     frozen_as_abfd);
+	  if (res.merge || res.reason == SAME_VALUE_AS_REF)
+	    {
+	      a_default->vals = res.vals;
+	      LINKED_LIST_INSERT_BEFORE(obj_attr_v2) (s_ref, a_default, a_ref);
+	    }
+	  else
+	    _bfd_elf_obj_attr_v2_free (a_default, s_ref->encoding);
+	  a_abfd = a_abfd->next;
+	}
+      else
+	{
+	  obj_attr_v2_merge_result res =
+	    oav2_attr_merge (info, abfd, s_ref, a_ref, a_abfd, a_frozen,
+			     frozen_as_abfd);
+	  if (res.merge)
+	    a_ref->vals = res.vals;
+	  else if (res.reason == UNSUPPORTED)
+	    a_ref->status = obj_attr_v2_unknown;
+	  a_ref = a_ref->next;
+	  a_abfd = a_abfd->next;
+	}
+    }
+
+  for (; a_abfd != NULL; a_abfd = a_abfd->next)
+    {
+      obj_attr_v2 *a_default = oav2_attr_default (info, s_ref, a_abfd);
+      obj_attr_v2_merge_result res =
+	oav2_attr_merge (info, abfd, s_ref, a_default, a_abfd, NULL, false);
+      if (res.merge || res.reason == SAME_VALUE_AS_REF)
+	{
+	  a_default->vals = res.vals;
+	  LINKED_LIST_APPEND(obj_attr_v2) (s_ref, a_default);
+	}
+      else
+	_bfd_elf_obj_attr_v2_free (a_default, s_ref->encoding);
+    }
+
+  for (; a_ref != NULL; a_ref = a_ref->next)
+    {
+      obj_attr_v2 *a_frozen = oav2_search_by_tag (a_frozen_first, a_ref->tag);
+      if (a_frozen != NULL)
+	a_frozen_first = a_frozen;
+
+      obj_attr_v2 *a_default = oav2_attr_default (info, s_abfd, a_ref);
+      obj_attr_v2_merge_result res =
+	oav2_attr_merge (info, abfd, s_ref, a_ref, a_default, a_frozen,
+			 frozen_as_abfd);
+      _bfd_elf_obj_attr_v2_free (a_default, s_ref->encoding);
+      if (res.merge)
+	a_ref->vals = res.vals;
+      else if (res.reason == UNSUPPORTED)
+	a_ref->status = obj_attr_v2_unknown;
+    }
+
+  return s_ref;
+}
+
+/* Merge case 1: S_ABFD and S_REF exists, so merge S_ABFD into S_REF.  */
+static obj_attr_subsection_v2 *
+handle_subsection_merge (struct bfd_link_info *info, bfd *ref_bfd, bfd *abfd,
+  obj_attr_subsection_v2 *s_ref, obj_attr_subsection_v2 *s_abfd,
+  obj_attr_subsection_v2 *s_frozen)
+{
+  if (! s_ref->optional)
+    return oav2_subsection_perfect_match (info, ref_bfd, abfd, s_ref, s_abfd);
+  return handle_optional_subsection_merge (info, ref_bfd, abfd, s_ref, s_abfd, s_frozen);
+}
+
+/* Merge case 2: S_ABFD does not exist, but S_REF does.
+   1. Create a new default-initialized S_ABFD.
+   2. Merge S_ABFD into S_REF.  */
+static bool
+handle_subsection_missing (struct bfd_link_info *info, bfd *ref_bfd, bfd *abfd,
+  obj_attr_subsection_v2 *s_ref, obj_attr_subsection_v2 *s_frozen)
+{
+  if (! s_ref->optional)
+    {
+      info->callbacks->einfo (_("%X%pB: error: missing required object "
+	"attributes subsection %s\n" ), abfd, s_ref->name);
+      return false;
+    }
+
+  /* Compute default values of the missing attributes in ABFD, but present in
+     REF, and merge ABFD's generated subsection with the one of REF.  */
+  obj_attr_subsection_v2 *s_abfd = oav2_subsection_default_new (info, s_ref);
+  obj_attr_subsection_v2 *merged =
+    handle_subsection_merge (info, ref_bfd, abfd, s_ref, s_abfd, s_frozen);
+  _bfd_elf_obj_attr_subsection_v2_free (s_abfd);
+  return merged != NULL;
+}
+
+/* Merge case 3: S_ABFD does not have a S_REF equivalent.
+   1. Create a new default-initialized S_REF subsection.
+   2. Merge S_ABFD into S_REF.
+   3. Insert S_REF into REF.  */
+static bool
+handle_subsection_additional (struct bfd_link_info *info,
+			      bfd *ref_bfd, bfd *abfd,
+			      obj_attr_subsection_v2 *s_ref_next,
+			      obj_attr_subsection_v2 *s_abfd,
+			      obj_attr_subsection_v2 *s_frozen)
+{
+  if (! s_abfd->optional)
+    {
+      info->callbacks->einfo (_("%X%pB: error: missing required object "
+	"attributes subsection %s\n" ), ref_bfd, s_abfd->name);
+      return false;
+    }
+
+  /* Compute default values of the missing attributes in REF, but present in
+     ABFD, and merge REF's generated subsection with the one of ABFD.  */
+  obj_attr_subsection_v2 *s_ref = oav2_subsection_default_new (info, s_abfd);
+  obj_attr_subsection_v2 *s_merged =
+    handle_subsection_merge (info, ref_bfd, abfd, s_ref, s_abfd, s_frozen);
+  BFD_ASSERT (s_merged == s_ref); // FIXME: I am not sure whether that it is true or false. If true, eliminate next free.
+  if (s_ref != s_merged)
+    _bfd_elf_obj_attr_subsection_v2_free (s_ref);
+  if (s_merged != NULL)
+    {
+      LINKED_LIST_INSERT_BEFORE(obj_attr_subsection_v2) (
+	&elf_obj_attr_subsections (ref_bfd), s_merged, s_ref_next);
+    }
+  return (s_merged != NULL);
+}
+
+/* Check for mismatch between the parameters of subsections S1 and S2.
+   Note: F1 can be null when comparing FROZEN and the first object file used to
+   store the merge result.  If an error is reported, it means that one of the
+   definition of S1 or S2 is corrupted.  Most likely S2 because it is a user
+   input, or S1 if it is a programmation error of FROZEN. In the second case,
+   please raise a bug to binutils bug tracker.  */
+static bool
+oav2_subsection_mismatching_params (struct bfd_link_info *info,
+				    bfd *f1, bfd *f2,
+				    obj_attr_subsection_v2 *s1,
+				    obj_attr_subsection_v2 *s2)
+{
+  if (! gnu_testing_namespace (s1->name))
+    {
+      /* Check whether the subsection is known, and if so, match against the
+	 expected properties.
+	 Note: this piece of code must be guarded against gnu-testing
+	 subsections, as the backend method looks up at the known subsections.
+	 Since the "fictive" entry for gnu-testing known subsection has random
+	 values for its encoding and optionality, it won't be able to detect
+	 mismatching parameters correctly.  */
+      bool match_known = true;
+      if (get_elf_backend_data (f2)->obj_attr_subsection_v2_match_known != NULL)
+	match_known = get_elf_backend_data (f2)
+	  ->obj_attr_subsection_v2_match_known (info, f2, s2);
+      if (! match_known)
+	return true;
+    }
+
+  bool mismatch = (s1->encoding != s2->encoding
+		   || s1->optional != s2->optional);
+
+  if (mismatch)
+    {
+      if (f1 != NULL)
+	info->callbacks->einfo (_("%X%pB, %pB: error: parameters of subsection"
+	  " '%s' are mismatching. (%s, %s) VS (%s, %s)\n"), f1, f2, s1->name,
+	  oav2_comprehension_to_string (s1->optional),
+	  oav2_encoding_to_string (s1->encoding),
+	  oav2_comprehension_to_string (s2->optional),
+	  oav2_encoding_to_string (s2->encoding));
+      else
+	info->callbacks->einfo (_("%X%pB: error: parameters of subsection"
+	  " '%s' are corrupted. (%s, %s) VS (%s, %s)\n"), f2, s1->name,
+	  oav2_comprehension_to_string (s1->optional),
+	  oav2_encoding_to_string (s1->encoding),
+	  oav2_comprehension_to_string (s2->optional),
+	  oav2_encoding_to_string (s2->encoding));
+    }
+
+  return mismatch;
+}
+
+/* Merge object attributes from FROZEN into the object file REF_BFD.
+   Note: this function is called only once before starting the merge process
+   between the object files. REF_BFD is used to store the result of the merge,
+   but REF_BFD is also an input file, so any mismatch against FROZEN should be
+   raised before the values of REF_BFD be modified.  */
+static bool
+oav2_subsections_merge_frozen (struct bfd_link_info *info,
+			       bfd *abfd,
+			       obj_attr_subsection_v2 *s_frozen)
+{
+  if (s_frozen == NULL)
+    return true;
+
+  bool success = true;
+
+  obj_attr_subsection_v2 *s_abfd = elf_obj_attr_subsections (abfd).first;
+  while (s_frozen != NULL && s_abfd != NULL)
+    {
+      int cmp = strcmp (s_abfd->name, s_frozen->name);
+      if (cmp < 0) /* ABFD has a subsection that FROZEN doesn't have.  */
+	{
+	  /* No need to try to merge anything here.  */
+	  s_abfd = s_abfd->next;
+	}
+      else if (cmp > 0) /* FROZEN has a subsection that ABFD doesn't have.  */
+	{
+	  success &= handle_subsection_additional (info, abfd, abfd,
+	    s_abfd, s_frozen, s_frozen);
+	  s_frozen = s_frozen->next;
+	}
+      else /* Both ABFD and frozen have the subsection.  */
+	{
+	  bool mismatch = oav2_subsection_mismatching_params (info, NULL, abfd,
+	    s_frozen, s_abfd);
+	  success &= ! mismatch;
+	  if (mismatch)
+	    /* FROZEN cannot be corrupted as it is generated from the command
+	       line arguments.  If it is corrupted, it is a bug.  */
+	    s_abfd->status = obj_attr_subsection_v2_corrupted;
+	  else
+	    success &= (handle_subsection_merge (info, abfd, abfd,
+	      s_abfd, s_frozen, s_frozen) != NULL);
+
+	  s_abfd = s_abfd->next;
+	  s_frozen = s_frozen->next;
+	}
+    }
+
+  /* No need to go through the remaining sections of ABFD, only mismatch against
+     FROZEN are interesting.  */
+
+  for (; s_frozen != NULL; s_frozen = s_frozen->next)
+    success &= handle_subsection_additional (info, abfd, abfd,
+      elf_obj_attr_subsections (abfd).last, s_frozen, s_frozen);
+
+  return success;
+}
+
+/* Merge object attributes from object file ABFD into REF_BFD.  */
+static bool
+oav2_subsections_merge (struct bfd_link_info *info, bfd *ref_bfd, bfd *abfd)
+{
+  bool success = true;
+  obj_attr_subsection_list *out_frozen_subsecs =
+    &elf_obj_attr_subsections (info->output_bfd);
+  obj_attr_subsection_list *abfd_subsecs = &elf_obj_attr_subsections (abfd);
+  obj_attr_subsection_list *ref_subsecs = &elf_obj_attr_subsections (ref_bfd);
+
+  obj_attr_subsection_v2 *s_frozen_first = out_frozen_subsecs->first;
+  obj_attr_subsection_v2 *s_abfd = abfd_subsecs->first;
+  obj_attr_subsection_v2 *s_ref = ref_subsecs->first;
+
+  /* Translate object attributes from abfd to GNU properties if they have an
+     equivalence.  */
+  _bfd_elf_translate_relevant_obj_attrs_to_gnu_props (abfd);
+
+  while (s_abfd != NULL && s_ref != NULL)
+    {
+      int cmp = strcmp (s_ref->name, s_abfd->name);
+
+      if (cmp < 0) /* REF has a subsection that ABFD doesn't have.  */
+	{
+	  if (s_ref->status != obj_attr_subsection_v2_ok)
+	    {
+	      s_ref = s_ref->next;
+	      continue;
+	    }
+
+	  obj_attr_subsection_v2 *s_frozen =
+	    obj_attr_subsection_v2_find_by_name (s_frozen_first, s_ref->name, true);
+
+	  /* Mismatching between REF and FROZEN already done in
+	     oav2_subsections_merge_frozen.  */
+	  success &= handle_subsection_missing (info, ref_bfd, abfd, s_ref,
+	    s_frozen);
+
+	  if (s_frozen != NULL)
+	    s_frozen_first = s_frozen->next;
+	  s_ref = s_ref->next;
+	}
+      else if (cmp > 0) /* ABFD has a subsection that REF doesn't have.  */
+	{
+	  if (s_abfd->status != obj_attr_subsection_v2_ok)
+	    {
+	      s_abfd = s_abfd->next;
+	      continue;
+	    }
+
+	  obj_attr_subsection_v2 *s_frozen =
+	    obj_attr_subsection_v2_find_by_name (s_frozen_first, s_abfd->name, true);
+	  if (s_frozen != NULL)
+	    {
+	      /* Check any mismatch against ABFD and FROZEN.  */
+	      bool mismatch = oav2_subsection_mismatching_params (info, NULL,
+		abfd, s_frozen, s_abfd);
+	      success &= ! mismatch;
+	      if (mismatch)
+		s_abfd->status = obj_attr_subsection_v2_corrupted;
+	      else
+		success &= handle_subsection_additional (info, ref_bfd, abfd,
+		  s_ref, s_abfd, s_frozen);
+	    }
+	  else
+	    success &= handle_subsection_additional (info, ref_bfd, abfd, s_ref,
+	      s_abfd, NULL);
+
+	  if (s_frozen != NULL)
+	    s_frozen_first = s_frozen->next;
+	  s_abfd = s_abfd->next;
+	}
+      else /* Both REF and ABFD have the subsection.  */
+	{
+	  if (s_ref->status != obj_attr_subsection_v2_ok)
+	    {
+	      s_ref = s_ref->next;
+	      s_abfd = s_abfd->next;
+	      continue;
+	    }
+
+	  obj_attr_subsection_v2 *s_frozen =
+	    obj_attr_subsection_v2_find_by_name (s_frozen_first, s_ref->name, true);
+	  if (s_frozen != NULL)
+	    {
+	      bool mismatch
+		= oav2_subsection_mismatching_params (info, NULL, abfd,
+						      s_frozen, s_abfd);
+	      success &= ! mismatch;
+	      if (mismatch)
+		s_abfd->status = obj_attr_subsection_v2_corrupted;
+	      else
+		success &= (handle_subsection_merge (info, ref_bfd, abfd,
+						     s_ref, s_abfd, s_frozen)
+			    != NULL);
+	    }
+	  else
+	    {
+	      bool mismatch
+		= oav2_subsection_mismatching_params (info, ref_bfd, abfd,
+						      s_ref, s_abfd);
+	      success &= ! mismatch;
+	      if (mismatch)
+		s_abfd->status = obj_attr_subsection_v2_corrupted;
+	      else
+		success &= (handle_subsection_merge (info, ref_bfd, abfd, s_ref,
+						     s_abfd, NULL) != NULL);
+	    }
+
+	  if (s_frozen != NULL)
+	    s_frozen_first = s_frozen->next;
+	  s_ref = s_ref->next;
+	  s_abfd = s_abfd->next;
+	}
+    }
+
+  for (; s_abfd != NULL; s_abfd = s_abfd->next)
+    {
+      if (s_abfd->status != obj_attr_subsection_v2_ok)
+	continue;
+
+      obj_attr_subsection_v2 *s_frozen =
+	obj_attr_subsection_v2_find_by_name (s_frozen_first, s_abfd->name, true);
+      if (s_frozen != NULL)
+	{
+	  bool mismatch = oav2_subsection_mismatching_params (info, NULL, abfd,
+							      s_frozen, s_abfd);
+	  success &= ! mismatch;
+	  if (mismatch)
+	    s_abfd->status = obj_attr_subsection_v2_corrupted;
+	  else
+	    success &= handle_subsection_additional (info, ref_bfd, abfd,
+						     ref_subsecs->last, s_abfd,
+						     s_frozen);
+	}
+      else
+	success &= handle_subsection_additional (info, ref_bfd, abfd,
+						 ref_subsecs->last, s_abfd,
+						 NULL);
+    }
+
+  for (; s_ref != NULL; s_ref = s_ref->next)
+    {
+      if (s_ref->status != obj_attr_subsection_v2_ok)
+	continue;
+
+      obj_attr_subsection_v2 *s_frozen =
+	obj_attr_subsection_v2_find_by_name (s_frozen_first, s_ref->name, true);
+      /* No need to check for matching parameters here as frozen has already
+	 been checked against ref.  */
+      success &= handle_subsection_missing (info, ref_bfd, abfd, s_ref, s_frozen);
+    }
+
+  return success;
+}
+
+/* Compact duplicated tag declarations in a same subsection.
+   Return True on success, False if any issue is found during the compaction,
+   i.e. conflicting values for the same tag.  */
+static bool
+oav2_compact_tags (bfd *abfd, obj_attr_subsection_v2 *subsec)
+{
+  bool success = true;
+
+
+  for (obj_attr_v2 *a = subsec->first;
+       a != NULL && a->next != NULL;)
+    {
+      if (a->tag != a->next->tag)
+	{
+	  a = a->next;
+	  continue;
+	}
+
+      if (subsec->encoding == OA_ENC_ULEB128)
+	{
+	  if (a->vals.uint_val != a->next->vals.uint_val)
+	    {
+	      success = false;
+	      _bfd_error_handler (_("%pB: error: found duplicated attributes "
+		"'Tag_unknown_%u' with conflicting values (0x%x vs 0x%x) in "
+		"subsection %s"), abfd, a->tag, a->vals.uint_val,
+		a->next->vals.uint_val, subsec->name);
+	    }
+	  else
+	    LINKED_LIST_REMOVE(obj_attr_v2) (subsec, a->next);
+	}
+      else /* (subsec->encoding == NTBS)  */
+	{
+	  if (strcmp (a->vals.string_val, a->next->vals.string_val) != 0)
+	    {
+	      success = false;
+	      _bfd_error_handler (_("%pB: error: found duplicated attributes "
+		"'Tag_unknown_%u' with conflicting values ('%s' vs '%s') in "
+		"subsection %s"), abfd, a->tag, a->vals.string_val,
+		a->next->vals.string_val, subsec->name);
+	    }
+	  else
+	    LINKED_LIST_REMOVE(obj_attr_v2) (subsec, a->next);
+	}
+    }
+
+  return success;
+}
+
+/* Merge two subsections together (object attributes v2 only).
+   The result is stored into subsec1.  subsec2 is destroyed.
+   Return true if the merge was successful, false otherwise.
+   Note: subsec1 and subsec2 are expected to be sorted before the call to this
+   function.  */
+static bool
+oav2_subsection_destructive_merge (bfd *abfd,
+				   obj_attr_subsection_v2 *subsec1,
+				   obj_attr_subsection_v2 *subsec2)
+{
+  BFD_ASSERT (subsec1->encoding == subsec2->encoding
+	   && subsec1->optional == subsec2->optional);
+
+  bool success = true;
+
+  success &= oav2_compact_tags (abfd, subsec1);
+  success &= oav2_compact_tags (abfd, subsec2);
+
+  obj_attr_v2 *a1 = subsec1->first;
+  obj_attr_v2 *a2 = subsec2->first;
+  while (a1 != NULL && a2 != NULL)
+    {
+      if (a1->tag < a2->tag)
+	{} /* Nothing to do, a1 is already in subsec1.  */
+      else if (a1->tag > a2->tag)
+	{
+	  /* a2 is missing in subsec1, add it.  */
+	  obj_attr_v2 *previous = LINKED_LIST_REMOVE(obj_attr_v2) (subsec2, a2);
+	  LINKED_LIST_INSERT_BEFORE(obj_attr_v2) (subsec1, a2, a1);
+	  a2 = previous;
+	}
+      else
+	{
+	  if (subsec1->encoding == OA_ENC_ULEB128
+	   && a1->vals.uint_val != a2->vals.uint_val)
+	    {
+	      success = false;
+	      _bfd_error_handler
+		(_("%pB: error: found 2 subsections with the same name '%s' "
+		   "and found conflicting values (%#x vs %#x) for object "
+		   "attribute 'Tag_unknown_%u'"),
+		   abfd, subsec1->name, a1->vals.uint_val, a2->vals.uint_val,
+		   a1->tag);
+	    }
+	  else if (subsec1->encoding == OA_ENC_NTBS
+	    && strcmp (a1->vals.string_val, a2->vals.string_val) != 0)
+	    {
+	      success = false;
+	      _bfd_error_handler
+		(_("%pB: error: found 2 subsections with the same name '%s' "
+		   "and found conflicting values ('%s' vs '%s') for object "
+		   "attribute 'Tag_unknown_%u'"),
+		   abfd, subsec1->name, a1->vals.string_val, a2->vals.string_val,
+		   a1->tag);
+	    }
+	}
+      a1 = a1->next;
+      a2 = a2->next;
+    }
+
+  for (; a2 != NULL; a2 = a2->next)
+    {
+      /* a2 is missing in subsec1, add it.  */
+      obj_attr_v2 *previous = LINKED_LIST_REMOVE(obj_attr_v2) (subsec2, a2);
+      LINKED_LIST_INSERT_BEFORE(obj_attr_v2) (subsec1, a2, a1);
+      a2 = previous;
+    }
+
+  /* If a1 != NULL, we don't care since it is already in subsec1.  */
+
+  /* Destroy subsec2 before exiting.  */
+  _bfd_elf_obj_attr_subsection_v2_free (subsec2);
+
+  return success;
+}
+
+/* Merge duplicated subsections and object attributes inside a same object
+   file.  After a call to this function, the subsections and object attributes
+   are sorted.  */
+static bool
+oav2_file_scope_merge_subsections (bfd *abfd)
+{
+  obj_attr_subsection_list *subsecs = &elf_obj_attr_subsections (abfd);
+
+  /* Sort all the subsections and object attributes as they might not have been
+     inserted in the right order.  From now on, the subsections and attributes
+     will be assumed to be always sorted.  Any additive mutation will need to
+     preserve the order.  */
+  oav2_sort_subsections (subsecs);
+
+  bool success = true;
+
+  obj_attr_subsection_v2 *subsec = subsecs->first;
+  while (subsec != NULL && subsec->next != NULL)
+    {
+      obj_attr_subsection_v2 *dup_subsec =
+	obj_attr_subsection_v2_find_by_name (subsec->next, subsec->name, false);
+      if (dup_subsec != NULL)
+	{
+	  LINKED_LIST_REMOVE(obj_attr_subsection_v2) (subsecs, dup_subsec);
+	  success &= oav2_subsection_destructive_merge (abfd, subsec, dup_subsec);
+	}
+      else
+	subsec = subsec->next;
+    }
+
+  return success;
+}
+
+/* If an Object Attribute subsection inside ABFD cannot be identified neither
+   as a GNU subsection or a backend-specific one, set the status of this
+   subsection to UNKNOWN.  The unknown subsection will be skipped during the
+   merge process, and will be pruned from the output.  */
+static void
+oav2_subsections_mark_unknown (bfd *abfd)
+{
+  const struct elf_backend_data *be = get_elf_backend_data (abfd);
+  for (obj_attr_subsection_v2* subsec = elf_obj_attr_subsections (abfd).first;
+       subsec != NULL;
+       subsec = subsec->next)
+    {
+      if (identify_subsection (be, subsec->name) == NULL)
+	subsec->status = obj_attr_subsection_v2_unknown;
+    }
+}
+
+/* Return True if the given BFD is an ELF object with the current backend
+   machine code, non-dynamic (i.e. not a shared library), not a plugin or
+   created by the linker.  False otherwise. */
+static bool
+oav2_relevant_elf_object (struct bfd_link_info *info,
+			  bfd *abfd)
+{
+  const struct elf_backend_data *output_bfd
+    = get_elf_backend_data (info->output_bfd);
+  unsigned int elfclass = output_bfd->s->elfclass;
+  int elf_machine_code = output_bfd->elf_machine_code;
+  return ((abfd->flags & (DYNAMIC | BFD_PLUGIN | BFD_LINKER_CREATED)) == 0
+	  && bfd_get_flavour (abfd) == bfd_target_elf_flavour
+	  && elf_machine_code == get_elf_backend_data (abfd)->elf_machine_code
+	  && elfclass == get_elf_backend_data (abfd)->s->elfclass);
+}
+
+/* Merge all the build attributes in INPUTS into REF_BFD.  Return true on
+   success, false otherwise.  */
+static bfd *
+oav2_merge_attrs (struct bfd_link_info *info, bfd *ref_bfd)
+{
+  oav2_subsections_mark_unknown (ref_bfd);
+
+  bool success = true;
+  for (bfd *abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
+    if (abfd != ref_bfd && oav2_relevant_elf_object (info, abfd))
+      {
+	/* Convert relevant GNU properties to object attributes.  */
+	_bfd_elf_translate_relevant_gnu_props_to_obj_attrs (abfd);
+	/* Merge duplicates subsections and attributes.  */
+	success &= oav2_file_scope_merge_subsections (abfd);
+
+	oav2_subsections_mark_unknown (abfd);
+	success &= oav2_subsections_merge (info, ref_bfd, abfd);
+      }
+
+  return success ? ref_bfd : NULL;
+}
+
+/* Prune the given attribute, and return the next one in the list.  */
+static obj_attr_v2 *
+oav2_attr_delete (obj_attr_subsection_v2 *subsec,
+		   obj_attr_v2 *attr)
+{
+  obj_attr_v2 *next = attr->next;
+  LINKED_LIST_REMOVE(obj_attr_v2) (subsec, attr);
+  _bfd_elf_obj_attr_v2_free (attr, subsec->encoding);
+  return next;
+}
+
+/* Prune the given subsection, and return the next one in the list.  */
+static obj_attr_subsection_v2 *
+oav2_subsec_delete (obj_attr_subsection_list *plist,
+		    obj_attr_subsection_v2 *subsec)
+{
+  obj_attr_subsection_v2 *next = subsec->next;
+  LINKED_LIST_REMOVE(obj_attr_subsection_v2) (plist, subsec);
+  _bfd_elf_obj_attr_subsection_v2_free (subsec);
+  return next;
+}
+
+/* Log a pruned attribute from the merge result.  */
+static void
+oav2_info_attr_delete (struct bfd_link_info *info,
+			obj_attr_subsection_v2 *subsec,
+			obj_attr_v2 *attr)
+{
+  const struct elf_backend_data *be = get_elf_backend_data (info->output_bfd);
+  const char *attr_s = obj_attr_v2_tag_to_string (be, subsec->name, attr->tag);
+  if (attr_s)
+    info->callbacks->minfo (_("Removed attribute '%s' from '%s'\n"), attr_s,
+      subsec->name);
+  else
+    info->callbacks->minfo (_("Removed attribute 'Tag_unknown_%u' from '%s'\n"),
+      attr->tag, subsec->name);
+}
+
+/* Log a pruned subsection from the merge result.  */
+static void
+oav2_info_subsec_delete (struct bfd_link_info *info,
+			 obj_attr_subsection_v2 *subsec)
+{
+  info->callbacks->minfo (_("Removed subsection '%s'\n"), subsec->name);
+}
+
+/* Prune any attributes with a status different from obj_attr_v2_ok in the
+   given subsection.  */
+static void
+oav2_attrs_prune_nok (struct bfd_link_info *info,
+		      obj_attr_subsection_v2 *subsec)
+{
+  for (obj_attr_v2 *attr = subsec->first;
+       attr != NULL;)
+    {
+      if (attr->status != obj_attr_v2_ok)
+	{
+	  oav2_info_attr_delete (info, subsec, attr);
+	  attr = oav2_attr_delete (subsec, attr);
+	}
+      else
+	attr = attr->next;
+    }
+}
+
+/* Prune any subsection with a status different from obj_attr_subsection_v2_ok
+   in the given list of subsections.  */
+static void
+oav2_subsecs_prune_nok (struct bfd_link_info *info,
+			obj_attr_subsection_list *plist)
+{
+  for (obj_attr_subsection_v2 *subsec = plist->first;
+       subsec != NULL;)
+    {
+      if (subsec->status == obj_attr_subsection_v2_ok)
+	oav2_attrs_prune_nok (info, subsec);
+
+      if (subsec->size == 0 || subsec->status != obj_attr_subsection_v2_ok)
+	{
+	  oav2_info_subsec_delete (info, subsec);
+	  subsec = oav2_subsec_delete (plist, subsec);
+	}
+      else
+	subsec = subsec->next;
+    }
+}
+
+/* Prune any subsection or attribute with a status different from OK.  */
+static bfd *
+oav2_prune_nok_attrs (struct bfd_link_info *info, bfd *abfd)
+{
+  if (abfd == NULL)
+    return NULL;
+
+  obj_attr_subsection_list *plist = &elf_obj_attr_subsections (abfd);
+  oav2_subsecs_prune_nok (info, plist);
+  return (plist->size != 0) ? abfd : NULL;
+}
+
+/* Set up object attributes coming from configuration, and merge them with the
+   ones from the input object files. Return a pointer to the input object file
+   containing the merge result on success, NULL otherwise.  */
+bfd *
+_bfd_elf_link_setup_build_attributes (struct bfd_link_info *info)
+{
+  obj_attr_subsection_list *frozen_
+    = &elf_obj_attr_subsections (info->output_bfd);
+
+  bfd_search_result_t res
+    = bfd_linear_search_one_with_build_attributes (info);
+
+  /* If res.pbfd is NULL, it means that it didn't find any ELF object files.  */
+  if (res.pbfd == NULL)
+    return NULL;
+
+  /* If the input has GNU properties, try to convert the relevant ones to object
+     attributes.  */
+  if (elf_properties (res.pbfd) != NULL)
+    {
+      _bfd_elf_translate_relevant_gnu_props_to_obj_attrs (res.pbfd);
+      if (elf_obj_attr_subsections (res.pbfd).size > 0)
+	res.has_build_attributes = true;
+    }
+
+  /* No frozen object attributes and no object file, so nothing to do.  */
+  if (!res.has_build_attributes && frozen_->size == 0)
+    return NULL;
+  /* If frozen object attributes were set by some command-line options, we still
+     need to emit warnings / errors if there are incompatibilities.  */
+
+  /* Set the object attribute version for the output object to the recommended
+     value by the backend.  */
+  elf_obj_attr_version (info->output_bfd)
+    = get_elf_backend_data (info->output_bfd)->default_obj_attr_version;
+
+  if (res.sec == NULL)
+    {
+      /* This input object has no object attribute section, so the object
+	 attribute version was never set by the deserializer.
+	 Set it to the backend recommended value.  */
+      elf_obj_attr_version (res.pbfd)
+	= get_elf_backend_data (res.pbfd)->default_obj_attr_version;
+      res.sec = create_build_attributes_section (info, res.pbfd);
+    }
+
+  /* Sort the frozen subsections and attributes in case that they were not
+     inserted in the correct order.  */
+  oav2_sort_subsections (frozen_);
+
+  bool success = true;
+
+  /* Merge duplicates subsections and attributes.  */
+  success &= oav2_file_scope_merge_subsections (res.pbfd);
+
+  /* Translate object attributes from res.pbfd to GNU properties if they have an
+     equivalence, before distorting them with the merge.  */
+  _bfd_elf_translate_relevant_obj_attrs_to_gnu_props (res.pbfd);
+
+  /* Emit warnings / errors (if any) when merging res.pbfd against frozen.
+     res.pbfd will be used as the ref and will be accumulating the merge result.
+     It means that we will lose its information.  */
+  success &= oav2_subsections_merge_frozen (info, res.pbfd, frozen_->first);
+
+  /* Merge build attributes sections.  */
+  info->callbacks->minfo (_("\n"));
+  info->callbacks->minfo (_("Merging build attributes\n"));
+  info->callbacks->minfo (_("\n"));
+
+  res.pbfd = oav2_merge_attrs (info, res.pbfd);
+  success &= (res.pbfd != NULL);
+  res.pbfd = oav2_prune_nok_attrs (info, res.pbfd);
+
+  if (res.pbfd && elf_obj_attr_subsections (res.pbfd).size > 0)
+    {
+      /* Shallow-copy the build attributes into output_bfd.  */
+      elf_obj_attr_subsections (info->output_bfd) =
+	elf_obj_attr_subsections (res.pbfd);
+
+      /* Note: the build attributes section in the output object is copied from
+	 the input object which was used for the merge (res.pbfd).  No need to
+	 create it here.  However, so that the section is copied to the output
+	 object, the size must be different from 0.  For now, we will set this
+	 size to 1.  The real size will be set later.  */
+      res.sec->size = 1;
+    }
+
+  return success ? res.pbfd : NULL;
+}
+
 /* Allocate/find an object attribute.  */
 obj_attribute *
 elf_new_obj_attr (bfd *abfd, obj_attr_vendor_t vendor, obj_attr_tag_t tag)
@@ -1191,6 +2658,32 @@ oav2_parse_section (bfd *abfd,
     }
 }
 
+/* Translate the relevant GNU properties to object attributes v2.  */
+void
+_bfd_elf_translate_relevant_gnu_props_to_obj_attrs (bfd *abfd)
+{
+  const struct elf_backend_data *be = get_elf_backend_data (abfd);
+  if (be->translate_relevant_gnu_props_to_obj_attrs == NULL)
+    return;
+
+  for (elf_property_list *p = elf_properties (abfd); p != NULL; p = p->next)
+    be->translate_relevant_gnu_props_to_obj_attrs (abfd, p);
+}
+
+/* Translate the relevant object attributes v2 to GNU properties.  */
+void
+_bfd_elf_translate_relevant_obj_attrs_to_gnu_props (bfd *abfd)
+{
+  const struct elf_backend_data *be = get_elf_backend_data (abfd);
+  if (be->translate_relevant_obj_attrs_to_gnu_props == NULL)
+    return;
+
+  for (obj_attr_subsection_v2 *subsec = elf_obj_attr_subsections (abfd).first;
+       subsec != NULL;
+       subsec = subsec->next)
+    be->translate_relevant_obj_attrs_to_gnu_props (abfd, subsec);
+}
+
 /* Parse an object attributes section.
    Note: The parsing setup is common between object attributes v1 and v2.  */
 void
@@ -1421,6 +2914,7 @@ _bfd_elf_obj_attr_v2_init (obj_attr_tag_t tag,
   memset (attr, 0, sizeof (*attr));
   attr->tag = tag;
   attr->vals = vals;
+  attr->status = obj_attr_v2_ok;
   return attr;
 }
 
@@ -1449,7 +2943,9 @@ _bfd_elf_obj_attr_v2_copy (obj_attr_v2 *other,
   else
     vals.uint_val = other->vals.uint_val;
 
-  return _bfd_elf_obj_attr_v2_init (other->tag, vals);
+  obj_attr_v2 *copy = _bfd_elf_obj_attr_v2_init (other->tag, vals);
+  copy->status = other->status;
+  return copy;
 }
 
 /* Compare two object attributes based on their TAG value only (partial
@@ -1517,6 +3013,7 @@ _bfd_elf_obj_attr_subsection_v2_init (const char *name,
   subsection->scope = scope;
   subsection->optional = optional;
   subsection->encoding = encoding;
+  subsection->status = obj_attr_subsection_v2_ok;
   return subsection;
 }
 
@@ -1544,6 +3041,8 @@ _bfd_elf_obj_attr_subsection_v2_copy (obj_attr_subsection_v2 const *other)
   obj_attr_subsection_v2 *new_subsec =
     _bfd_elf_obj_attr_subsection_v2_init (other->name, other->scope,
 					  other->optional, other->encoding);
+  new_subsec->status = other->status;
+
   for (obj_attr_v2* attr = other->first;
        attr != NULL;
        attr = attr->next)
diff --git a/bfd/elf-attrs.h b/bfd/elf-attrs.h
index 103ed1cf45d..edc9571fdf6 100644
--- a/bfd/elf-attrs.h
+++ b/bfd/elf-attrs.h
@@ -55,6 +55,16 @@ typedef union obj_attr_value_v2 {
   const char* string_val;
 } obj_attr_value_v2;
 
+typedef enum obj_attr_v2_status
+{
+  /* An attribute that is unknown to the linker, and so cannot be merged.  */
+  obj_attr_v2_unknown = 0,
+  /* An attribute that was reported as corrupted.  */
+  obj_attr_v2_corrupted,
+  /* A valid attribute.  */
+  obj_attr_v2_ok,
+} obj_attr_v2_status;
+
 typedef uint32_t obj_attr_tag_t;
 
 typedef struct obj_attr_v2 {
@@ -64,6 +74,9 @@ typedef struct obj_attr_v2 {
   /* The value assigned to an attribute, can be ULEB128 or NTBS.  */
   union obj_attr_value_v2 vals;
 
+  /* The attribute status after merge.  */
+  obj_attr_v2_status status;
+
   /* The next attribute in the list or NULL.  */
   struct obj_attr_v2 *next;
 
@@ -78,6 +91,16 @@ typedef enum obj_attr_subsection_scope_v2
   OA_SUBSEC_PRIVATE = 1,
 } obj_attr_subsection_scope_v2;
 
+typedef enum obj_attr_subsection_v2_status
+{
+  /* A subsection that is unknown to the linker, and so cannot be merged.  */
+  obj_attr_subsection_v2_unknown = 0,
+  /* A subsection that was reported as corrupted.  */
+  obj_attr_subsection_v2_corrupted,
+  /* A valid subsection.  */
+  obj_attr_subsection_v2_ok,
+} obj_attr_subsection_v2_status;
+
 typedef struct obj_attr_subsection_v2 {
   /* The name of the subsection.  */
   const char *name;
@@ -91,6 +114,9 @@ typedef struct obj_attr_subsection_v2 {
   /* The value encoding of attributes in this subsection.  */
   obj_attr_encoding_v2 encoding;
 
+  /* The subsection status after merge.  */
+  obj_attr_subsection_v2_status status;
+
   /* The size of the list.  */
   uint32_t size;
 
@@ -193,3 +219,36 @@ known_obj_attr_v2_find_by_tag (const struct elf_backend_data *,
 extern const char *
 obj_attr_v2_tag_to_string (const struct elf_backend_data *, const char*,
 			   obj_attr_tag_t);
+
+enum obj_attr_v2_merge_result_reason
+{
+  /* Default: everything is ok.  */
+  MERGE_OK = 0,
+  /* The result value of the merge is the same as REF.  */
+  SAME_VALUE_AS_REF,
+  /* No implementation of a merge for this attribute exists.  */
+  UNSUPPORTED,
+  /* The merge failed, an error message should be logged.  */
+  ERROR,
+};
+typedef struct {
+  /* Should the merge be performed ?  */
+  bool merge;
+  /* The merged value.  */
+  union obj_attr_value_v2 vals;
+  /* If the merge should not be performed, give the reason to differentiate
+     error cases from normal cases.  Typically, if REF already is set to the
+     same value as the merged result, no merge is needed, and this is not an
+     error.  */
+  enum obj_attr_v2_merge_result_reason reason;
+} obj_attr_v2_merge_result;
+
+/* 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
+obj_attr_v2_tag_merge_AND (struct bfd_link_info *, bfd *,
+			   obj_attr_subsection_v2 *, obj_attr_v2 *,
+			   obj_attr_v2 *, obj_attr_v2 *);
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index 06405e68906..02aef577753 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1663,6 +1663,26 @@ struct elf_backend_data
   /* The size of the array of known subsections.  */
   const size_t obj_attr_v2_known_subsections_size;
 
+  /* Translate the relevant GNU properties to object attributes v2.  */
+  void (*translate_relevant_gnu_props_to_obj_attrs) (bfd *,
+    elf_property_list *);
+
+  /* Translate the relevant object attributes v2 to GNU properties.  */
+  void (*translate_relevant_obj_attrs_to_gnu_props) (bfd *,
+    obj_attr_subsection_v2 *);
+
+  /* Check build attributes subsection v2 against expected properties.  */
+  bool (*obj_attr_subsection_v2_match_known) (struct bfd_link_info *, bfd *,
+    obj_attr_subsection_v2 *);
+
+  /* Get default value for an attribute.  */
+  bool (*obj_attr_v2_default_value) (struct bfd_link_info *,
+    const obj_attr_info_t *, obj_attr_subsection_v2 *, obj_attr_v2 *);
+
+  /* Merge a build attribute v2.  */
+  obj_attr_v2_merge_result (*obj_attr_v2_tag_merge) (struct bfd_link_info *,
+    bfd *, obj_attr_subsection_v2 *, obj_attr_v2 *, obj_attr_v2 *, obj_attr_v2 *);
+
   /* This function determines the order in which any attributes are
      written.  It must be defined for input in the range
      LEAST_KNOWN_OBJ_ATTRIBUTE..NUM_KNOWN_OBJ_ATTRIBUTES-1 (this range
@@ -1684,6 +1704,9 @@ struct elf_backend_data
   bool (*merge_gnu_properties) (struct bfd_link_info *, bfd *, bfd *,
 				       elf_property *, elf_property *);
 
+  /* Set up build attributes.  */
+  bfd *(*setup_build_attributes) (struct bfd_link_info *);
+
   /* Set up GNU properties.  */
   bfd *(*setup_gnu_properties) (struct bfd_link_info *);
 
@@ -3116,9 +3139,14 @@ extern obj_attribute *bfd_elf_add_obj_attr_int_string
 
 extern bool _bfd_elf_write_section_build_attributes
   (bfd *, struct bfd_link_info *);
+extern bfd *_bfd_elf_link_setup_build_attributes
+  (struct bfd_link_info *);
+
 extern char *_bfd_elf_attr_strdup (bfd *, const char *);
 extern void _bfd_elf_copy_obj_attributes (bfd *, bfd *);
 extern int _bfd_elf_obj_attrs_arg_type (bfd *, obj_attr_vendor_t, obj_attr_tag_t);
+extern void _bfd_elf_translate_relevant_gnu_props_to_obj_attrs (bfd *);
+extern void _bfd_elf_translate_relevant_obj_attrs_to_gnu_props (bfd *);
 extern void _bfd_elf_parse_attributes (bfd *, Elf_Internal_Shdr *);
 extern bool _bfd_elf_merge_object_attributes
   (bfd *, struct bfd_link_info *);
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index d4903c18e7a..d0ce9235bf1 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -569,6 +569,21 @@
 #ifndef	elf_backend_obj_attr_v2_known_subsections_size
 #define elf_backend_obj_attr_v2_known_subsections_size	0
 #endif
+#ifndef elf_backend_translate_relevant_gnu_props_to_obj_attrs
+#define elf_backend_translate_relevant_gnu_props_to_obj_attrs	NULL
+#endif
+#ifndef elf_backend_translate_relevant_obj_attrs_to_gnu_props
+#define elf_backend_translate_relevant_obj_attrs_to_gnu_props	NULL
+#endif
+#ifndef elf_backend_obj_attr_subsection_v2_match_known
+#define elf_backend_obj_attr_subsection_v2_match_known	NULL
+#endif
+#ifndef elf_backend_obj_attr_v2_default_value
+#define elf_backend_obj_attr_v2_default_value	NULL
+#endif
+#ifndef elf_backend_obj_attr_v2_tag_merge
+#define elf_backend_obj_attr_v2_tag_merge	NULL
+#endif
 #ifndef elf_backend_obj_attrs_order
 #define elf_backend_obj_attrs_order		NULL
 #endif
@@ -581,6 +596,9 @@
 #ifndef elf_backend_merge_gnu_properties
 #define elf_backend_merge_gnu_properties	NULL
 #endif
+#ifndef elf_backend_setup_build_attributes
+#define elf_backend_setup_build_attributes	_bfd_elf_link_setup_build_attributes
+#endif
 #ifndef elf_backend_setup_gnu_properties
 #define elf_backend_setup_gnu_properties	_bfd_elf_link_setup_gnu_properties
 #endif
@@ -952,10 +970,16 @@ static const struct elf_backend_data elfNN_bed =
   elf_backend_obj_attrs_version_enc,
   elf_backend_obj_attr_v2_known_subsections,
   elf_backend_obj_attr_v2_known_subsections_size,
+  elf_backend_translate_relevant_gnu_props_to_obj_attrs,
+  elf_backend_translate_relevant_obj_attrs_to_gnu_props,
+  elf_backend_obj_attr_subsection_v2_match_known,
+  elf_backend_obj_attr_v2_default_value,
+  elf_backend_obj_attr_v2_tag_merge,
   elf_backend_obj_attrs_order,
   elf_backend_obj_attrs_handle_unknown,
   elf_backend_parse_gnu_properties,
   elf_backend_merge_gnu_properties,
+  elf_backend_setup_build_attributes,
   elf_backend_setup_gnu_properties,
   elf_backend_fixup_gnu_properties,
   elf_backend_compact_eh_encoding,
diff --git a/ld/ldelf.c b/ld/ldelf.c
index f4f27fc3873..e16899db9b8 100644
--- a/ld/ldelf.c
+++ b/ld/ldelf.c
@@ -1291,6 +1291,7 @@ ldelf_after_open (int use_libpath, int native, int is_linux, int is_freebsd,
 	}
     }
 
+  get_elf_backend_data (link_info.output_bfd)->setup_build_attributes (&link_info);
   get_elf_backend_data (link_info.output_bfd)->setup_gnu_properties (&link_info);
 
   /* Do not allow executable files to be used as inputs to the link.  */
-- 
2.50.1



More information about the Binutils mailing list