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

Matthieu Longo matthieu.longo@arm.com
Wed Jul 9 16:47:49 GMT 2025


On 2025-07-09 15:29, Richard Earnshaw (lists) wrote:
> 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);
> 

Fixed in the next revision.

>> +}
>> +
>> +/* 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
> 

Fixed in the next revision.

>> +    {}
>> +  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.
> 

Fixed in the next revision.

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

Fixed in the next revision.

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

Fixed in the next revision.

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

Fixed in the next revision.

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

Well, this one I am wondering if the medicine is not worse than the disease.

	      _bfd_error_handler
		(_("%pB: error: found 2 subsections with the 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);

I lost one line and more horizontal space, and I don't find that the 
indentation is better than before but rather worse.

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

Same as my previous comment.

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

Fixed in the next revision.

>> +}
>> +
>> +/* 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.
> 

Fixed in the next revision.

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

Fixed in the next revision.

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

Fixed in the next revision.

>> +  /* 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