@@ -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
@@ -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. */
@@ -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;