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