@@ -382,6 +382,1227 @@ bfd_elf_set_obj_attr_contents (bfd *abfd, bfd_byte *buffer, bfd_vma size)
write_obj_attr_section_v1 (abfd, buffer, size);
}
+typedef struct
+{
+ bfd *pbfd;
+ bool has_build_attributes;
+ asection* sec;
+} bfd_search_result_t;
+
+static bfd_search_result_t
+bfd_linear_search_one_with_build_attributes (struct bfd_link_info *info)
+{
+ 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;
+ }
+
+ 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;
+
+ bool
+ bfd_is_non_dynamic_elf_object (bfd *abfd)
+ {
+ 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);
+ }
+
+ 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 (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
+ unknown, so will be default initialized in the same way but its status will
+ be set to obj_attr_subsection_v2_unknown. */
+static const known_tag_v2 known_tags_gnu_testing [] =
+{
+ {0, "GNUTestTag_0", .default_value = {0}},
+ {1, "GNUTestTag_1", .default_value = {0}},
+};
+
+static known_subsection_v2 obj_attr_v2_known_gnu_subsections[] =
+{
+ {
+ /* Note: the currently set values for the subsection name, its optionality,
+ and encoding are irrelevant for a testing subsection. These values are
+ unused. This entry is only a placeholder for list of known GNU testing
+ tags. */
+ .subsec_name = NULL,
+ .known_tags = known_tags_gnu_testing,
+ .optional = true,
+ .encoding = ULEB128,
+ .len = sizeof (known_tags_gnu_testing) / sizeof (known_tag_v2),
+ },
+ /* Note for the future: GNU subsections can be added here below. */
+};
+
+static bool
+gnu_testing_namespace (const char *subsec_name)
+{
+ return strncmp ("gnu-testing-", subsec_name, 12) == 0;
+}
+
+const known_subsection_v2 *
+identify_subsection (const struct elf_backend_data *be,
+ const char* name)
+{
+ /* Check known backend subsections. */
+ const known_subsection_v2 *known_subsections = be->obj_attr_v2_known_subsections;
+ const size_t known_subsections_size = be->obj_attr_v2_known_subsections_size;
+
+ for (unsigned i = 0; i < known_subsections_size; ++i)
+ {
+ int cmp = strcmp (known_subsections[i].subsec_name, name);
+ if (cmp == 0)
+ return &known_subsections[i];
+ else if (cmp > 0)
+ break;
+ }
+
+ /* Check known GNU subsections. */
+ /* Note for the future: search known GNU subsections here. Don't forget to
+ skip the first entry (placeholder for GNU testing subsection). */
+
+ /* Check whether this subsection is a GNU testing subsection. */
+ if (gnu_testing_namespace (name))
+ return &obj_attr_v2_known_gnu_subsections[0];
+
+ return NULL;
+}
+
+static const known_tag_v2 *
+identify_tag (const known_subsection_v2* subsec, uint32_t tag)
+{
+ for (unsigned i = 0; i < subsec->len; ++i)
+ {
+ const known_tag_v2 *known_tag = &subsec->known_tags[i];
+ if (known_tag->tag == tag)
+ return known_tag;
+ else if (known_tag->tag > tag)
+ break;
+ }
+ return NULL;
+}
+
+const known_tag_v2 *
+known_obj_attr_v2_find_by_tag (const struct elf_backend_data *be,
+ const char* subsec_name, uint32_t tag)
+{
+ const known_subsection_v2 *subsec_info =
+ identify_subsection (be, subsec_name);
+ if (subsec_info != NULL)
+ {
+ const known_tag_v2 *tag_info = identify_tag (subsec_info, tag);
+ return tag_info;
+ }
+ return NULL;
+}
+
+const char *
+obj_attr_v2_tag_to_string (const struct elf_backend_data *be,
+ const char* subsec_name,
+ uint32_t tag)
+{
+ const known_tag_v2 *tag_info =
+ known_obj_attr_v2_find_by_tag (be, subsec_name, tag);
+ if (tag_info != NULL)
+ return tag_info->name;
+ return NULL;
+}
+
+static void
+obj_attr_v2_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 known_tag_v2 *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 == 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 == NTBS)
+ {
+ if (tag_info->default_value.string_val != NULL)
+ {
+ if (attr->vals.string_val != NULL)
+ free ((void *) attr->vals.string_val);
+ attr->vals.string_val = strdup (tag_info->default_value.string_val);
+ }
+ else
+ attr->vals.string_val = NULL;
+ }
+ else
+ attr->vals.uint_val = tag_info->default_value.uint_val;
+}
+
+static obj_attr_v2 *
+obj_attr_v2_tag_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);
+ obj_attr_v2_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;
+
+/* The GNU policy are detected from the name of the subsection. It should follow
+ the following pattern: "gnu-testing-XXXXXX-MERGE-<POLICY>". */
+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,
+ bfd *abfd,
+ obj_attr_subsection_v2 *subsec,
+ obj_attr_v2 *ref, obj_attr_v2 *rhs,
+ obj_attr_v2 *frozen)
+{
+ (void) info;
+ (void) abfd;
+ (void) frozen;
+
+ BFD_ASSERT (subsec->encoding == ULEB128);
+
+ obj_attr_v2_merge_result res = {
+ .merge = true,
+ .vals.uint_val = 0,
+ .reason = NONE,
+ };
+
+ 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,
+ bfd *abfd,
+ obj_attr_subsection_v2 *subsec,
+ obj_attr_v2 *ref, obj_attr_v2 *rhs,
+ obj_attr_v2 *frozen)
+{
+ (void) info;
+ (void) abfd;
+ (void) frozen;
+
+ BFD_ASSERT (subsec->encoding == ULEB128);
+
+ obj_attr_v2_merge_result res = {
+ .merge = true,
+ .vals.uint_val = 0,
+ .reason = NONE,
+ };
+
+ 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 together adding a '+' character
+ in-between. */
+static obj_attr_v2_merge_result
+obj_attr_v2_tag_merge_ADD (struct bfd_link_info *info,
+ bfd *abfd,
+ obj_attr_subsection_v2 *subsec,
+ obj_attr_v2 *ref, obj_attr_v2 *rhs,
+ obj_attr_v2 *frozen)
+{
+ (void) info;
+ (void) abfd;
+ BFD_ASSERT (subsec->encoding == 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 = NONE,
+ };
+
+ 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;
+}
+
+/* True, the tag needs to be merged, False don't merge. */
+static
+obj_attr_v2_merge_result
+_bfd_elf_obj_attr_v2_tag_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 = NONE,
+ };
+
+ 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 none is detected, the
+ subsection is considered unmergeable. */
+ if (! res.merge && res.reason == UNSUPPORTED)
+ {
+ 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);
+ }
+ }
+ }
+
+ return res;
+}
+
+static void
+obj_attr_v2_default_append (struct bfd_link_info *info,
+ obj_attr_subsection_v2 *s_abfd_missing,
+ obj_attr_v2 *a_ref)
+{
+ obj_attr_v2 *new_attr = obj_attr_v2_tag_default (info, s_abfd_missing, a_ref);
+ LINKED_LIST_APPEND(obj_attr_v2) (s_abfd_missing, new_attr);
+}
+
+static obj_attr_subsection_v2 *
+obj_attr_subsection_v2_defaults (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)
+ obj_attr_v2_default_append (info, new_subsec, attr);
+
+ return new_subsec;
+}
+
+static obj_attr_subsection_v2 *
+obj_attr_subsection_v2_perfect_match (struct bfd_link_info *info,
+ bfd *ref_bfd, bfd *abfd,
+ obj_attr_subsection_v2 *s_ref,
+ obj_attr_subsection_v2 *s_abfd)
+{
+ const struct elf_backend_data *be = get_elf_backend_data (abfd);
+
+ void
+ report_missing_required_obj_attr (bfd * ebfd, uint32_t tag)
+ {
+ 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" ), ebfd, tag_s, s_ref->name);
+ else
+ info->callbacks->einfo (
+ _("%X%pB: error: missing required object attribute 'Tag_unknown_%u' in "
+ "subsection '%s'\n" ), ebfd, tag, s_ref->name);
+ }
+
+ void
+ report_mismatching_required_obj_attr (obj_attr_v2 *a_ref, obj_attr_v2 *a_abfd)
+ {
+ const char* tag_s = obj_attr_v2_tag_to_string (be, s_ref->name, a_ref->tag);
+ if (s_ref->encoding == 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);
+ }
+ }
+
+ 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 (abfd, a_ref->tag);
+ a_ref = a_ref->next;
+ }
+ else if (a_ref->tag > a_abfd->tag)
+ {
+ success = false;
+ report_missing_required_obj_attr (ref_bfd, a_abfd->tag);
+ a_abfd = a_abfd->next;
+ }
+ else
+ {
+ if (s_ref->encoding == ULEB128)
+ {
+ if (a_ref->vals.uint_val != a_abfd->vals.uint_val)
+ {
+ success = false;
+ report_mismatching_required_obj_attr (a_ref, a_abfd);
+ }
+ }
+ else if (s_ref->encoding == NTBS)
+ {
+ if (strcmp (a_ref->vals.string_val, a_abfd->vals.string_val) != 0)
+ {
+ success = false;
+ report_mismatching_required_obj_attr (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 (ref_bfd, a_abfd->tag);
+ }
+
+ for (; a_ref != NULL; a_ref = a_ref->next)
+ {
+ success = false;
+ report_missing_required_obj_attr (abfd, a_ref->tag);
+ }
+
+ return success ? s_ref : NULL;
+}
+
+static obj_attr_v2 *
+search_obj_attr_v2_by_tag (obj_attr_v2 *attr_first, uint32_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;
+}
+
+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 =
+ search_obj_attr_v2_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 = obj_attr_v2_tag_default (info, s_abfd, a_ref);
+ obj_attr_v2_merge_result res =
+ _bfd_elf_obj_attr_v2_tag_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 = obj_attr_v2_tag_default (info, s_ref, a_abfd);
+ obj_attr_v2_merge_result res =
+ _bfd_elf_obj_attr_v2_tag_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 =
+ _bfd_elf_obj_attr_v2_tag_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 = obj_attr_v2_tag_default (info, s_ref, a_abfd);
+ obj_attr_v2_merge_result res =
+ _bfd_elf_obj_attr_v2_tag_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 = search_obj_attr_v2_by_tag (a_frozen_first, a_ref->tag);
+ if (a_frozen != NULL)
+ a_frozen_first = a_frozen;
+
+ obj_attr_v2 *a_default = obj_attr_v2_tag_default (info, s_abfd, a_ref);
+ obj_attr_v2_merge_result res =
+ _bfd_elf_obj_attr_v2_tag_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;
+}
+
+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 obj_attr_subsection_v2_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);
+}
+
+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 =
+ obj_attr_subsection_v2_defaults (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;
+}
+
+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 =
+ obj_attr_subsection_v2_defaults (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);
+}
+
+const char *
+obj_attr_subsection_v2_optional_to_string (bool optional)
+{
+ return optional ? "optional" : "required";
+}
+
+const char *
+obj_attr_encoding_v2_to_string (obj_attr_encoding_v2 encoding)
+{
+ return (encoding == ULEB128) ? "uleb128" : "ntbs";
+}
+
+static bool
+check_mismatching_parameters (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;
+}
+
+static bool
+obj_attr_subsection_v2_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 = check_mismatching_parameters (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;
+}
+
+static bool
+obj_attr_subsection_v2_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
+ obj_attr_subsection_v2_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 = check_mismatching_parameters (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 = check_mismatching_parameters (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 = check_mismatching_parameters (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 = check_mismatching_parameters (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;
+}
+
+static void
+mark_unknown_subsections (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;
+ }
+}
+
+static bfd *
+bfd_elf_merge_build_attributes (struct bfd_link_info *info, bfd *ref_bfd)
+{
+ const struct elf_backend_data *be_out =
+ get_elf_backend_data (info->output_bfd);
+ unsigned int elfclass = be_out->s->elfclass;
+ int elf_machine_code = be_out->elf_machine_code;
+
+ bool
+ valid_elf_object (bfd *abfd)
+ {
+ 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);
+ }
+
+ mark_unknown_subsections (ref_bfd);
+
+ bool success = true;
+ for (bfd *abfd = info->input_bfds; abfd != NULL; abfd = abfd->link.next)
+ if (abfd != ref_bfd && valid_elf_object (abfd))
+ {
+ mark_unknown_subsections (abfd);
+ success &= obj_attr_subsection_v2_merge (info, ref_bfd, abfd);
+ }
+
+ return success ? ref_bfd : NULL;
+}
+
+/* Prune the given attribute, and return the next one. */
+static obj_attr_v2 *
+obj_attr_v2_prune (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. */
+static obj_attr_subsection_v2 *
+obj_attr_subsection_v2_prune (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;
+}
+
+static void
+info_obj_attr_v2_prune (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);
+}
+
+static void
+info_obj_attr_subsection_v2_prune (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
+obj_attr_v2_filter_prune (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)
+ {
+ info_obj_attr_v2_prune (info, subsec, attr);
+ attr = obj_attr_v2_prune (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
+obj_attr_subsection_v2_filter_prune (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)
+ obj_attr_v2_filter_prune (info, subsec);
+
+ if (subsec->size == 0 || subsec->status != obj_attr_subsection_v2_ok)
+ {
+ info_obj_attr_subsection_v2_prune (info, subsec);
+ subsec = obj_attr_subsection_v2_prune (plist, subsec);
+ }
+ else
+ subsec = subsec->next;
+ }
+}
+
+static bfd *
+bfd_elf_prune_build_attributes (struct bfd_link_info *info, bfd *abfd)
+{
+ if (abfd == NULL)
+ return NULL;
+
+ obj_attr_subsection_list *plist = &elf_obj_attr_subsections (abfd);
+ obj_attr_subsection_v2_filter_prune (info, plist);
+ return (plist->size != 0) ? abfd : NULL;
+}
+
+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;
+
+ /* 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. */
+
+ if (res.sec == NULL)
+ 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. */
+ obj_attr_subsection_v2_sort (frozen_);
+
+ /* 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. */
+ bool success = true;
+ success &= obj_attr_subsection_v2_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 = bfd_elf_merge_build_attributes (info, res.pbfd);
+ success &= (res.pbfd != NULL);
+ res.pbfd = bfd_elf_prune_build_attributes (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. */
static obj_attribute *
elf_new_obj_attr (bfd *abfd, int vendor, unsigned int tag)
@@ -1097,6 +2318,32 @@ bfd_elf_parse_attr_section_v2 (bfd *abfd,
obj_attr_subsection_v2_sort (subsecs);
}
+/* 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
@@ -1318,6 +2565,7 @@ _bfd_elf_obj_attr_v2_init (unsigned int tag,
memset ((void *) attr, 0, sizeof (obj_attr_v2));
attr->tag = tag;
attr->vals = vals;
+ attr->status = obj_attr_v2_ok;
return attr;
}
@@ -1413,6 +2661,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;
}
@@ -33,11 +33,24 @@ typedef enum obj_attr_encoding_v2
NTBS = 1,
} obj_attr_encoding_v2;
+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 struct obj_attr_v2 {
/* The name/tag of an attribute. */
uint32_t tag;
@@ -45,6 +58,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;
@@ -59,6 +75,16 @@ typedef enum obj_attr_subsection_scope_v2
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;
@@ -87,8 +113,14 @@ typedef struct obj_attr_subsection_v2 {
/* The size of the list. */
uint32_t size;
+ /* The subsection status after merge. */
+ obj_attr_subsection_v2_status status;
+
} 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. */
@@ -118,3 +150,47 @@ typedef struct
const size_t len;
} known_subsection_v2;
+struct elf_backend_data;
+
+extern const known_subsection_v2 *
+identify_subsection (const struct elf_backend_data *, const char*);
+
+extern const known_tag_v2 *
+known_obj_attr_v2_find_by_tag (const struct elf_backend_data *,
+ const char*, uint32_t);
+
+extern const char *
+obj_attr_v2_tag_to_string (const struct elf_backend_data *, const char*,
+ uint32_t);
+
+enum obj_attr_v2_merge_result_reason
+{
+ /* Default: everything is ok. */
+ NONE = 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 *);
@@ -1641,6 +1641,31 @@ struct elf_backend_data
/* The version of object attributes (1 or 2). */
unsigned int obj_attrs_version;
+ /* The known subsections and attributes (v2 only). */
+ const known_subsection_v2 *obj_attr_v2_known_subsections;
+ /* 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 known_tag_v2 *, 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
@@ -1662,6 +1687,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 *);
@@ -3083,9 +3111,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 *, int, unsigned int);
+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 *);
@@ -11446,17 +11446,29 @@ elfobj_grok_gnu_build_id (bfd *abfd, Elf_Internal_Note *note)
static bool
elfobj_grok_gnu_note (bfd *abfd, Elf_Internal_Note *note)
{
+ bool res;
switch (note->type)
{
- default:
- return true;
-
case NT_GNU_PROPERTY_TYPE_0:
- return _bfd_elf_parse_gnu_properties (abfd, note);
+ res = _bfd_elf_parse_gnu_properties (abfd, note);
+ if (res)
+ {
+ uint32_t version = get_elf_backend_data (abfd)->obj_attrs_version;
+
+ if (version == 2)
+ _bfd_elf_translate_relevant_gnu_props_to_obj_attrs (abfd);
+ }
+ break;
case NT_GNU_BUILD_ID:
- return elfobj_grok_gnu_build_id (abfd, note);
+ res = elfobj_grok_gnu_build_id (abfd, note);
+ break;
+
+ default:
+ res = true;
+ break;
}
+ return res;
}
static bool
@@ -559,6 +559,27 @@
#ifndef elf_backend_obj_attrs_version
#define elf_backend_obj_attrs_version 1 /* Default version. */
#endif
+#ifndef elf_backend_obj_attr_v2_known_subsections
+#define elf_backend_obj_attr_v2_known_subsections NULL
+#endif
+#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
@@ -571,6 +592,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
@@ -938,10 +962,18 @@ static const struct elf_backend_data elfNN_bed =
elf_backend_obj_attrs_arg_type,
elf_backend_obj_attrs_section_type,
elf_backend_obj_attrs_version,
+ 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,
@@ -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. */