[PATCH v5 13/20] Merge of Object Attributes v2 during linkage (generic logic)

Richard Earnshaw (lists) Richard.Earnshaw@arm.com
Wed Jul 9 14:29:32 GMT 2025


On 07/07/2025 17:49, Matthieu Longo wrote:
> 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    | 1509 +++++++++++++++++++++++++++++++++++++++++++-
>  bfd/elf-attrs.h    |   64 ++
>  bfd/elf-bfd.h      |   28 +
>  bfd/elfxx-target.h |   24 +
>  ld/ldelf.c         |    1 +
>  5 files changed, 1625 insertions(+), 1 deletion(-)
> 
> diff --git a/bfd/elf-attrs.c b/bfd/elf-attrs.c
> index 82fdf12542e..575d583dad5 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);

Put parenthesis around the entire expression so that the indentation works correctly
  return (abc
          && def
          && ghi);

> +}
> +
> +/* 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
> @@ -541,6 +735,1287 @@ obj_attr_v2_tag_to_string (const struct elf_backend_data *be,
>    return NULL;
>  }
>  
> +/* 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))

Indentation

> +    {}
> +  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);
> +}
> +
> +/* To-string function for the subsection parameter "optional".  */
> +const char *
> +obj_attr_subsection_v2_optional_to_string (bool optional)
> +{
> +  return optional ? "optional" : "required";
> +}
> +
> +/* To-string function for the subsection parameter "encoding".  */
> +const char *
> +obj_attr_encoding_v2_to_string (obj_attr_encoding_v2 encoding)
> +{
> +  return (encoding == OA_ENC_ULEB128) ? "uleb128" : "ntbs";
> +}
> +
> +/* 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,
> +	  obj_attr_subsection_v2_optional_to_string (s1->optional),
> +	  obj_attr_encoding_v2_to_string (s1->encoding),
> +	  obj_attr_subsection_v2_optional_to_string (s2->optional),
> +	  obj_attr_encoding_v2_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,
> +	  obj_attr_subsection_v2_optional_to_string (s1->optional),
> +	  obj_attr_encoding_v2_to_string (s1->encoding),
> +	  obj_attr_subsection_v2_optional_to_string (s2->optional),
> +	  obj_attr_encoding_v2_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);
> +		}
You don't really need braces around a single statement.

> +	    }
> +	  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);
Indentation

> +	  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);
and here

> +	}
> +      else
> +	success &= handle_subsection_additional (info, ref_bfd, abfd,
> +	  ref_subsecs->last_, s_abfd, NULL);

and here.

> +    }
> +
> +  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 "

In this case I'd put the opening parenthesis on the following line, so that you
don't lose too much horizontal white space when correctly indenting ...
> +		"same name '%s' and found conflicting values (0x%x vs 0x%x) for"
> +		" object attribute 'Tag_unknown_%u'"), abfd, subsec1->name,
> +		a1->vals.uint_val, a2->vals.uint_val, a1->tag);
... all of these.

> +	    }
> +	  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);

Same here.

> +	    }
> +	}
> +      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);

Indentation.

> +}
> +
> +/* 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);
(too much) indentation.

> +  _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_ =

The '=' should be on the following line with the value assigned.

  Type X
    = val;

> +    &elf_obj_attr_subsections (info->output_bfd);
> +
> +  bfd_search_result_t res =
> +    bfd_linear_search_one_with_build_attributes (info);
> +

And here.

> +  /* 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)
> @@ -1177,6 +2652,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
> @@ -1407,6 +2908,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;
>  }
>  
> @@ -1435,7 +2937,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
> @@ -1503,6 +3007,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;
>  }
>  
> @@ -1530,6 +3035,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 9105dcaaf72..3a6b5c76f01 100644
> --- a/bfd/elf-attrs.h
> +++ b/bfd/elf-attrs.h
> @@ -47,11 +47,24 @@ typedef enum obj_attr_encoding_v2
>  #define obj_attr_encoding_v2_to_u8(value) \
>    ((uint8_t) (value - 1))
>  
> +extern const char *
> +obj_attr_encoding_v2_to_string (obj_attr_encoding_v2);
> +
>  typedef union obj_attr_value_v2 {
>    uint32_t uint_val;
>    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 {
> @@ -61,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;
>  
> @@ -75,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;
> @@ -88,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;
>  
> @@ -105,6 +134,9 @@ typedef struct obj_attr_subsection_v2 {
>  
>  } obj_attr_subsection_v2;
>  
> +extern const char *
> +obj_attr_subsection_v2_optional_to_string (bool);
> +
>  typedef struct obj_attr_subsection_list
>  {
>    /* A pointer to the first node of the list.  */
> @@ -187,3 +219,35 @@ 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 4b1f2fc9c8d..c45e8d97cc8 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 *);
>  
> @@ -3118,9 +3141,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 9277edf8690..d0f41b250c4 100644
> --- a/bfd/elfxx-target.h
> +++ b/bfd/elfxx-target.h
> @@ -571,6 +571,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
> @@ -583,6 +598,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
> @@ -954,10 +972,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.  */

R.



More information about the Binutils mailing list