[18/19] bfd/RISC-V: move _bfd_riscv_elf_merge_private_bfd_data()

Message ID 07919897-963f-406e-9583-729ea168b0b9@suse.com
State New
Headers
Series RISC-V: assorted fixes and (hopefully) improvements |

Commit Message

Jan Beulich April 21, 2026, 11:57 a.m. UTC
  The sole size dependent parameter used there is ARCH_SIZE. For just this
there's no good reason to build all of the involved code twice. Make the
value a function parameter instead.
  

Patch

--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -193,11 +193,6 @@  struct _bfd_riscv_elf_obj_tdata
   (*((h) != NULL ? &riscv_elf_hash_entry (h)->tls_type		\
      : &_bfd_riscv_elf_local_got_tls_type (abfd) [symndx]))
 
-#define is_riscv_elf(bfd)				\
-  (bfd_get_flavour (bfd) == bfd_target_elf_flavour	\
-   && elf_tdata (bfd) != NULL				\
-   && elf_object_id (bfd) == RISCV_ELF_DATA)
-
 static bool
 elfNN_riscv_mkobject (bfd *abfd)
 {
@@ -3918,560 +3913,10 @@  riscv_reloc_type_class (const struct bfd
     }
 }
 
-/* Given the ELF header flags in FLAGS, it returns a string that describes the
-   float ABI.  */
-
-static const char *
-riscv_float_abi_string (flagword flags)
-{
-  switch (flags & EF_RISCV_FLOAT_ABI)
-    {
-    case EF_RISCV_FLOAT_ABI_SOFT:
-      return "soft-float";
-      break;
-    case EF_RISCV_FLOAT_ABI_SINGLE:
-      return "single-float";
-      break;
-    case EF_RISCV_FLOAT_ABI_DOUBLE:
-      return "double-float";
-      break;
-    case EF_RISCV_FLOAT_ABI_QUAD:
-      return "quad-float";
-      break;
-    default:
-      abort ();
-    }
-}
-
-/* The information of architecture elf attributes.  */
-static riscv_subset_list_t in_subsets;
-static riscv_subset_list_t out_subsets;
-static riscv_subset_list_t merged_subsets;
-
-/* Predicator for standard extension.  */
-
-static bool
-riscv_std_ext_p (const char *name)
-{
-  return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
-}
-
-/* Update the output subset's version to match the input when the input
-   subset's version is newer.  */
-
-static void
-riscv_update_subset_version (struct riscv_subset_t *in,
-			     struct riscv_subset_t *out)
-{
-  if (in == NULL || out == NULL)
-    return;
-
-  /* Update the output ISA versions to the newest ones, but otherwise don't
-     provide any errors or warnings about mis-matched ISA versions as it's
-     generally too tricky to check for these at link time. */
-  if ((in->major_version > out->major_version)
-      || (in->major_version == out->major_version
-	  && in->minor_version > out->minor_version)
-      || (out->major_version == RISCV_UNKNOWN_VERSION))
-    {
-      out->major_version = in->major_version;
-      out->minor_version = in->minor_version;
-    }
-}
-
-/* Return true if subset is 'i' or 'e'.  */
-
 static bool
