X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=bfd%2Felfnn-riscv.c;h=8f9f0d8a86a3a793b627b0de653b83a5df3338db;hb=ddc01737d34f16074b2c9a92c5a808be5c91340f;hp=a944b3302274700791a303187ab8083745741a3f;hpb=ddfe525f2875e76e0c32ff348fc0d3d6aa5fb4a3;p=binutils-gdb.git diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index a944b330227..8f9f0d8a86a 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -1,5 +1,5 @@ /* RISC-V-specific support for NN-bit ELF. - Copyright (C) 2011-2021 Free Software Foundation, Inc. + Copyright (C) 2011-2022 Free Software Foundation, Inc. Contributed by Andrew Waterman (andrew@sifive.com). Based on TILE-Gx and MIPS targets. @@ -32,7 +32,6 @@ #include "elf/riscv.h" #include "opcode/riscv.h" #include "objalloc.h" -#include "cpu-riscv.h" #include #ifndef CHAR_BIT @@ -62,6 +61,8 @@ #define ELF_MAXPAGESIZE 0x1000 #define ELF_COMMONPAGESIZE 0x1000 +#define RISCV_ATTRIBUTES_SECTION_NAME ".riscv.attributes" + /* RISC-V ELF linker hash entry. */ struct riscv_elf_link_hash_entry @@ -130,8 +131,12 @@ struct riscv_elf_link_hash_table /* The index of the last unused .rel.iplt slot. */ bfd_vma last_iplt_index; - /* Re-run the relaxations from relax pass 0 if TRUE. */ - bool restart_relax; + /* The data segment phase, don't relax the section + when it is exp_seg_relro_adjust. */ + int *data_segment_phase; + + /* Relocations for variant CC symbols may be present. */ + int variant_cc; }; /* Instruction access functions. */ @@ -400,7 +405,6 @@ riscv_elf_link_hash_table_create (bfd *abfd) } ret->max_alignment = (bfd_vma) -1; - ret->restart_relax = false; /* Create hash table for local ifunc. */ ret->loc_hash_table = htab_try_create (1024, @@ -1171,6 +1175,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) h->root.u.def.section = s; h->root.u.def.value = h->plt.offset; } + + /* If the symbol has STO_RISCV_VARIANT_CC flag, then raise the + variant_cc flag of riscv_elf_link_hash_table. */ + if (h->other & STO_RISCV_VARIANT_CC) + htab->variant_cc = 1; } else { @@ -1554,7 +1563,18 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) return false; } - return _bfd_elf_add_dynamic_tags (output_bfd, info, true); + /* Add dynamic entries. */ + if (elf_hash_table (info)->dynamic_sections_created) + { + if (!_bfd_elf_add_dynamic_tags (output_bfd, info, true)) + return false; + + if (htab->variant_cc + && !_bfd_elf_add_dynamic_entry (info, DT_RISCV_VARIANT_CC, 0)) + return false; + } + + return true; } #define TP_OFFSET 0 @@ -1735,25 +1755,41 @@ perform_relocation (const reloc_howto_type *howto, typedef struct { + /* PC value. */ bfd_vma address; + /* Relocation value with addend. */ bfd_vma value; + /* Original reloc type. */ + int type; } riscv_pcrel_hi_reloc; typedef struct riscv_pcrel_lo_reloc { + /* PC value of auipc. */ + bfd_vma address; + /* Internal relocation. */ + const Elf_Internal_Rela *reloc; + /* Record the following information helps to resolve the %pcrel + which cross different input section. For now we build a hash + for pcrel at the start of riscv_elf_relocate_section, and then + free the hash at the end. But riscv_elf_relocate_section only + handles an input section at a time, so that means we can only + resolve the %pcrel_hi and %pcrel_lo which are in the same input + section. Otherwise, we will report dangerous relocation errors + for those %pcrel which are not in the same input section. */ asection *input_section; struct bfd_link_info *info; reloc_howto_type *howto; - const Elf_Internal_Rela *reloc; - bfd_vma addr; - const char *name; bfd_byte *contents; + /* The next riscv_pcrel_lo_reloc. */ struct riscv_pcrel_lo_reloc *next; } riscv_pcrel_lo_reloc; typedef struct { + /* Hash table for riscv_pcrel_hi_reloc. */ htab_t hi_relocs; + /* Linked list for riscv_pcrel_lo_reloc. */ riscv_pcrel_lo_reloc *lo_relocs; } riscv_pcrel_relocs; @@ -1801,8 +1837,7 @@ riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel, bfd_vma pc, bfd_vma addr, bfd_byte *contents, - const reloc_howto_type *howto, - bfd *input_bfd ATTRIBUTE_UNUSED) + const reloc_howto_type *howto) { /* We may need to reference low addreses in PC-relative modes even when the PC is far away from these addresses. For example, undefweak references @@ -1835,11 +1870,14 @@ riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel, } static bool -riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr, - bfd_vma value, bool absolute) +riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, + bfd_vma addr, + bfd_vma value, + int type, + bool absolute) { bfd_vma offset = absolute ? value : value - addr; - riscv_pcrel_hi_reloc entry = {addr, offset}; + riscv_pcrel_hi_reloc entry = {addr, offset, type}; riscv_pcrel_hi_reloc **slot = (riscv_pcrel_hi_reloc **) htab_find_slot (p->hi_relocs, &entry, INSERT); @@ -1853,20 +1891,19 @@ riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr, static bool riscv_record_pcrel_lo_reloc (riscv_pcrel_relocs *p, + bfd_vma addr, + const Elf_Internal_Rela *reloc, asection *input_section, struct bfd_link_info *info, reloc_howto_type *howto, - const Elf_Internal_Rela *reloc, - bfd_vma addr, - const char *name, bfd_byte *contents) { riscv_pcrel_lo_reloc *entry; entry = (riscv_pcrel_lo_reloc *) bfd_malloc (sizeof (riscv_pcrel_lo_reloc)); if (entry == NULL) return false; - *entry = (riscv_pcrel_lo_reloc) {input_section, info, howto, reloc, addr, - name, contents, p->lo_relocs}; + *entry = (riscv_pcrel_lo_reloc) {addr, reloc, input_section, info, + howto, contents, p->lo_relocs}; p->lo_relocs = entry; return true; } @@ -1880,16 +1917,34 @@ riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p) { bfd *input_bfd = r->input_section->owner; - riscv_pcrel_hi_reloc search = {r->addr, 0}; + riscv_pcrel_hi_reloc search = {r->address, 0, 0}; riscv_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search); - if (entry == NULL - /* Check for overflow into bit 11 when adding reloc addend. */ - || (!(entry->value & 0x800) - && ((entry->value + r->reloc->r_addend) & 0x800))) + /* There may be a risk if the %pcrel_lo with addend refers to + an IFUNC symbol. The %pcrel_hi has been relocated to plt, + so the corresponding %pcrel_lo with addend looks wrong. */ + char *string = NULL; + if (entry == NULL) + string = _("%pcrel_lo missing matching %pcrel_hi"); + else if (entry->type == R_RISCV_GOT_HI20 + && r->reloc->r_addend != 0) + string = _("%pcrel_lo with addend isn't allowed for R_RISCV_GOT_HI20"); + else if (RISCV_CONST_HIGH_PART (entry->value) + != RISCV_CONST_HIGH_PART (entry->value + r->reloc->r_addend)) + { + /* Check the overflow when adding reloc addend. */ + if (asprintf (&string, + _("%%pcrel_lo overflow with an addend, the " + "value of %%pcrel_hi is 0x%" PRIx64 " without " + "any addend, but may be 0x%" PRIx64 " after " + "adding the %%pcrel_lo addend"), + (int64_t) RISCV_CONST_HIGH_PART (entry->value), + (int64_t) RISCV_CONST_HIGH_PART + (entry->value + r->reloc->r_addend)) == -1) + string = _("%pcrel_lo overflow with an addend"); + } + + if (string != NULL) { - char *string = (entry == NULL - ? "%pcrel_lo missing matching %pcrel_hi" - : "%pcrel_lo overflow with an addend"); (*r->info->callbacks->reloc_dangerous) (r->info, string, input_bfd, r->input_section, r->reloc->r_offset); return true; @@ -2211,12 +2266,9 @@ riscv_elf_relocate_section (bfd *output_bfd, relocation = base_got->output_section->vma + base_got->output_offset + off; - r_type = ELFNN_R_TYPE (rel->r_info); - howto = riscv_elf_rtype_to_howto (input_bfd, r_type); - if (howto == NULL) - r = bfd_reloc_notsupported; - else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, - relocation, false)) + if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, + relocation, r_type, + false)) r = bfd_reloc_overflow; goto do_relocation; @@ -2228,12 +2280,9 @@ riscv_elf_relocate_section (bfd *output_bfd, goto do_relocation; case R_RISCV_PCREL_HI20: - r_type = ELFNN_R_TYPE (rel->r_info); - howto = riscv_elf_rtype_to_howto (input_bfd, r_type); - if (howto == NULL) - r = bfd_reloc_notsupported; - else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, - relocation, false)) + if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, + relocation, r_type, + false)) r = bfd_reloc_overflow; goto do_relocation; @@ -2370,21 +2419,29 @@ riscv_elf_relocate_section (bfd *output_bfd, local_got_offsets[r_symndx] |= 1; } } - relocation = sec_addr (htab->elf.sgot) + off; - absolute = riscv_zero_pcrel_hi_reloc (rel, - info, - pc, - relocation, - contents, - howto, - input_bfd); - r_type = ELFNN_R_TYPE (rel->r_info); - howto = riscv_elf_rtype_to_howto (input_bfd, r_type); - if (howto == NULL) - r = bfd_reloc_notsupported; - else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, - relocation, absolute)) - r = bfd_reloc_overflow; + + if (rel->r_addend != 0) + { + msg = _("The addend isn't allowed for R_RISCV_GOT_HI20"); + r = bfd_reloc_dangerous; + } + else + { + /* Address of got entry. */ + relocation = sec_addr (htab->elf.sgot) + off; + absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc, + relocation, contents, + howto); + /* Update howto if relocation is changed. */ + howto = riscv_elf_rtype_to_howto (input_bfd, + ELFNN_R_TYPE (rel->r_info)); + if (howto == NULL) + r = bfd_reloc_notsupported; + else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, + relocation, r_type, + absolute)) + r = bfd_reloc_overflow; + } break; case R_RISCV_ADD8: @@ -2485,20 +2542,16 @@ riscv_elf_relocate_section (bfd *output_bfd, } case R_RISCV_PCREL_HI20: - absolute = riscv_zero_pcrel_hi_reloc (rel, - info, - pc, - relocation, - contents, - howto, - input_bfd); - r_type = ELFNN_R_TYPE (rel->r_info); - howto = riscv_elf_rtype_to_howto (input_bfd, r_type); + absolute = riscv_zero_pcrel_hi_reloc (rel, info, pc, relocation, + contents, howto); + /* Update howto if relocation is changed. */ + howto = riscv_elf_rtype_to_howto (input_bfd, + ELFNN_R_TYPE (rel->r_info)); if (howto == NULL) r = bfd_reloc_notsupported; else if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, relocation + rel->r_addend, - absolute)) + r_type, absolute)) r = bfd_reloc_overflow; break; @@ -2518,8 +2571,8 @@ riscv_elf_relocate_section (bfd *output_bfd, break; } - if (riscv_record_pcrel_lo_reloc (&pcrel_relocs, input_section, info, - howto, rel, relocation, name, + if (riscv_record_pcrel_lo_reloc (&pcrel_relocs, relocation, rel, + input_section, info, howto, contents)) continue; r = bfd_reloc_overflow; @@ -2712,7 +2765,8 @@ riscv_elf_relocate_section (bfd *output_bfd, BFD_ASSERT (off < (bfd_vma) -2); relocation = sec_addr (htab->elf.sgot) + off + (is_ie ? ie_off : 0); if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, - relocation, false)) + relocation, r_type, + false)) r = bfd_reloc_overflow; unresolved_reloc = false; break; @@ -3317,52 +3371,27 @@ riscv_std_ext_p (const char *name) return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's'); } -/* Check if the versions are compatible. */ +/* Update the output subset's version to match the input when the input + subset's version is newer. */ -static bool -riscv_version_mismatch (bfd *ibfd, - struct riscv_subset_t *in, - struct riscv_subset_t *out) +static void +riscv_update_subset_version (struct riscv_subset_t *in, + struct riscv_subset_t *out) { if (in == NULL || out == NULL) - return true; - - /* Since there are no version conflicts for now, we just report - warning when the versions are mis-matched. */ - if (in->major_version != out->major_version - || in->minor_version != out->minor_version) + 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)) { - if ((in->major_version == RISCV_UNKNOWN_VERSION - && in->minor_version == RISCV_UNKNOWN_VERSION) - || (out->major_version == RISCV_UNKNOWN_VERSION - && out->minor_version == RISCV_UNKNOWN_VERSION)) - { - /* Do not report the warning when the version of input - or output is RISCV_UNKNOWN_VERSION, since the extension - is added implicitly. */ - } - else - _bfd_error_handler - (_("warning: %pB: mis-matched ISA version %d.%d for '%s' " - "extension, the output version is %d.%d"), - ibfd, - in->major_version, - in->minor_version, - in->name, - out->major_version, - out->minor_version); - - /* Update the output ISA versions to the newest ones. */ - if ((in->major_version > out->major_version) - || (in->major_version == out->major_version - && in->minor_version > out->minor_version)) - { - out->major_version = in->major_version; - out->minor_version = in->minor_version; - } + out->major_version = in->major_version; + out->minor_version = in->minor_version; } - - return true; } /* Return true if subset is 'i' or 'e'. */ @@ -3403,7 +3432,7 @@ riscv_merge_std_ext (bfd *ibfd, struct riscv_subset_t **pin, struct riscv_subset_t **pout) { - const char *standard_exts = riscv_supported_std_ext (); + const char *standard_exts = "mafdqlcbjtpvn"; const char *p; struct riscv_subset_t *in = *pin; struct riscv_subset_t *out = *pout; @@ -3423,11 +3452,10 @@ riscv_merge_std_ext (bfd *ibfd, ibfd, in->name, out->name); return false; } - else if (!riscv_version_mismatch (ibfd, in, out)) - return false; - else - riscv_add_subset (&merged_subsets, - out->name, out->major_version, out->minor_version); + + 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; @@ -3445,10 +3473,8 @@ riscv_merge_std_ext (bfd *ibfd, if (!find_in && !find_out) continue; - if (find_in - && find_out - && !riscv_version_mismatch (ibfd, ext_in, ext_out)) - return false; + 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, @@ -3470,8 +3496,7 @@ riscv_merge_std_ext (bfd *ibfd, on success and FALSE when a conflict is found. */ static bool -riscv_merge_multi_letter_ext (bfd *ibfd, - riscv_subset_t **pin, +riscv_merge_multi_letter_ext (riscv_subset_t **pin, riscv_subset_t **pout) { riscv_subset_t *in = *pin; @@ -3501,8 +3526,7 @@ riscv_merge_multi_letter_ext (bfd *ibfd, else { /* Both present, check version and increment both. */ - if (!riscv_version_mismatch (ibfd, in, out)) - return false; + riscv_update_subset_version (in, out); riscv_add_subset (&merged_subsets, out->name, out->major_version, out->minor_version); @@ -3539,35 +3563,22 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch) merged_subsets.head = NULL; merged_subsets.tail = NULL; - riscv_parse_subset_t rpe_in; - riscv_parse_subset_t rpe_out; - - /* Only assembler needs to check the default version of ISA, so just set - the rpe_in.get_default_version and rpe_out.get_default_version to NULL. */ - rpe_in.subset_list = &in_subsets; - rpe_in.error_handler = _bfd_error_handler; - rpe_in.xlen = &xlen_in; - rpe_in.get_default_version = NULL; - - rpe_out.subset_list = &out_subsets; - rpe_out.error_handler = _bfd_error_handler; - rpe_out.xlen = &xlen_out; - rpe_out.get_default_version = 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 (&rpe_in, in_arch)) + if (!riscv_parse_subset (&riscv_rps_ld_in, in_arch)) return NULL; - - if (!riscv_parse_subset (&rpe_out, out_arch)) + if (!riscv_parse_subset (&riscv_rps_ld_out, out_arch)) return NULL; /* Checking XLEN. */ @@ -3588,7 +3599,7 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch) return NULL; /* Merge all non-single letter extensions with single call. */ - if (!riscv_merge_multi_letter_ext (ibfd, &in, &out)) + if (!riscv_merge_multi_letter_ext (&in, &out)) return NULL; if (xlen_in != xlen_out) @@ -3882,115 +3893,6 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) return false; } -/* Delete some bytes from a section while relaxing. */ - -static bool -riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count, - struct bfd_link_info *link_info) -{ - unsigned int i, symcount; - bfd_vma toaddr = sec->size; - struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd); - Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; - unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); - struct bfd_elf_section_data *data = elf_section_data (sec); - bfd_byte *contents = data->this_hdr.contents; - - /* Actually delete the bytes. */ - sec->size -= count; - memmove (contents + addr, contents + addr + count, toaddr - addr - count); - - /* Adjust the location of all of the relocs. Note that we need not - adjust the addends, since all PC-relative references must be against - symbols, which we will adjust below. */ - for (i = 0; i < sec->reloc_count; i++) - if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr) - data->relocs[i].r_offset -= count; - - /* Adjust the local symbols defined in this section. */ - for (i = 0; i < symtab_hdr->sh_info; i++) - { - Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i; - if (sym->st_shndx == sec_shndx) - { - /* If the symbol is in the range of memory we just moved, we - have to adjust its value. */ - if (sym->st_value > addr && sym->st_value <= toaddr) - sym->st_value -= count; - - /* If the symbol *spans* the bytes we just deleted (i.e. its - *end* is in the moved bytes but its *start* isn't), then we - must adjust its size. - - This test needs to use the original value of st_value, otherwise - we might accidentally decrease size when deleting bytes right - before the symbol. But since deleted relocs can't span across - symbols, we can't have both a st_value and a st_size decrease, - so it is simpler to just use an else. */ - else if (sym->st_value <= addr - && sym->st_value + sym->st_size > addr - && sym->st_value + sym->st_size <= toaddr) - sym->st_size -= count; - } - } - - /* Now adjust the global symbols defined in this section. */ - symcount = ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym)) - - symtab_hdr->sh_info); - - for (i = 0; i < symcount; i++) - { - struct elf_link_hash_entry *sym_hash = sym_hashes[i]; - - /* The '--wrap SYMBOL' option is causing a pain when the object file, - containing the definition of __wrap_SYMBOL, includes a direct - call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference - the same symbol (which is __wrap_SYMBOL), but still exist as two - different symbols in 'sym_hashes', we don't want to adjust - the global symbol __wrap_SYMBOL twice. - - The same problem occurs with symbols that are versioned_hidden, as - foo becomes an alias for foo@BAR, and hence they need the same - treatment. */ - if (link_info->wrap_hash != NULL - || sym_hash->versioned == versioned_hidden) - { - struct elf_link_hash_entry **cur_sym_hashes; - - /* Loop only over the symbols which have already been checked. */ - for (cur_sym_hashes = sym_hashes; cur_sym_hashes < &sym_hashes[i]; - cur_sym_hashes++) - { - /* If the current symbol is identical to 'sym_hash', that means - the symbol was already adjusted (or at least checked). */ - if (*cur_sym_hashes == sym_hash) - break; - } - /* Don't adjust the symbol again. */ - if (cur_sym_hashes < &sym_hashes[i]) - continue; - } - - if ((sym_hash->root.type == bfd_link_hash_defined - || sym_hash->root.type == bfd_link_hash_defweak) - && sym_hash->root.u.def.section == sec) - { - /* As above, adjust the value if needed. */ - if (sym_hash->root.u.def.value > addr - && sym_hash->root.u.def.value <= toaddr) - sym_hash->root.u.def.value -= count; - - /* As above, adjust the size if needed. */ - else if (sym_hash->root.u.def.value <= addr - && sym_hash->root.u.def.value + sym_hash->size > addr - && sym_hash->root.u.def.value + sym_hash->size <= toaddr) - sym_hash->size -= count; - } - } - - return true; -} - /* A second format for recording PC-relative hi relocations. This stores the information required to relax them to GP-relative addresses. */ @@ -4121,6 +4023,155 @@ riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off) return false; } +static void +riscv_update_pcgp_relocs (riscv_pcgp_relocs *p, asection *deleted_sec, + bfd_vma deleted_addr, size_t deleted_count) +{ + /* Bytes have already been deleted and toaddr should match the old section + size for our checks, so adjust it here. */ + bfd_vma toaddr = deleted_sec->size + deleted_count; + riscv_pcgp_lo_reloc *l; + riscv_pcgp_hi_reloc *h; + + /* Update section offsets of corresponding pcrel_hi relocs for the pcrel_lo + entries where they occur after the deleted bytes. */ + for (l = p->lo; l != NULL; l = l->next) + if (l->hi_sec_off > deleted_addr + && l->hi_sec_off < toaddr) + l->hi_sec_off -= deleted_count; + + /* Update both section offsets, and symbol values of pcrel_hi relocs where + these values occur after the deleted bytes. */ + for (h = p->hi; h != NULL; h = h->next) + { + if (h->hi_sec_off > deleted_addr + && h->hi_sec_off < toaddr) + h->hi_sec_off -= deleted_count; + if (h->sym_sec == deleted_sec + && h->hi_addr > deleted_addr + && h->hi_addr < toaddr) + h->hi_addr -= deleted_count; + } +} + +/* Delete some bytes from a section while relaxing. */ + +static bool +riscv_relax_delete_bytes (bfd *abfd, + asection *sec, + bfd_vma addr, + size_t count, + struct bfd_link_info *link_info, + riscv_pcgp_relocs *p) +{ + unsigned int i, symcount; + bfd_vma toaddr = sec->size; + struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd); + Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + struct bfd_elf_section_data *data = elf_section_data (sec); + bfd_byte *contents = data->this_hdr.contents; + + /* Actually delete the bytes. */ + sec->size -= count; + memmove (contents + addr, contents + addr + count, toaddr - addr - count); + + /* Adjust the location of all of the relocs. Note that we need not + adjust the addends, since all PC-relative references must be against + symbols, which we will adjust below. */ + for (i = 0; i < sec->reloc_count; i++) + if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr) + data->relocs[i].r_offset -= count; + + /* Adjust the hi_sec_off, and the hi_addr of any entries in the pcgp relocs + table for which these values occur after the deleted bytes. */ + if (p) + riscv_update_pcgp_relocs (p, sec, addr, count); + + /* Adjust the local symbols defined in this section. */ + for (i = 0; i < symtab_hdr->sh_info; i++) + { + Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i; + if (sym->st_shndx == sec_shndx) + { + /* If the symbol is in the range of memory we just moved, we + have to adjust its value. */ + if (sym->st_value > addr && sym->st_value <= toaddr) + sym->st_value -= count; + + /* If the symbol *spans* the bytes we just deleted (i.e. its + *end* is in the moved bytes but its *start* isn't), then we + must adjust its size. + + This test needs to use the original value of st_value, otherwise + we might accidentally decrease size when deleting bytes right + before the symbol. But since deleted relocs can't span across + symbols, we can't have both a st_value and a st_size decrease, + so it is simpler to just use an else. */ + else if (sym->st_value <= addr + && sym->st_value + sym->st_size > addr + && sym->st_value + sym->st_size <= toaddr) + sym->st_size -= count; + } + } + + /* Now adjust the global symbols defined in this section. */ + symcount = ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym)) + - symtab_hdr->sh_info); + + for (i = 0; i < symcount; i++) + { + struct elf_link_hash_entry *sym_hash = sym_hashes[i]; + + /* The '--wrap SYMBOL' option is causing a pain when the object file, + containing the definition of __wrap_SYMBOL, includes a direct + call to SYMBOL as well. Since both __wrap_SYMBOL and SYMBOL reference + the same symbol (which is __wrap_SYMBOL), but still exist as two + different symbols in 'sym_hashes', we don't want to adjust + the global symbol __wrap_SYMBOL twice. + + The same problem occurs with symbols that are versioned_hidden, as + foo becomes an alias for foo@BAR, and hence they need the same + treatment. */ + if (link_info->wrap_hash != NULL + || sym_hash->versioned != unversioned) + { + struct elf_link_hash_entry **cur_sym_hashes; + + /* Loop only over the symbols which have already been checked. */ + for (cur_sym_hashes = sym_hashes; cur_sym_hashes < &sym_hashes[i]; + cur_sym_hashes++) + { + /* If the current symbol is identical to 'sym_hash', that means + the symbol was already adjusted (or at least checked). */ + if (*cur_sym_hashes == sym_hash) + break; + } + /* Don't adjust the symbol again. */ + if (cur_sym_hashes < &sym_hashes[i]) + continue; + } + + if ((sym_hash->root.type == bfd_link_hash_defined + || sym_hash->root.type == bfd_link_hash_defweak) + && sym_hash->root.u.def.section == sec) + { + /* As above, adjust the value if needed. */ + if (sym_hash->root.u.def.value > addr + && sym_hash->root.u.def.value <= toaddr) + sym_hash->root.u.def.value -= count; + + /* As above, adjust the size if needed. */ + else if (sym_hash->root.u.def.value <= addr + && sym_hash->root.u.def.value + sym_hash->size > addr + && sym_hash->root.u.def.value + sym_hash->size <= toaddr) + sym_hash->size -= count; + } + } + + return true; +} + typedef bool (*relax_func_t) (bfd *, asection *, asection *, struct bfd_link_info *, Elf_Internal_Rela *, @@ -4138,7 +4189,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, bfd_vma max_alignment, bfd_vma reserve_size ATTRIBUTE_UNUSED, bool *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + riscv_pcgp_relocs *pcgp_relocs, bool undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; @@ -4202,7 +4253,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, /* Delete unnecessary JALR. */ *again = true; return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len, - link_info); + link_info, pcgp_relocs); } /* Traverse all output sections and return the max alignment. */ @@ -4234,7 +4285,7 @@ _bfd_riscv_relax_lui (bfd *abfd, bfd_vma max_alignment, bfd_vma reserve_size, bool *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + riscv_pcgp_relocs *pcgp_relocs, bool undefined_weak) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; @@ -4296,7 +4347,7 @@ _bfd_riscv_relax_lui (bfd *abfd, rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE); *again = true; return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, - link_info); + link_info, pcgp_relocs); default: abort (); @@ -4329,7 +4380,7 @@ _bfd_riscv_relax_lui (bfd *abfd, *again = true; return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2, - link_info); + link_info, pcgp_relocs); } return true; @@ -4347,7 +4398,7 @@ _bfd_riscv_relax_tls_le (bfd *abfd, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bool *again, - riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED, + riscv_pcgp_relocs *pcgp_relocs, bool undefined_weak ATTRIBUTE_UNUSED) { /* See if this symbol is in range of tp. */ @@ -4370,7 +4421,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd, /* We can delete the unnecessary instruction and reloc. */ rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE); *again = true; - return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info); + return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, link_info, + pcgp_relocs); default: abort (); @@ -4389,7 +4441,7 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bool *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED, + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, bool undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; @@ -4401,6 +4453,9 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec, bfd_vma aligned_addr = ((symval - 1) & ~(alignment - 1)) + alignment; bfd_vma nop_bytes = aligned_addr - symval; + /* Once we've handled an R_RISCV_ALIGN, we can't relax anything else. */ + sec->sec_flg0 = true; + /* Make sure there are enough NOPs to actually achieve the alignment. */ if (rel->r_addend < nop_bytes) { @@ -4430,7 +4485,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec, /* Delete the excess bytes. */ return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + nop_bytes, - rel->r_addend - nop_bytes, link_info); + rel->r_addend - nop_bytes, link_info, + NULL); } /* Relax PC-relative references to GP-relative references. */ @@ -4596,47 +4652,33 @@ _bfd_riscv_relax_delete (bfd *abfd, bfd_vma symval ATTRIBUTE_UNUSED, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, - bool *again, + bool *again ATTRIBUTE_UNUSED, riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, bool undefined_weak ATTRIBUTE_UNUSED) { if (!riscv_relax_delete_bytes (abfd, sec, rel->r_offset, rel->r_addend, - link_info)) + link_info, NULL)) return false; rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE); - *again = true; return true; } -/* Called by after_allocation to check if we need to run the whole - relaxations again. */ +/* Called by after_allocation to set the information of data segment + before relaxing. */ -bool -bfd_elfNN_riscv_restart_relax_sections (struct bfd_link_info *info) +void +bfd_elfNN_riscv_set_data_segment_info (struct bfd_link_info *info, + int *data_segment_phase) { struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info); - bool restart = htab->restart_relax; - /* Reset the flag. */ - htab->restart_relax = false; - return restart; + htab->data_segment_phase = data_segment_phase; } /* Relax a section. - Pass 0: Shortens code sequences for LUI/CALL/TPREL relocs. - Pass 1: Shortens code sequences for PCREL relocs. - Pass 2: Deletes the bytes that pass 1 made obsolete. - Pass 3: Which cannot be disabled, handles code alignment directives. - - The `again` is used to determine whether the relax pass itself needs to - run again. And the `restart_relax` is used to determine if we need to - run the whole relax passes again from 0 to 2. Once we have deleted the - code between relax pass 0 to 2, the restart_relax will be set to TRUE, - and we should run the whole relaxations again to give them more chances - to shorten the code. - - Since we can't relax anything else once we start to handle the alignments, - we will only enter into the relax pass 3 when the restart_relax is FALSE. */ + Pass 0: Shortens code sequences for LUI/CALL/TPREL/PCREL relocs. + Pass 1: Deletes the bytes that PCREL relaxation in pass 0 made obsolete. + Pass 2: Which cannot be disabled, handles code alignment directives. */ static bool _bfd_riscv_relax_section (bfd *abfd, asection *sec, @@ -4655,12 +4697,14 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, *again = false; if (bfd_link_relocatable (info) + || sec->sec_flg0 || (sec->flags & SEC_RELOC) == 0 || sec->reloc_count == 0 || (info->disable_target_specific_optimizations - && info->relax_pass < 2) - || (htab->restart_relax - && info->relax_pass == 3)) + && info->relax_pass == 0) + /* The exp_seg_relro_adjust is enum phase_enum (0x4), + and defined in ld/ldexp.h. */ + || *(htab->data_segment_phase) == 4) return true; riscv_init_pcgp_relocs (&pcgp_relocs); @@ -4710,24 +4754,14 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, || type == R_RISCV_TPREL_LO12_I || type == R_RISCV_TPREL_LO12_S) relax_func = _bfd_riscv_relax_tls_le; + else if (!bfd_link_pic (info) + && (type == R_RISCV_PCREL_HI20 + || type == R_RISCV_PCREL_LO12_I + || type == R_RISCV_PCREL_LO12_S)) + relax_func = _bfd_riscv_relax_pc; else continue; - } - else if (info->relax_pass == 1 - && !bfd_link_pic (info) - && (type == R_RISCV_PCREL_HI20 - || type == R_RISCV_PCREL_LO12_I - || type == R_RISCV_PCREL_LO12_S)) - relax_func = _bfd_riscv_relax_pc; - else if (info->relax_pass == 2 && type == R_RISCV_DELETE) - relax_func = _bfd_riscv_relax_delete; - else if (info->relax_pass == 3 && type == R_RISCV_ALIGN) - relax_func = _bfd_riscv_relax_align; - else - continue; - if (info->relax_pass < 2) - { /* Only relax this reloc if it is paired with R_RISCV_RELAX. */ if (i == sec->reloc_count - 1 || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX @@ -4737,6 +4771,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, /* Skip over the R_RISCV_RELAX. */ i++; } + else if (info->relax_pass == 1 && type == R_RISCV_DELETE) + relax_func = _bfd_riscv_relax_delete; + else if (info->relax_pass == 2 && type == R_RISCV_ALIGN) + relax_func = _bfd_riscv_relax_align; + else + continue; data->relocs = relocs; @@ -4899,9 +4939,6 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, free (relocs); riscv_free_pcgp_relocs (&pcgp_relocs, abfd, sec); - if (*again) - htab->restart_relax = true; - return ret; } @@ -5094,14 +5131,111 @@ riscv_elf_obj_attrs_arg_type (int tag) return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL; } -/* PR27584, Omit local and empty symbols since they usually generated - for pcrel relocations. */ +/* Do not choose mapping symbols as a function name. */ + +static bfd_size_type +riscv_maybe_function_sym (const asymbol *sym, + asection *sec, + bfd_vma *code_off) +{ + if (sym->flags & BSF_LOCAL + && riscv_elf_is_mapping_symbols (sym->name)) + return 0; + + return _bfd_elf_maybe_function_sym (sym, sec, code_off); +} + +/* Treat the following cases as target special symbols, they are + usually omitted. */ static bool riscv_elf_is_target_special_symbol (bfd *abfd, asymbol *sym) { + /* PR27584, local and empty symbols. Since they are usually + generated for pcrel relocations. */ return (!strcmp (sym->name, "") - || _bfd_elf_is_local_label_name (abfd, sym->name)); + || _bfd_elf_is_local_label_name (abfd, sym->name) + /* PR27916, mapping symbols. */ + || riscv_elf_is_mapping_symbols (sym->name)); +} + +static int +riscv_elf_additional_program_headers (bfd *abfd, + struct bfd_link_info *info ATTRIBUTE_UNUSED) +{ + int ret = 0; + + /* See if we need a PT_RISCV_ATTRIBUTES segment. */ + if (bfd_get_section_by_name (abfd, RISCV_ATTRIBUTES_SECTION_NAME)) + ++ret; + + return ret; +} + +static bool +riscv_elf_modify_segment_map (bfd *abfd, + struct bfd_link_info *info ATTRIBUTE_UNUSED) +{ + asection *s; + struct elf_segment_map *m, **pm; + size_t amt; + + /* If there is a .riscv.attributes section, we need a PT_RISCV_ATTRIBUTES + segment. */ + s = bfd_get_section_by_name (abfd, RISCV_ATTRIBUTES_SECTION_NAME); + if (s != NULL) + { + for (m = elf_seg_map (abfd); m != NULL; m = m->next) + if (m->p_type == PT_RISCV_ATTRIBUTES) + break; + /* If there is already a PT_RISCV_ATTRIBUTES header, avoid adding + another. */ + if (m == NULL) + { + amt = sizeof (*m); + m = bfd_zalloc (abfd, amt); + if (m == NULL) + return false; + + m->p_type = PT_RISCV_ATTRIBUTES; + m->count = 1; + m->sections[0] = s; + + /* We want to put it after the PHDR and INTERP segments. */ + pm = &elf_seg_map (abfd); + while (*pm != NULL + && ((*pm)->p_type == PT_PHDR + || (*pm)->p_type == PT_INTERP)) + pm = &(*pm)->next; + + m->next = *pm; + *pm = m; + } + } + + return true; +} + +/* Merge non-visibility st_other attributes. */ + +static void +riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h, + unsigned int st_other, + bool definition ATTRIBUTE_UNUSED, + bool dynamic ATTRIBUTE_UNUSED) +{ + unsigned int isym_sto = st_other & ~ELF_ST_VISIBILITY (-1); + unsigned int h_sto = h->other & ~ELF_ST_VISIBILITY (-1); + + if (isym_sto == h_sto) + return; + + if (isym_sto & ~STO_RISCV_VARIANT_CC) + _bfd_error_handler (_("unknown attribute for symbol `%s': 0x%02x"), + h->root.root.string, isym_sto); + + if (isym_sto & STO_RISCV_VARIANT_CC) + h->other |= STO_RISCV_VARIANT_CC; } #define TARGET_LITTLE_SYM riscv_elfNN_vec @@ -5132,10 +5266,15 @@ riscv_elf_is_target_special_symbol (bfd *abfd, asymbol *sym) #define elf_backend_grok_psinfo riscv_elf_grok_psinfo #define elf_backend_object_p riscv_elf_object_p #define elf_backend_write_core_note riscv_write_core_note +#define elf_backend_maybe_function_sym riscv_maybe_function_sym #define elf_info_to_howto_rel NULL #define elf_info_to_howto riscv_info_to_howto_rela #define bfd_elfNN_bfd_relax_section _bfd_riscv_relax_section #define bfd_elfNN_mkobject elfNN_riscv_mkobject +#define elf_backend_additional_program_headers \ + riscv_elf_additional_program_headers +#define elf_backend_modify_segment_map riscv_elf_modify_segment_map +#define elf_backend_merge_symbol_attribute riscv_elf_merge_symbol_attribute #define elf_backend_init_index_section _bfd_elf_init_1_index_section @@ -5157,6 +5296,6 @@ riscv_elf_is_target_special_symbol (bfd *abfd, asymbol *sym) #undef elf_backend_obj_attrs_section_type #define elf_backend_obj_attrs_section_type SHT_RISCV_ATTRIBUTES #undef elf_backend_obj_attrs_section -#define elf_backend_obj_attrs_section ".riscv.attributes" +#define elf_backend_obj_attrs_section RISCV_ATTRIBUTES_SECTION_NAME #include "elfNN-target.h"