+ riscv_isa_ext_class_t ca = riscv_get_prefix_class (a);
+ riscv_isa_ext_class_t cb = riscv_get_prefix_class (b);
+
+ /* Extension name without prefix */
+ const char *anp = riscv_skip_prefix (a, ca);
+ const char *bnp = riscv_skip_prefix (b, cb);
+
+ if (ca == cb)
+ return strcasecmp (anp, bnp);
+
+ return (int)ca - (int)cb;
+}
+
+/* Merge multi letter extensions. PIN is a pointer to the head of the input
+ object subset list. Likewise for POUT and the output object. Return TRUE
+ on success and FALSE when a conflict is found. */
+
+static bfd_boolean
+riscv_merge_multi_letter_ext (bfd *ibfd,
+ riscv_subset_t **pin,
+ riscv_subset_t **pout)
+{
+ riscv_subset_t *in = *pin;
+ riscv_subset_t *out = *pout;
+ riscv_subset_t *tail;
+
+ int cmp;
+
+ while (in && out)
+ {
+ cmp = riscv_prefix_cmp (in->name, out->name);
+
+ if (cmp < 0)
+ {
+ /* `in' comes before `out', append `in' and increment. */
+ riscv_add_subset (&merged_subsets, in->name, in->major_version,
+ in->minor_version);
+ in = in->next;
+ }
+ else if (cmp > 0)
+ {
+ /* `out' comes before `in', append `out' and increment. */
+ riscv_add_subset (&merged_subsets, out->name, out->major_version,
+ out->minor_version);
+ out = out->next;
+ }
+ else
+ {
+ /* Both present, check version and increment both. */
+ if ((in->major_version != out->major_version)
+ || (in->minor_version != out->minor_version))
+ {
+ riscv_version_mismatch (ibfd, in, out);
+ return FALSE;
+ }
+
+ riscv_add_subset (&merged_subsets, out->name, out->major_version,
+ out->minor_version);
+ out = out->next;
+ in = in->next;
+ }
+ }
+
+ if (in || out) {
+ /* If we're here, either `in' or `out' is running longer than
+ the other. So, we need to append the corresponding tail. */
+ tail = in ? in : out;
+
+ while (tail)
+ {
+ riscv_add_subset (&merged_subsets, tail->name, tail->major_version,
+ tail->minor_version);
+ tail = tail->next;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Merge Tag_RISCV_arch attribute. */
+
+static char *
+riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch)
+{
+ riscv_subset_t *in, *out;
+ char *merged_arch_str;
+
+ unsigned xlen_in, xlen_out;
+ merged_subsets.head = NULL;
+ merged_subsets.tail = NULL;
+
+ riscv_parse_subset_t rpe_in;
+ riscv_parse_subset_t rpe_out;
+
+ rpe_in.subset_list = &in_subsets;
+ rpe_in.error_handler = _bfd_error_handler;
+ rpe_in.xlen = &xlen_in;
+
+ rpe_out.subset_list = &out_subsets;
+ rpe_out.error_handler = _bfd_error_handler;
+ rpe_out.xlen = &xlen_out;
+
+ if (in_arch == NULL && out_arch == NULL)
+ return NULL;
+
+ if (in_arch == NULL && out_arch != NULL)
+ return out_arch;
+
+ if (in_arch != NULL && out_arch == NULL)
+ return in_arch;
+
+ /* Parse subset from arch string. */
+ if (!riscv_parse_subset (&rpe_in, in_arch))
+ return NULL;
+
+ if (!riscv_parse_subset (&rpe_out, out_arch))
+ return NULL;
+
+ /* Checking XLEN. */
+ if (xlen_out != xlen_in)
+ {
+ _bfd_error_handler
+ (_("error: %pB: ISA string of input (%s) doesn't match "
+ "output (%s)."), ibfd, in_arch, out_arch);
+ return NULL;
+ }
+
+ /* 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 (ibfd, &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 bfd_boolean
+riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info)
+{
+ bfd *obfd = info->output_bfd;
+ obj_attribute *in_attr;
+ obj_attribute *out_attr;
+ bfd_boolean result = TRUE;
+ 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 arch 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 (out_attr[i].i != in_attr[i].i)
+ {
+ _bfd_error_handler
+ (_("error: %pB: conflicting priv spec version "
+ "(major/minor/revision)."), ibfd);
+ result = FALSE;
+ }
+ 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 bfd_boolean
+_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;
+
+ new_flags = elf_elfheader (ibfd)->e_flags;
+ old_flags = elf_elfheader (obfd)->e_flags;
+
+ if (! elf_flags_init (obfd))
+ {
+ elf_flags_init (obfd) = TRUE;
+ elf_elfheader (obfd)->e_flags = new_flags;
+ return TRUE;
+ }
+
+ /* 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))
+ {
+ bfd_boolean null_input_bfd = TRUE;
+ bfd_boolean only_data_sections = TRUE;
+ asection *sec;
+
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ {
+ if ((bfd_section_flags (sec)
+ & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+ == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+ only_data_sections = FALSE;
+
+ null_input_bfd = FALSE;
+ break;
+ }
+
+ if (null_input_bfd || only_data_sections)
+ return TRUE;
+ }
+
+ /* Disallow linking different float ABIs. */
+ if ((old_flags ^ new_flags) & EF_RISCV_FLOAT_ABI)
+ {
+ (*_bfd_error_handler)
+ (_("%pB: can't link %s modules with %s modules"), ibfd,
+ riscv_float_abi_string (new_flags),
+ riscv_float_abi_string (old_flags));
+ goto fail;
+ }
+
+ /* Disallow linking RVE and non-RVE. */
+ if ((old_flags ^ new_flags) & EF_RISCV_RVE)
+ {
+ (*_bfd_error_handler)
+ (_("%pB: can't link RVE with other target"), ibfd);
+ goto fail;
+ }
+
+ /* Allow linking RVC and non-RVC, and keep the RVC flag. */
+ elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC;
+
+ return TRUE;
+
+ fail:
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+}
+
+/* Delete some bytes from a section while relaxing. */
+
+static bfd_boolean
+riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count,
+ struct bfd_link_info *link_info)
+{
+ unsigned int i, symcount;
+ bfd_vma toaddr = sec->size;
+ struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
+ Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
+ struct bfd_elf_section_data *data = elf_section_data (sec);
+ bfd_byte *contents = data->this_hdr.contents;