[PATCH v5 13/20] Merge of Object Attributes v2 during linkage (generic logic)
Matthieu Longo
matthieu.longo@arm.com
Mon Jul 7 16:49:30 GMT 2025
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);
+}
+
+/* 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))
+ {}
+ 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);
+ }
+ }
+ 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);
+ 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);
+ }
+ else
+ success &= handle_subsection_additional (info, ref_bfd, abfd,
+ ref_subsecs->last_, s_abfd, NULL);
+ }
+
+ 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 "
+ "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);
+ }
+ 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);
+ }
+ }
+ 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);
+}
+
+/* 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);
+ _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_ =
+ &elf_obj_attr_subsections (info->output_bfd);
+
+ bfd_search_result_t res =
+ bfd_linear_search_one_with_build_attributes (info);
+
+ /* 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. */
--
2.50.0
More information about the Binutils
mailing list