[binutils-gdb] bfd/RISC-V: move _bfd_riscv_elf_merge_private_bfd_data()

Jan Beulich via Binutils-cvs <[email protected]>
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=3a5657bd3f26111214e70d3882150b06c9e45dff

commit 3a5657bd3f26111214e70d3882150b06c9e45dff
Author: Jan Beulich <[email protected]>
Date:   Fri Jun 5 11:12:00 2026 +0200

    bfd/RISC-V: move _bfd_riscv_elf_merge_private_bfd_data()
    
    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.
    
    Reviewed-by: Jiawei <[email protected]>

Diff:
---
 bfd/elfnn-riscv.c | 561 +-----------------------------------------------------
 bfd/elfxx-riscv.c | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 bfd/elfxx-riscv.h |   9 +
 3 files changed, 572 insertions(+), 558 deletions(-)

diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index eb3d7926c26..2aa0e12ff20 100644
--- 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_link_info *info,
     }
 }
 
-/* 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)
-{
-  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)
+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 bfd_link_info *info, bfd *abfd,
   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
diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index b8297ba86f1..df6ff6448c1 100644
--- 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.  */
diff --git a/bfd/elfxx-riscv.h b/bfd/elfxx-riscv.h
index 2241dc3f8b8..e57e90462f8 100644
--- 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 (struct bfd_link_info *, int *);
 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;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.