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

Jan Beulich jbeulich@suse.com
Fri Aug 22 09:50:58 GMT 2025


On 15.07.2025 13:39, 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.

May a linker legitimately remove anything? (Instead I would have hoped
for some forward compatibility.)

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

What's "current"?

> +   machine code, non-dynamic (i.e. not a shared library), and has sections.

But an executable is okay? I.e. aren't after relocatable objects here?

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

"output_bed" may be a better name for this variable. It's hard to see though
why you have ...

> +  unsigned int elfclass = output_bfd->s->elfclass;
> +  int elf_machine_code = output_bfd->elf_machine_code;

... three variables for one side ...

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

... of the comparisons and none for the other. I would suggest to have "bed"
and "output_bed", and no further ones.

As this function isn't generic, I'd recommend dropping (or replacing) its
bfd_ prefix. (This likely applies elsewhere as well.)

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

s/search_one/find_first/?

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

Wait - we're in generic code here? How can there be an Arm64 specific check?
Did you perhaps mean to use struct bfd_arch_info's bits_per_address?

> +  if (!bfd_set_section_alignment (sec, align))
> +    info->callbacks->einfo (_("%F%pA: failed to align section\n"), sec);

What is %F? I understand there are very few other uses, but I can't help the
impression that they're all stale (at best). If I try to use it, all I get is
"%F" in the output. Ah, here we go - commit 8d97c1a53f3d. You mean to use
->fatal() instead (also a few lines up, and perhaps elsewhere).

> @@ -454,8 +648,8 @@ static known_subsection_v2 obj_attr_v2_known_gnu_subsections[] =
>    /* Note for the future: GNU subsections can be added here below.  */
>  };
>  
> -/* Return True if the given subsection name is part of the reserved "gnu-testing"
> -   namespace.  */
> +/* Return True if the given subsection name is part of the reserved testing
> +   namespace, i.e. SUBSEC_NAME begins with "gnu-testing".  */
>  static bool
>  gnu_testing_namespace (const char *subsec_name)
>  {

Was this adjustment meant to be done in patch 04?

> @@ -555,6 +749,1285 @@ oav2_encoding_to_string (obj_attr_encoding_v2 encoding)
>    return (encoding == OA_ENC_ULEB128) ? "ULEB128" : "NTBS";
>  }
>  
> +/* Initialize the given ATTR with its default value coming from the known tag
> +   registry.  */
> +static void
> +oav2_attr_overwrite_with_default (struct bfd_link_info *info,
> +				  obj_attr_subsection_v2 *subsec,
> +				  obj_attr_v2 *attr)
> +{
> +  const struct elf_backend_data *be = get_elf_backend_data (info->output_bfd);

The commonly used name for this is "bed", I think.

> +  const obj_attr_info_t *tag_info
> +    = known_obj_attr_v2_find_by_tag (be, subsec->name, attr->tag);
> +  if (tag_info == NULL)
> +    {
> +      attr->status = obj_attr_v2_unknown;
> +      if (subsec->encoding == OA_ENC_ULEB128)
> +	attr->vals.uint_val = 0;
> +      else
> +	attr->vals.string_val = NULL;
> +      return;
> +    }
> +
> +  if (be->obj_attr_v2_default_value != NULL
> +      && be->obj_attr_v2_default_value (info, tag_info, subsec, attr))
> +    {}
> +  else if (subsec->encoding == OA_ENC_NTBS)
> +    {
> +      if (tag_info->default_value.val.string != NULL)
> +	{
> +	  if (attr->vals.string_val != NULL)
> +	    free ((void *) attr->vals.string_val);
> +	  attr->vals.string_val = strdup (tag_info->default_value.val.string);

xstrdup() ?

> +	}
> +      else
> +	attr->vals.string_val = NULL;

Leaking the earlier string in this case?

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

Doing twice in a row the effectively same comparison?

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

xmalloc() (Hopefully you've already gone through and changed all the
allocations throughout the series.)

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

This is the first use of the variable - why is it declared and set at the
top of the function?

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

I can't bring comment and code together.

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

By inverting this and handling ...

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

... this first, you can save a level of indentation, improving readability.

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

Nit: For the pending open parenthesis this line wants indenting yet another
level.

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

In case I didn't say so elsewhere already: Please prefer %#x over 0x%x.

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

Such wants sorting before this can go in.

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

I'm not a native speaker, but I think it wants to be "mismatched" here, much
like you have ...

> +	  oav2_comprehension_to_string (s1->optional),
> +	  oav2_encoding_to_string (s1->encoding),
> +	  oav2_comprehension_to_string (s2->optional),
> +	  oav2_encoding_to_string (s2->encoding));
> +      else
> +	info->callbacks->einfo (_("%X%pB: error: parameters of subsection"
> +	  " '%s' are corrupted. (%s, %s) VS (%s, %s)\n"), f2, s1->name,

... "corrupted" here.

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

Nit (grammmar): Either "mismatches" or "is".

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

How does this call fit here? Below you're only dealing with attributes,
afaict.

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

This and ...

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

... this look to be easy to fold into just a single call. (Same pattern again
at least once further down.)

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

What does it follow from that the tag is unknown?

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

Code in obj_attr_v2_tag_merge_ADD() suggests that vals.string_val can be NULL,
in which case it would be invalid to pass to strcmp().

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

Nit: Indentation.

> +	    {
> +	      success = false;
> +	      _bfd_error_handler
> +		(_("%pB: error: found 2 subsections with the same name '%s' "
> +		   "and found conflicting values (%#x vs %#x) for object "
> +		   "attribute 'Tag_unknown_%u'"),
> +		   abfd, subsec1->name, a1->vals.uint_val, a2->vals.uint_val,
> +		   a1->tag);
> +	    }
> +	  else if (subsec1->encoding == OA_ENC_NTBS
> +	    && strcmp (a1->vals.string_val, a2->vals.string_val) != 0)

Again.

> @@ -1146,9 +2619,9 @@ oav2_parse_subsection (bfd *abfd,
>      ? OA_SUBSEC_PUBLIC
>      : OA_SUBSEC_PRIVATE;
>  
> -  obj_attr_subsection_v2 *subsec =
> -    _bfd_elf_obj_attr_subsection_v2_init (subsection_name, scope, optional_raw,
> -					  attr_type_raw);
> +  obj_attr_subsection_v2 *subsec
> +    = _bfd_elf_obj_attr_subsection_v2_init (subsection_name, scope,
> +					    optional_raw, attr_type_raw);
>    while (cursor < end)
>      {
>        BufferReadOp_t op_ = oav2_parse_attr (abfd, cursor, end, attr_type_raw);

Misplaced format adjustment?

Once again an overly large patch, close to impossible to sensibly review.

Jan


More information about the Binutils mailing list