]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
bfd/RISC-V: move _bfd_riscv_elf_merge_private_bfd_data()
authorJan Beulich <jbeulich@suse.com>
Fri, 5 Jun 2026 09:12:00 +0000 (11:12 +0200)
committerJan Beulich <jbeulich@suse.com>
Fri, 5 Jun 2026 09:12:00 +0000 (11:12 +0200)
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 <jiawei@iscas.ac.cn>
bfd/elfnn-riscv.c
bfd/elfxx-riscv.c
bfd/elfxx-riscv.h

index eb3d7926c262ee988f77334b165a3d5018fcd075..2aa0e12ff2050b630de0ffabac4a76f440f8de89 100644 (file)
@@ -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
index b8297ba86f1b97c5e45c12ef638209d09ec156b4..df6ff6448c1fc9c97126c369a53c3fbe6d9ed284 100644 (file)
@@ -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.  */
index 2241dc3f8b80ba85f5c1671a63b83d01e72b4da8..e57e90462f85dc5da6eebb4cf52ea67e2422577c 100644 (file)
 
 #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;