-riscv_i_or_e_p (bfd *ibfd,
-		const char *arch,
-		struct riscv_subset_t *subset)
+riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 {
-  if ((strcasecmp (subset->name, "e") != 0)
-      && (strcasecmp (subset->name, "i") != 0))
-    {
-      _bfd_error_handler
-	(_("error: %pB: corrupted ISA string '%s'.  "
-	   "First letter should be 'i' or 'e' but got '%s'"),
-	   ibfd, arch, subset->name);
-      return false;
-    }
-  return true;
-}
-
-/* Merge standard extensions.
-
-   Return Value:
-     Return FALSE if failed to merge.
-
-   Arguments:
-     `bfd`: bfd handler.
-     `in_arch`: Raw ISA string for input object.
-     `out_arch`: Raw ISA string for output object.
-     `pin`: Subset list for input object.
-     `pout`: Subset list for output object.  */
-
-static bool
-riscv_merge_std_ext (bfd *ibfd,
-		     const char *in_arch,
-		     const char *out_arch,
-		     struct riscv_subset_t **pin,
-		     struct riscv_subset_t **pout)
-{
-  const char *standard_exts = "mafdqlcbjtpvnh";
-  const char *p;
-  struct riscv_subset_t *in = *pin;
-  struct riscv_subset_t *out = *pout;
-
-  /* First letter should be 'i' or 'e'.  */
-  if (!riscv_i_or_e_p (ibfd, in_arch, in))
-    return false;
-
-  if (!riscv_i_or_e_p (ibfd, out_arch, out))
-    return false;
-
-  if (strcasecmp (in->name, out->name) != 0)
-    {
-      /* TODO: We might allow merge 'i' with 'e'.  */
-      _bfd_error_handler
-	(_("error: %pB: mis-matched ISA string to merge '%s' and '%s'"),
-	 ibfd, in->name, out->name);
-      return false;
-    }
-
-  riscv_update_subset_version(in, out);
-  riscv_add_subset (&merged_subsets,
-		    out->name, out->major_version, out->minor_version);
-
-  in = in->next;
-  out = out->next;
-
-  /* Handle standard extension first.  */
-  for (p = standard_exts; *p; ++p)
-    {
-      struct riscv_subset_t *ext_in, *ext_out, *ext_merged;
-      char find_ext[2] = {*p, '\0'};
-      bool find_in, find_out;
-
-      find_in = riscv_lookup_subset (&in_subsets, find_ext, &ext_in);
-      find_out = riscv_lookup_subset (&out_subsets, find_ext, &ext_out);
-
-      if (!find_in && !find_out)
-	continue;
-
-      if (find_in && find_out)
-	riscv_update_subset_version(ext_in, ext_out);
-
-      ext_merged = find_out ? ext_out : ext_in;
-      riscv_add_subset (&merged_subsets, ext_merged->name,
-			ext_merged->major_version, ext_merged->minor_version);
-    }
-
-  /* Skip all standard extensions.  */
-  while ((in != NULL) && riscv_std_ext_p (in->name)) in = in->next;
-  while ((out != NULL) && riscv_std_ext_p (out->name)) out = out->next;
-
-  *pin = in;
-  *pout = out;
-
-  return true;
-}
-
-/* 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 bool
-riscv_merge_multi_letter_ext (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_compare_subsets (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.  */
-	  riscv_update_subset_version (in, out);
-
-	  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;
-  static char *merged_arch_str = NULL;
-
-  unsigned xlen_in, xlen_out;
-  merged_subsets.head = NULL;
-  merged_subsets.tail = NULL;
-
-  riscv_parse_subset_t riscv_rps_ld_in =
-    {&in_subsets, _bfd_error_handler, &xlen_in, NULL, false};
-  riscv_parse_subset_t riscv_rps_ld_out =
-    {&out_subsets, _bfd_error_handler, &xlen_out, NULL, false};
-
-  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 ISA string.  */
-  if (!riscv_parse_subset (&riscv_rps_ld_in, in_arch))
-    return NULL;
-  if (!riscv_parse_subset (&riscv_rps_ld_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 (&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;
-    }
-
-  /* Free the previous merged_arch_str which called xmalloc.  */
-  free (merged_arch_str);
-
-  merged_arch_str = riscv_arch_str (ARCH_SIZE, &merged_subsets,
-				    false/* update */);
-
-  /* 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)
-	      {
-		/* The abandoned privileged spec v1.9.1 can not be linked with
-		   others since the conflicts.  Keep the check since compatible
-		   issue.  */
-		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))
-    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)
-	{
-	  null_input_bfd = false;
-
-	  if ((bfd_section_flags (sec)
-	       & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
-	      == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
-	    {
-	      only_data_sections = false;
-	      break;
-	    }
-	}
-
-      if (null_input_bfd || only_data_sections)
-	return true;
-    }
-
-  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;
-    }
-
-  /* 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;
-
-  /* Allow linking TSO and non-TSO, and keep the TSO flag.  */
-  elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_TSO;
-
-  return true;
-
- fail:
-  bfd_set_error (bfd_error_bad_value);
-  return false;
+  return _bfd_riscv_elf_merge_private_bfd_data (ibfd, info, ARCH_SIZE);
 }
 
 /* Ignore and report warning for the unknwon elf attribute.  */
@@ -6095,7 +5540,7 @@  elfNN_riscv_merge_gnu_properties (struct
   riscv_elf_link_hash_table_create
 #define bfd_elfNN_bfd_reloc_type_lookup		riscv_reloc_type_lookup
 #define bfd_elfNN_bfd_merge_private_bfd_data	\
-  _bfd_riscv_elf_merge_private_bfd_data
+  riscv_elf_merge_private_bfd_data
 #define bfd_elfNN_bfd_is_target_special_symbol	\
   riscv_elf_is_target_special_symbol
 #define bfd_elfNN_bfd_relax_section		_bfd_riscv_relax_section
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -3432,6 +3432,566 @@  riscv_print_extensions (void)
   printf ("\n");
 }
 
