[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