+ /* Merge subset list. */
+ in = in_subsets.head;
+ out = out_subsets.head;
+
+ /* Merge standard extension. */
+ if (!riscv_merge_std_ext (ibfd, in_arch, out_arch, &in, &out))
+ return NULL;
+
+ /* Merge all non-single letter extensions with single call. */
+ if (!riscv_merge_multi_letter_ext (&in, &out))
+ return NULL;
+
+ if (xlen_in != xlen_out)
+ {
+ _bfd_error_handler
+ (_("error: %pB: XLEN of input (%u) doesn't match "
+ "output (%u)"), ibfd, xlen_in, xlen_out);
+ return NULL;
+ }
+
+ if (xlen_in != ARCH_SIZE)
+ {
+ _bfd_error_handler
+ (_("error: %pB: unsupported XLEN (%u), you might be "
+ "using wrong emulation"), ibfd, xlen_in);
+ return NULL;
+ }
+
+ merged_arch_str = riscv_arch_str (ARCH_SIZE, &merged_subsets);
+
+ /* Release the subset lists. */
+ riscv_release_subset_list (&in_subsets);
+ riscv_release_subset_list (&out_subsets);
+ riscv_release_subset_list (&merged_subsets);
+
+ return merged_arch_str;
+}
+
+/* Merge object attributes from IBFD into output_bfd of INFO.
+ Raise an error if there are conflicting attributes. */
+
+static bool
+riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
+{
+ bfd *obfd = info->output_bfd;
+ obj_attribute *in_attr;
+ obj_attribute *out_attr;
+ bool result = true;
+ bool priv_attrs_merged = false;
+ const char *sec_name = get_elf_backend_data (ibfd)->obj_attrs_section;
+ unsigned int i;
+
+ /* Skip linker created files. */
+ if (ibfd->flags & BFD_LINKER_CREATED)
+ return true;
+
+ /* Skip any input that doesn't have an attribute section.
+ This enables to link object files without attribute section with
+ any others. */
+ if (bfd_get_section_by_name (ibfd, sec_name) == NULL)
+ return true;
+
+ if (!elf_known_obj_attributes_proc (obfd)[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+ out_attr = elf_known_obj_attributes_proc (obfd);
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ out_attr[0].i = 1;
+
+ return true;
+ }
+
+ in_attr = elf_known_obj_attributes_proc (ibfd);
+ out_attr = elf_known_obj_attributes_proc (obfd);
+
+ for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
+ {
+ switch (i)
+ {
+ case Tag_RISCV_arch:
+ if (!out_attr[Tag_RISCV_arch].s)
+ out_attr[Tag_RISCV_arch].s = in_attr[Tag_RISCV_arch].s;
+ else if (in_attr[Tag_RISCV_arch].s
+ && out_attr[Tag_RISCV_arch].s)
+ {
+ /* Check compatible. */
+ char *merged_arch =
+ riscv_merge_arch_attr_info (ibfd,
+ in_attr[Tag_RISCV_arch].s,
+ out_attr[Tag_RISCV_arch].s);
+ if (merged_arch == NULL)
+ {
+ result = false;
+ out_attr[Tag_RISCV_arch].s = "";
+ }
+ else
+ out_attr[Tag_RISCV_arch].s = merged_arch;
+ }
+ break;
+
+ case Tag_RISCV_priv_spec:
+ case Tag_RISCV_priv_spec_minor:
+ case Tag_RISCV_priv_spec_revision:
+ /* If we have handled the privileged elf attributes, then skip it. */
+ if (!priv_attrs_merged)
+ {
+ unsigned int Tag_a = Tag_RISCV_priv_spec;
+ unsigned int Tag_b = Tag_RISCV_priv_spec_minor;
+ unsigned int Tag_c = Tag_RISCV_priv_spec_revision;
+ enum riscv_spec_class in_priv_spec = PRIV_SPEC_CLASS_NONE;
+ enum riscv_spec_class out_priv_spec = PRIV_SPEC_CLASS_NONE;
+
+ /* Get the privileged spec class from elf attributes. */
+ riscv_get_priv_spec_class_from_numbers (in_attr[Tag_a].i,
+ in_attr[Tag_b].i,
+ in_attr[Tag_c].i,
+ &in_priv_spec);
+ riscv_get_priv_spec_class_from_numbers (out_attr[Tag_a].i,
+ out_attr[Tag_b].i,
+ out_attr[Tag_c].i,
+ &out_priv_spec);
+
+ /* Allow to link the object without the privileged specs. */
+ if (out_priv_spec == PRIV_SPEC_CLASS_NONE)
+ {
+ out_attr[Tag_a].i = in_attr[Tag_a].i;
+ out_attr[Tag_b].i = in_attr[Tag_b].i;
+ out_attr[Tag_c].i = in_attr[Tag_c].i;
+ }
+ else if (in_priv_spec != PRIV_SPEC_CLASS_NONE
+ && in_priv_spec != out_priv_spec)
+ {
+ _bfd_error_handler
+ (_("warning: %pB use privileged spec version %u.%u.%u but "
+ "the output use version %u.%u.%u"),
+ ibfd,
+ in_attr[Tag_a].i,
+ in_attr[Tag_b].i,
+ in_attr[Tag_c].i,
+ out_attr[Tag_a].i,
+ out_attr[Tag_b].i,
+ out_attr[Tag_c].i);
+
+ /* The privileged spec v1.9.1 can not be linked with others
+ since the conflicts, so we plan to drop it in a year or
+ two. */
+ if (in_priv_spec == PRIV_SPEC_CLASS_1P9P1
+ || out_priv_spec == PRIV_SPEC_CLASS_1P9P1)
+ {
+ _bfd_error_handler
+ (_("warning: privileged spec version 1.9.1 can not be "
+ "linked with other spec versions"));
+ }
+
+ /* Update the output privileged spec to the newest one. */
+ if (in_priv_spec > out_priv_spec)
+ {
+ out_attr[Tag_a].i = in_attr[Tag_a].i;
+ out_attr[Tag_b].i = in_attr[Tag_b].i;
+ out_attr[Tag_c].i = in_attr[Tag_c].i;
+ }
+ }
+ priv_attrs_merged = true;
+ }
+ break;
+
+ case Tag_RISCV_unaligned_access:
+ out_attr[i].i |= in_attr[i].i;
+ break;
+
+ case Tag_RISCV_stack_align:
+ if (out_attr[i].i == 0)
+ out_attr[i].i = in_attr[i].i;
+ else if (in_attr[i].i != 0
+ && out_attr[i].i != 0
+ && out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("error: %pB use %u-byte stack aligned but the output "
+ "use %u-byte stack aligned"),
+ ibfd, in_attr[i].i, out_attr[i].i);
+ result = false;
+ }
+ break;
+
+ default:
+ result &= _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
+ }
+
+ /* If out_attr was copied from in_attr then it won't have a type yet. */
+ if (in_attr[i].type && !out_attr[i].type)
+ out_attr[i].type = in_attr[i].type;
+ }
+
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ if (!_bfd_elf_merge_object_attributes (ibfd, info))
+ return false;
+
+ /* Check for any attributes not known on RISC-V. */
+ result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
+
+ return result;
+}
+
+/* Merge backend specific data from an object file to the output
+ object file when linking. */
+
+static bool
+_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
+{
+ bfd *obfd = info->output_bfd;
+ flagword new_flags, old_flags;
+
+ if (!is_riscv_elf (ibfd) || !is_riscv_elf (obfd))
+ return true;
+
+ if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0)
+ {
+ (*_bfd_error_handler)
+ (_("%pB: ABI is incompatible with that of the selected emulation:\n"
+ " target emulation `%s' does not match `%s'"),
+ ibfd, bfd_get_target (ibfd), bfd_get_target (obfd));
+ return false;
+ }
+
+ if (!_bfd_elf_merge_object_attributes (ibfd, info))
+ return false;
+
+ if (!riscv_merge_attributes (ibfd, info))
+ return false;
+
+ /* Check to see if the input BFD actually contains any sections. If not,
+ its flags may not have been initialized either, but it cannot actually
+ cause any incompatibility. Do not short-circuit dynamic objects; their
+ section list may be emptied by elf_link_add_object_symbols.
+
+ Also check to see if there are no code sections in the input. In this
+ case, there is no need to check for code specific flags. */
+ if (!(ibfd->flags & DYNAMIC))
+ {
+ bool null_input_bfd = true;
+ bool only_data_sections = true;
+ asection *sec;
+
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)