+/* Given the ELF header flags in FLAGS, it returns a string that describes the
+   float ABI.  */
+
+static const char *
+riscv_float_abi_string (flagword flags)
+{
+  switch (flags & EF_RISCV_FLOAT_ABI)
+    {
+    case EF_RISCV_FLOAT_ABI_SOFT:
+      return "soft-float";
+      break;
+    case EF_RISCV_FLOAT_ABI_SINGLE:
+      return "single-float";
+      break;
+    case EF_RISCV_FLOAT_ABI_DOUBLE:
+      return "double-float";
+      break;
+    case EF_RISCV_FLOAT_ABI_QUAD:
+      return "quad-float";
+      break;
+    default:
+      abort ();
+    }
+}
+
+/* The information of architecture elf attributes.  */
+static riscv_subset_list_t in_subsets;
+static riscv_subset_list_t out_subsets;
+static riscv_subset_list_t merged_subsets;
+
+/* Predicator for standard extension.  */
+
+static bool
+riscv_std_ext_p (const char *name)
+{
+  return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's');
+}
+
+/* Update the output subset's version to match the input when the input
+   subset's version is newer.  */
+
+static void
+riscv_update_subset_version (struct riscv_subset_t *in,
+			     struct riscv_subset_t *out)
+{
+  if (in == NULL || out == NULL)
+    return;
+
+  /* Update the output ISA versions to the newest ones, but otherwise don't
+     provide any errors or warnings about mis-matched ISA versions as it's
+     generally too tricky to check for these at link time. */
+  if ((in->major_version > out->major_version)
+      || (in->major_version == out->major_version
+	  && in->minor_version > out->minor_version)
+      || (out->major_version == RISCV_UNKNOWN_VERSION))
+    {
+      out->major_version = in->major_version;
+      out->minor_version = in->minor_version;
+    }
+}
+
+/* Return true if subset is 'i' or 'e'.  */
+
+static bool
+riscv_i_or_e_p (bfd *ibfd,
+		const char *arch,
+		struct riscv_subset_t *subset)
+{
+  if ((strcasecmp (subset->name, "e") != 0)
+      && (strcasecmp (subset->name, "i") != 0))
+    {
+      _bfd_error_handler
+	(_("error: %pB: corrupted ISA string '%s'.  "
+	   "First letter should be 'i' or 'e' but got '%s'"),
+	   ibfd, arch, subset->name);
+      return false;
+    }
+  return true;
+}
+
+/* Merge standard extensions.
+
+   Return Value:
+     Return FALSE if failed to merge.
+
+   Arguments:
+     `bfd`: bfd handler.
+     `in_arch`: Raw ISA string for input object.
+     `out_arch`: Raw ISA string for output object.
+     `pin`: Subset list for input object.
+     `pout`: Subset list for output object.  */
+
+static bool
+riscv_merge_std_ext (bfd *ibfd,
+		     const char *in_arch,
+		     const char *out_arch,
+		     struct riscv_subset_t **pin,
+		     struct riscv_subset_t **pout)
+{
+  const char *standard_exts = "mafdqlcbjtpvnh";
+  const char *p;
+  struct riscv_subset_t *in = *pin;
+  struct riscv_subset_t *out = *pout;
+
+  /* First letter should be 'i' or 'e'.  */
+  if (!riscv_i_or_e_p (ibfd, in_arch, in))
+    return false;
+
+  if (!riscv_i_or_e_p (ibfd, out_arch, out))
+    return false;
+
+  if (strcasecmp (in->name, out->name) != 0)
+    {
+      /* TODO: We might allow merge 'i' with 'e'.  */
+      _bfd_error_handler
+	(_("error: %pB: mis-matched ISA string to merge '%s' and '%s'"),
+	 ibfd, in->name, out->name);
+      return false;
+    }
+
+  riscv_update_subset_version(in, out);
+  riscv_add_subset (&merged_subsets,
+		    out->name, out->major_version, out->minor_version);
+
+  in = in->next;
+  out = out->next;
+
+  /* Handle standard extension first.  */
+  for (p = standard_exts; *p; ++p)
+    {
+      struct riscv_subset_t *ext_in, *ext_out, *ext_merged;
+      char find_ext[2] = {*p, '\0'};
+      bool find_in, find_out;
+
+      find_in = riscv_lookup_subset (&in_subsets, find_ext, &ext_in);
+      find_out = riscv_lookup_subset (&out_subsets, find_ext, &ext_out);
+
+      if (!find_in && !find_out)
+	continue;
+
+      if (find_in && find_out)
+	riscv_update_subset_version(ext_in, ext_out);
+
+      ext_merged = find_out ? ext_out : ext_in;
+      riscv_add_subset (&merged_subsets, ext_merged->name,
+			ext_merged->major_version, ext_merged->minor_version);
+    }
+
+  /* Skip all standard extensions.  */
+  while ((in != NULL) && riscv_std_ext_p (in->name)) in = in->next;
+  while ((out != NULL) && riscv_std_ext_p (out->name)) out = out->next;
+
+  *pin = in;
+  *pout = out;
+
+  return true;
+}
+
+/* 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 bool
+riscv_merge_multi_letter_ext (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_compare_subsets (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.  */
+	  riscv_update_subset_version (in, out);
+
+	  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,
+			    unsigned int arch_size)
+{
+  riscv_subset_t *in, *out;
+  static char *merged_arch_str = NULL;
+
+  unsigned xlen_in, xlen_out;
+  merged_subsets.head = NULL;
+  merged_subsets.tail = NULL;
+
+  riscv_parse_subset_t riscv_rps_ld_in =
+    {&in_subsets, _bfd_error_handler, &xlen_in, NULL, false};
+  riscv_parse_subset_t riscv_rps_ld_out =
+    {&out_subsets, _bfd_error_handler, &xlen_out, NULL, false};
+
+  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 ISA string.  */
+  if (!riscv_parse_subset (&riscv_rps_ld_in, in_arch))
+    return NULL;
+  if (!riscv_parse_subset (&riscv_rps_ld_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 (&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;
+    }
+
+  /* Free the previous merged_arch_str which called xmalloc.  */
+  free (merged_arch_str);
+
+  merged_arch_str = riscv_arch_str (arch_size, &merged_subsets,
+				    false/* update */);
+
+  /* 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,
+			unsigned int arch_size)
+{
+  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,
+					    arch_size);
+	    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)
+	      {
+		/* The abandoned privileged spec v1.9.1 can not be linked with
+		   others since the conflicts.  Keep the check since compatible
+		   issue.  */
+		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.  */
+
+bool
+_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info,
+				       unsigned int arch_size)
+{
+  bfd *obfd = info->output_bfd;
+  flagword new_flags, old_flags;
+
+  if (!is_riscv_elf (ibfd))
+    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, arch_size))
+    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)
+	{
+	  null_input_bfd = false;
+
+	  if ((bfd_section_flags (sec)
+	       & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+	      == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+	    {
+	      only_data_sections = false;
+	      break;
+	    }
+	}
+
+      if (null_input_bfd || only_data_sections)
+	return true;
+    }
+
+  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;
+    }
+
+  /* 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;
+
+  /* Allow linking TSO and non-TSO, and keep the TSO flag.  */
+  elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_TSO;
+
+  return true;
+
+ fail:
+  bfd_set_error (bfd_error_bad_value);
+  return false;
+}
+
 /* Find the first input bfd with GNU property and merge it with GPROP.  If no
    such input is found, add it to a new section at the last input.  Update
    GPROP accordingly.  */
--- a/bfd/elfxx-riscv.h
+++ b/bfd/elfxx-riscv.h
@@ -27,6 +27,11 @@ 
 
 #define RISCV_UNKNOWN_VERSION -1
 
+#define is_riscv_elf(bfd)				\
+  (bfd_get_flavour (bfd) == bfd_target_elf_flavour	\
+   && elf_tdata (bfd) != NULL				\
+   && elf_object_id (bfd) == RISCV_ELF_DATA)
+
 typedef enum
 {
     PLT_NORMAL            = 0x0,  /* Normal plts.  */
@@ -138,6 +143,10 @@  bfd_elf32_riscv_set_data_segment_info (s
 extern void
 bfd_elf64_riscv_set_data_segment_info (struct bfd_link_info *, int *);
 
+extern bool
+_bfd_riscv_elf_merge_private_bfd_data (bfd *, struct bfd_link_info *,
+				       unsigned int) ATTRIBUTE_HIDDEN;
+
 extern bfd *
 _bfd_riscv_elf_link_setup_gnu_properties (struct bfd_link_info *, uint32_t *) ATTRIBUTE_HIDDEN;