[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;