X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felfnn-riscv.c;h=473bf50f2de75272215e1f189c5aa0286f78ea3c;hb=c69ad65744134f0c28432cc7c5204b9950f0b2c7;hp=1ab97b9b725b412a349aa528b0b696117a0848eb;hpb=96b0927de3ebdb302d8d571c43da3db5ec23847e;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index 1ab97b9b72..473bf50f2d 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -1,5 +1,5 @@ /* RISC-V-specific support for NN-bit ELF. - Copyright 2011-2016 Free Software Foundation, Inc. + Copyright (C) 2011-2020 Free Software Foundation, Inc. Contributed by Andrew Waterman (andrew@sifive.com). Based on TILE-Gx and MIPS targets. @@ -32,6 +32,9 @@ #include "elf/riscv.h" #include "opcode/riscv.h" +/* Internal relocations used exclusively by the relaxation pass. */ +#define R_RISCV_DELETE (R_RISCV_max + 1) + #define ARCH_SIZE NN #define MINUS_ONE ((bfd_vma)0 - 1) @@ -52,26 +55,6 @@ #define ELF_MAXPAGESIZE 0x1000 #define ELF_COMMONPAGESIZE 0x1000 -/* The RISC-V linker needs to keep track of the number of relocs that it - decides to copy as dynamic relocs in check_relocs for each symbol. - This is so that it can later discard them if they are found to be - unnecessary. We store the information in a field extending the - regular ELF linker hash table. */ - -struct riscv_elf_dyn_relocs -{ - struct riscv_elf_dyn_relocs *next; - - /* The input section of the reloc. */ - asection *sec; - - /* Total number of relocs copied for the input section. */ - bfd_size_type count; - - /* Number of pc-relative relocs copied for the input section. */ - bfd_size_type pc_count; -}; - /* RISC-V ELF linker hash entry. */ struct riscv_elf_link_hash_entry @@ -79,7 +62,7 @@ struct riscv_elf_link_hash_entry struct elf_link_hash_entry elf; /* Track dynamic relocs copied for this symbol. */ - struct riscv_elf_dyn_relocs *dyn_relocs; + struct elf_dyn_relocs *dyn_relocs; #define GOT_UNKNOWN 0 #define GOT_NORMAL 1 @@ -123,12 +106,13 @@ struct riscv_elf_link_hash_table struct elf_link_hash_table elf; /* Short-cuts to get to dynamic linker sections. */ - asection *sdynbss; - asection *srelbss; asection *sdyntdata; /* Small local sym to section mapping cache. */ struct sym_cache sym_cache; + + /* The max alignment of output sections. */ + bfd_vma max_alignment; }; @@ -137,12 +121,13 @@ struct riscv_elf_link_hash_table (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \ == RISCV_ELF_DATA ? ((struct riscv_elf_link_hash_table *) ((p)->hash)) : NULL) -static void -riscv_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED, +static bfd_boolean +riscv_info_to_howto_rela (bfd *abfd, arelent *cache_ptr, Elf_Internal_Rela *dst) { - cache_ptr->howto = riscv_elf_rtype_to_howto (ELFNN_R_TYPE (dst->r_info)); + cache_ptr->howto = riscv_elf_rtype_to_howto (abfd, ELFNN_R_TYPE (dst->r_info)); + return cache_ptr->howto != NULL; } static void @@ -184,20 +169,29 @@ riscv_elf_got_plt_val (bfd_vma plt_index, struct bfd_link_info *info) /* Generate a PLT header. */ -static void -riscv_make_plt_header (bfd_vma gotplt_addr, bfd_vma addr, uint32_t *entry) +static bfd_boolean +riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr, + uint32_t *entry) { bfd_vma gotplt_offset_high = RISCV_PCREL_HIGH_PART (gotplt_addr, addr); bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, addr); + /* RVE has no t3 register, so this won't work, and is not supported. */ + if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE) + { + _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"), + output_bfd); + return FALSE; + } + /* auipc t2, %hi(.got.plt) - sub t1, t1, t3 # shifted .got.plt offset + hdr size + 12 + sub t1, t1, t3 # shifted .got.plt offset + hdr size + 12 l[w|d] t3, %lo(.got.plt)(t2) # _dl_runtime_resolve addi t1, t1, -(hdr size + 12) # shifted .got.plt offset addi t0, t2, %lo(.got.plt) # &.got.plt srli t1, t1, log2(16/PTRSIZE) # .got.plt offset - l[w|d] t0, PTRSIZE(t0) # link map - jr t3 */ + l[w|d] t0, PTRSIZE(t0) # link map + jr t3 */ entry[0] = RISCV_UTYPE (AUIPC, X_T2, gotplt_offset_high); entry[1] = RISCV_RTYPE (SUB, X_T1, X_T1, X_T3); @@ -207,22 +201,35 @@ riscv_make_plt_header (bfd_vma gotplt_addr, bfd_vma addr, uint32_t *entry) entry[5] = RISCV_ITYPE (SRLI, X_T1, X_T1, 4 - RISCV_ELF_LOG_WORD_BYTES); entry[6] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES); entry[7] = RISCV_ITYPE (JALR, 0, X_T3, 0); + + return TRUE; } /* Generate a PLT entry. */ -static void -riscv_make_plt_entry (bfd_vma got, bfd_vma addr, uint32_t *entry) +static bfd_boolean +riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr, + uint32_t *entry) { + /* RVE has no t3 register, so this won't work, and is not supported. */ + if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE) + { + _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"), + output_bfd); + return FALSE; + } + /* auipc t3, %hi(.got.plt entry) l[w|d] t3, %lo(.got.plt entry)(t3) jalr t1, t3 nop */ entry[0] = RISCV_UTYPE (AUIPC, X_T3, RISCV_PCREL_HIGH_PART (got, addr)); - entry[1] = RISCV_ITYPE (LREG, X_T3, X_T3, RISCV_PCREL_LOW_PART(got, addr)); + entry[1] = RISCV_ITYPE (LREG, X_T3, X_T3, RISCV_PCREL_LOW_PART (got, addr)); entry[2] = RISCV_ITYPE (JALR, X_T1, X_T3, 0); entry[3] = RISCV_NOP; + + return TRUE; } /* Create an entry in an RISC-V ELF linker hash table. */ @@ -262,7 +269,7 @@ static struct bfd_link_hash_table * riscv_elf_link_hash_table_create (bfd *abfd) { struct riscv_elf_link_hash_table *ret; - bfd_size_type amt = sizeof (struct riscv_elf_link_hash_table); + size_t amt = sizeof (struct riscv_elf_link_hash_table); ret = (struct riscv_elf_link_hash_table *) bfd_zmalloc (amt); if (ret == NULL) @@ -276,6 +283,7 @@ riscv_elf_link_hash_table_create (bfd *abfd) return NULL; } + ret->max_alignment = (bfd_vma) -1; return &ret->elf.root; } @@ -302,13 +310,13 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) (bed->dynamic_sec_flags | SEC_READONLY)); if (s == NULL - || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->srelgot = s; s = s_got = bfd_make_section_anyway_with_flags (abfd, ".got", flags); if (s == NULL - || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->sgot = s; @@ -319,8 +327,7 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) { s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags); if (s == NULL - || !bfd_set_section_alignment (abfd, s, - bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->sgotplt = s; @@ -363,17 +370,30 @@ riscv_elf_create_dynamic_sections (bfd *dynobj, if (!_bfd_elf_create_dynamic_sections (dynobj, info)) return FALSE; - htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss"); if (!bfd_link_pic (info)) { - htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss"); + /* Technically, this section doesn't have contents. It is used as the + target of TLS copy relocs, to copy TLS data from shared libraries into + the executable. However, if we don't mark it as loadable, then it + matches the IS_TBSS test in ldlang.c, and there is no run-time address + space allocated for it even though it has SEC_ALLOC. That test is + correct for .tbss, but not correct for this section. There is also + a second problem that having a section with no contents can only work + if it comes after all sections with contents in the same segment, + but the linker script does not guarantee that. This is just mixed in + with other .tdata.* sections. We can fix both problems by lying and + saying that there are contents. This section is expected to be small + so this should not cause a significant extra program startup cost. */ htab->sdyntdata = bfd_make_section_anyway_with_flags (dynobj, ".tdata.dyn", - SEC_ALLOC | SEC_THREAD_LOCAL); + (SEC_ALLOC | SEC_THREAD_LOCAL + | SEC_LOAD | SEC_DATA + | SEC_HAS_CONTENTS + | SEC_LINKER_CREATED)); } - if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss - || (!bfd_link_pic (info) && (!htab->srelbss || !htab->sdyntdata))) + if (!htab->elf.splt || !htab->elf.srelplt || !htab->elf.sdynbss + || (!bfd_link_pic (info) && (!htab->elf.srelbss || !htab->sdyntdata))) abort (); return TRUE; @@ -395,14 +415,14 @@ riscv_elf_copy_indirect_symbol (struct bfd_link_info *info, { if (edir->dyn_relocs != NULL) { - struct riscv_elf_dyn_relocs **pp; - struct riscv_elf_dyn_relocs *p; + struct elf_dyn_relocs **pp; + struct elf_dyn_relocs *p; /* Add reloc counts against the indirect sym to the direct sym list. Merge any entries against the same section. */ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; ) { - struct riscv_elf_dyn_relocs *q; + struct elf_dyn_relocs *q; for (q = edir->dyn_relocs; q != NULL; q = q->next) if (q->sec == p->sec) @@ -441,7 +461,7 @@ riscv_elf_record_tls_type (bfd *abfd, struct elf_link_hash_entry *h, if ((*new_tls_type & GOT_NORMAL) && (*new_tls_type & ~GOT_NORMAL)) { (*_bfd_error_handler) - (_("%B: `%s' accessed both as normal and thread local symbol"), + (_("%pB: `%s' accessed both as normal and thread local symbol"), abfd, h ? h->root.root.string : ""); return FALSE; } @@ -484,11 +504,13 @@ riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info, static bfd_boolean bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h) { + reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type); + (*_bfd_error_handler) - (_("%B: relocation %s against `%s' can not be used when making a shared " + (_("%pB: relocation %s against `%s' can not be used when making a shared " "object; recompile with -fPIC"), - abfd, riscv_elf_rtype_to_howto (r_type)->name, - h != NULL ? h->root.root.string : "a local symbol"); + abfd, r ? r->name : _(""), + h != NULL ? h->root.root.string : "a local symbol"); bfd_set_error (bfd_error_bad_value); return FALSE; } @@ -519,7 +541,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, for (rel = relocs; rel < relocs + sec->reloc_count; rel++) { unsigned int r_type; - unsigned long r_symndx; + unsigned int r_symndx; struct elf_link_hash_entry *h; r_symndx = ELFNN_R_SYM (rel->r_info); @@ -527,7 +549,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) { - (*_bfd_error_handler) (_("%B: bad symbol index: %d"), + (*_bfd_error_handler) (_("%pB: bad symbol index: %d"), abfd, r_symndx); return FALSE; } @@ -540,10 +562,6 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; - - /* PR15323, ref flags aren't set for references in the same - object. */ - h->root.non_ir_ref = 1; } switch (r_type) @@ -645,9 +663,11 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, may need to keep relocations for symbols satisfied by a dynamic library if we manage to avoid copy relocs for the symbol. */ + reloc_howto_type * r = riscv_elf_rtype_to_howto (abfd, r_type); + if ((bfd_link_pic (info) && (sec->flags & SEC_ALLOC) != 0 - && (! riscv_elf_rtype_to_howto (r_type)->pc_relative + && ((r != NULL && ! r->pc_relative) || (h != NULL && (! info->symbolic || h->root.type == bfd_link_hash_defweak @@ -658,8 +678,8 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, && (h->root.type == bfd_link_hash_defweak || !h->def_regular))) { - struct riscv_elf_dyn_relocs *p; - struct riscv_elf_dyn_relocs **head; + struct elf_dyn_relocs *p; + struct elf_dyn_relocs **head; /* When creating a shared object, we must copy these relocs into the output file. We create a reloc @@ -698,14 +718,14 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, s = sec; vpp = &elf_section_data (s)->local_dynrel; - head = (struct riscv_elf_dyn_relocs **) vpp; + head = (struct elf_dyn_relocs **) vpp; } p = *head; if (p == NULL || p->sec != sec) { - bfd_size_type amt = sizeof *p; - p = ((struct riscv_elf_dyn_relocs *) + size_t amt = sizeof *p; + p = ((struct elf_dyn_relocs *) bfd_alloc (htab->elf.dynobj, amt)); if (p == NULL) return FALSE; @@ -717,7 +737,7 @@ riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } p->count += 1; - p->pc_count += riscv_elf_rtype_to_howto (r_type)->pc_relative; + p->pc_count += r == NULL ? 0 : r->pc_relative; } break; @@ -758,98 +778,21 @@ riscv_elf_gc_mark_hook (asection *sec, return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } -/* Update the got entry reference counts for the section being removed. */ +/* Find dynamic relocs for H that apply to read-only sections. */ -static bfd_boolean -riscv_elf_gc_sweep_hook (bfd *abfd, - struct bfd_link_info *info, - asection *sec, - const Elf_Internal_Rela *relocs) +static asection * +readonly_dynrelocs (struct elf_link_hash_entry *h) { - const Elf_Internal_Rela *rel, *relend; - Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd); - struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd); - bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd); - - if (bfd_link_relocatable (info)) - return TRUE; + struct elf_dyn_relocs *p; - elf_section_data (sec)->local_dynrel = NULL; - - for (rel = relocs, relend = relocs + sec->reloc_count; rel < relend; rel++) + for (p = riscv_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next) { - unsigned long r_symndx; - struct elf_link_hash_entry *h = NULL; - - r_symndx = ELFNN_R_SYM (rel->r_info); - if (r_symndx >= symtab_hdr->sh_info) - { - struct riscv_elf_link_hash_entry *eh; - struct riscv_elf_dyn_relocs **pp; - struct riscv_elf_dyn_relocs *p; - - h = sym_hashes[r_symndx - symtab_hdr->sh_info]; - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; - eh = (struct riscv_elf_link_hash_entry *) h; - for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next) - if (p->sec == sec) - { - /* Everything must go for SEC. */ - *pp = p->next; - break; - } - } - - switch (ELFNN_R_TYPE (rel->r_info)) - { - case R_RISCV_GOT_HI20: - case R_RISCV_TLS_GOT_HI20: - case R_RISCV_TLS_GD_HI20: - if (h != NULL) - { - if (h->got.refcount > 0) - h->got.refcount--; - } - else - { - if (local_got_refcounts && - local_got_refcounts[r_symndx] > 0) - local_got_refcounts[r_symndx]--; - } - break; - - case R_RISCV_HI20: - case R_RISCV_PCREL_HI20: - case R_RISCV_COPY: - case R_RISCV_JUMP_SLOT: - case R_RISCV_RELATIVE: - case R_RISCV_64: - case R_RISCV_32: - case R_RISCV_BRANCH: - case R_RISCV_CALL: - case R_RISCV_JAL: - case R_RISCV_RVC_BRANCH: - case R_RISCV_RVC_JUMP: - if (bfd_link_pic (info)) - break; - /* Fall through. */ - - case R_RISCV_CALL_PLT: - if (h != NULL) - { - if (h->plt.refcount > 0) - h->plt.refcount--; - } - break; + asection *s = p->sec->output_section; - default: - break; - } + if (s != NULL && (s->flags & SEC_READONLY) != 0) + return p->sec; } - - return TRUE; + return NULL; } /* Adjust a symbol defined by a dynamic object and referenced by a @@ -864,9 +807,8 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info, { struct riscv_elf_link_hash_table *htab; struct riscv_elf_link_hash_entry * eh; - struct riscv_elf_dyn_relocs *p; bfd *dynobj; - asection *s; + asection *s, *srel; htab = riscv_elf_hash_table (info); BFD_ASSERT (htab != NULL); @@ -877,7 +819,7 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info, BFD_ASSERT (dynobj != NULL && (h->needs_plt || h->type == STT_GNU_IFUNC - || h->u.weakdef != NULL + || h->is_weakalias || (h->def_dynamic && h->ref_regular && !h->def_regular))); @@ -908,12 +850,12 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info, /* If this is a weak symbol, and there is a real definition, the processor independent code will have arranged for us to see the real definition first, and we can just use the same value. */ - if (h->u.weakdef != NULL) + if (h->is_weakalias) { - BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined - || h->u.weakdef->root.type == bfd_link_hash_defweak); - h->root.u.def.section = h->u.weakdef->root.u.def.section; - h->root.u.def.value = h->u.weakdef->root.u.def.value; + struct elf_link_hash_entry *def = weakdef (h); + BFD_ASSERT (def->root.type == bfd_link_hash_defined); + h->root.u.def.section = def->root.u.def.section; + h->root.u.def.value = def->root.u.def.value; return TRUE; } @@ -939,17 +881,9 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info, return TRUE; } - eh = (struct riscv_elf_link_hash_entry *) h; - for (p = eh->dyn_relocs; p != NULL; p = p->next) - { - s = p->sec->output_section; - if (s != NULL && (s->flags & SEC_READONLY) != 0) - break; - } - - /* If we didn't find any dynamic relocs in read-only sections, then + /* If we don't find any dynamic relocs in read-only sections, then we'll be keeping the dynamic relocs and avoiding the copy reloc. */ - if (p == NULL) + if (!readonly_dynrelocs (h)) { h->non_got_ref = 0; return TRUE; @@ -969,16 +903,29 @@ riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info, to copy the initial value out of the dynamic object and into the runtime process image. We need to remember the offset into the .rel.bss section we are going to use. */ + eh = (struct riscv_elf_link_hash_entry *) h; + if (eh->tls_type & ~GOT_NORMAL) + { + s = htab->sdyntdata; + srel = htab->elf.srelbss; + } + else if ((h->root.u.def.section->flags & SEC_READONLY) != 0) + { + s = htab->elf.sdynrelro; + srel = htab->elf.sreldynrelro; + } + else + { + s = htab->elf.sdynbss; + srel = htab->elf.srelbss; + } if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0) { - htab->srelbss->size += sizeof (ElfNN_External_Rela); + srel->size += sizeof (ElfNN_External_Rela); h->needs_copy = 1; } - if (eh->tls_type & ~GOT_NORMAL) - return _bfd_elf_adjust_dynamic_copy (info, h, htab->sdyntdata); - - return _bfd_elf_adjust_dynamic_copy (info, h, htab->sdynbss); + return _bfd_elf_adjust_dynamic_copy (info, h, s); } /* Allocate space in .plt, .got and associated reloc sections for @@ -990,7 +937,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) struct bfd_link_info *info; struct riscv_elf_link_hash_table *htab; struct riscv_elf_link_hash_entry *eh; - struct riscv_elf_dyn_relocs *p; + struct elf_dyn_relocs *p; if (h->root.type == bfd_link_hash_indirect) return TRUE; @@ -1090,7 +1037,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) else { s->size += RISCV_ELF_WORD_BYTES; - if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)) + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h) + && ! UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) htab->elf.srelgot->size += sizeof (ElfNN_External_Rela); } } @@ -1111,7 +1059,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) { if (SYMBOL_CALLS_LOCAL (info, h)) { - struct riscv_elf_dyn_relocs **pp; + struct elf_dyn_relocs **pp; for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) { @@ -1129,7 +1077,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) if (eh->dyn_relocs != NULL && h->root.type == bfd_link_hash_undefweak) { - if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) + if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT + || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) eh->dyn_relocs = NULL; /* Make sure undefined weak symbols are output as a dynamic @@ -1185,24 +1134,29 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) return TRUE; } -/* Find any dynamic relocs that apply to read-only sections. */ +/* Set DF_TEXTREL if we find any dynamic relocs that apply to + read-only sections. */ static bfd_boolean -readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf) +maybe_set_textrel (struct elf_link_hash_entry *h, void *info_p) { - struct riscv_elf_link_hash_entry *eh; - struct riscv_elf_dyn_relocs *p; + asection *sec; - eh = (struct riscv_elf_link_hash_entry *) h; - for (p = eh->dyn_relocs; p != NULL; p = p->next) + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + sec = readonly_dynrelocs (h); + if (sec != NULL) { - asection *s = p->sec->output_section; + struct bfd_link_info *info = (struct bfd_link_info *) info_p; - if (s != NULL && (s->flags & SEC_READONLY) != 0) - { - ((struct bfd_link_info *) inf)->flags |= DF_TEXTREL; - return FALSE; - } + info->flags |= DF_TEXTREL; + info->callbacks->minfo + (_("%pB: dynamic relocation against `%pT' in read-only section `%pA'\n"), + sec->owner, h->root.root.string, sec); + + /* Not an error, just cut short the traversal. */ + return FALSE; } return TRUE; } @@ -1248,7 +1202,7 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) for (s = ibfd->sections; s != NULL; s = s->next) { - struct riscv_elf_dyn_relocs *p; + struct elf_dyn_relocs *p; for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) { @@ -1332,7 +1286,9 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) if (s == htab->elf.splt || s == htab->elf.sgot || s == htab->elf.sgotplt - || s == htab->sdynbss) + || s == htab->elf.sdynbss + || s == htab->elf.sdynrelro + || s == htab->sdyntdata) { /* Strip this section if we don't need it; see the comment below. */ @@ -1411,7 +1367,7 @@ riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) /* If any dynamic relocs apply to a read-only section, then we need a DT_TEXTREL entry. */ if ((info->flags & DF_TEXTREL) == 0) - elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, info); + elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info); if (info->flags & DF_TEXTREL) { @@ -1456,7 +1412,7 @@ riscv_global_pointer_value (struct bfd_link_info *info) { struct bfd_link_hash_entry *h; - h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE); + h = bfd_link_hash_lookup (info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE); if (h == NULL || h->type != bfd_link_hash_defined) return 0; @@ -1493,6 +1449,7 @@ perform_relocation (const reloc_howto_type *howto, case R_RISCV_LO12_I: case R_RISCV_GPREL_I: case R_RISCV_TPREL_LO12_I: + case R_RISCV_TPREL_I: case R_RISCV_PCREL_LO12_I: value = ENCODE_ITYPE_IMM (value); break; @@ -1500,6 +1457,7 @@ perform_relocation (const reloc_howto_type *howto, case R_RISCV_LO12_S: case R_RISCV_GPREL_S: case R_RISCV_TPREL_LO12_S: + case R_RISCV_TPREL_S: case R_RISCV_PCREL_LO12_S: value = ENCODE_STYPE_IMM (value); break; @@ -1537,9 +1495,21 @@ perform_relocation (const reloc_howto_type *howto, break; case R_RISCV_RVC_LUI: - if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value))) + if (RISCV_CONST_HIGH_PART (value) == 0) + { + /* Linker relaxation can convert an address equal to or greater than + 0x800 to slightly below 0x800. C.LUI does not accept zero as a + valid immediate. We can fix this by converting it to a C.LI. */ + bfd_vma insn = bfd_get (howto->bitsize, input_bfd, + contents + rel->r_offset); + insn = (insn & ~MATCH_C_LUI) | MATCH_C_LI; + bfd_put (howto->bitsize, input_bfd, insn, contents + rel->r_offset); + value = ENCODE_RVC_IMM (0); + } + else if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value))) return bfd_reloc_overflow; - value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)); + else + value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)); break; case R_RISCV_32: @@ -1548,14 +1518,23 @@ perform_relocation (const reloc_howto_type *howto, case R_RISCV_ADD16: case R_RISCV_ADD32: case R_RISCV_ADD64: + case R_RISCV_SUB6: case R_RISCV_SUB8: case R_RISCV_SUB16: case R_RISCV_SUB32: case R_RISCV_SUB64: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SET32: + case R_RISCV_32_PCREL: case R_RISCV_TLS_DTPREL32: case R_RISCV_TLS_DTPREL64: break; + case R_RISCV_DELETE: + return bfd_reloc_ok; + default: return bfd_reloc_notsupported; } @@ -1578,14 +1557,14 @@ typedef struct typedef struct riscv_pcrel_lo_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; - struct riscv_pcrel_lo_reloc * next; + 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; + struct riscv_pcrel_lo_reloc * next; } riscv_pcrel_lo_reloc; typedef struct @@ -1634,9 +1613,50 @@ riscv_free_pcrel_relocs (riscv_pcrel_relocs *p) } static bfd_boolean -riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr, bfd_vma value) +riscv_zero_pcrel_hi_reloc (Elf_Internal_Rela *rel, + struct bfd_link_info *info, + bfd_vma pc, + bfd_vma addr, + bfd_byte *contents, + const reloc_howto_type *howto, + bfd *input_bfd) +{ + /* 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 + * need to produce the address 0 when linked. As 0 is far from the arbitrary + * addresses that we can link PC-relative programs at, the linker can't + * actually relocate references to those symbols. In order to allow these + * programs to work we simply convert the PC-relative auipc sequences to + * 0-relative lui sequences. */ + if (bfd_link_pic (info)) + return FALSE; + + /* If it's possible to reference the symbol using auipc we do so, as that's + * more in the spirit of the PC-relative relocations we're processing. */ + bfd_vma offset = addr - pc; + if (ARCH_SIZE == 32 || VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (offset))) + return FALSE; + + /* If it's impossible to reference this with a LUI-based offset then don't + * bother to convert it at all so users still see the PC-relative relocation + * in the truncation message. */ + if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (addr))) + return FALSE; + + rel->r_info = ELFNN_R_INFO(addr, R_RISCV_HI20); + + bfd_vma insn = bfd_get(howto->bitsize, input_bfd, contents + rel->r_offset); + insn = (insn & ~MASK_AUIPC) | MATCH_LUI; + bfd_put(howto->bitsize, input_bfd, insn, contents + rel->r_offset); + return TRUE; +} + +static bfd_boolean +riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr, + bfd_vma value, bfd_boolean absolute) { - riscv_pcrel_hi_reloc entry = {addr, value - addr}; + bfd_vma offset = absolute ? value : value - addr; + riscv_pcrel_hi_reloc entry = {addr, offset}; riscv_pcrel_hi_reloc **slot = (riscv_pcrel_hi_reloc **) htab_find_slot (p->hi_relocs, &entry, INSERT); @@ -1679,13 +1699,18 @@ riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p) riscv_pcrel_hi_reloc search = {r->addr, 0}; riscv_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search); - if (entry == NULL) - { - ((*r->info->callbacks->reloc_overflow) - (r->info, NULL, r->name, r->howto->name, (bfd_vma) 0, - input_bfd, r->input_section, r->reloc->r_offset)); + if (entry == NULL + /* Check for overflow into bit 11 when adding reloc addend. */ + || (! (entry->value & 0x800) + && ((entry->value + r->reloc->r_addend) & 0x800))) + { + 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; - } + } perform_relocation (r->howto, r->reloc, entry->value, r->input_section, input_bfd, r->contents); @@ -1741,6 +1766,7 @@ riscv_elf_relocate_section (bfd *output_bfd, Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd); struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd); bfd_vma *local_got_offsets = elf_local_got_offsets (input_bfd); + bfd_boolean absolute; if (!riscv_init_pcrel_relocs (&pcrel_relocs)) return FALSE; @@ -1759,10 +1785,13 @@ riscv_elf_relocate_section (bfd *output_bfd, bfd_boolean unresolved_reloc, is_ie = FALSE; bfd_vma pc = sec_addr (input_section) + rel->r_offset; int r_type = ELFNN_R_TYPE (rel->r_info), tls_type; - reloc_howto_type *howto = riscv_elf_rtype_to_howto (r_type); + reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type); const char *msg = NULL; + char *msg_buf = NULL; + bfd_boolean resolved_to_zero; - if (r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY) + if (howto == NULL + || r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY) continue; /* This is a final link. */ @@ -1811,12 +1840,16 @@ riscv_elf_relocate_section (bfd *output_bfd, name = (bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name)); if (name == NULL || *name == '\0') - name = bfd_section_name (input_bfd, sec); + name = bfd_section_name (sec); } + resolved_to_zero = (h != NULL + && UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)); + switch (r_type) { case R_RISCV_NONE: + case R_RISCV_RELAX: case R_RISCV_TPREL_ADD: case R_RISCV_COPY: case R_RISCV_JUMP_SLOT: @@ -1830,6 +1863,12 @@ riscv_elf_relocate_section (bfd *output_bfd, case R_RISCV_RVC_LUI: case R_RISCV_LO12_I: case R_RISCV_LO12_S: + case R_RISCV_SET6: + case R_RISCV_SET8: + case R_RISCV_SET16: + case R_RISCV_SET32: + case R_RISCV_32_PCREL: + case R_RISCV_DELETE: /* These require no special handling beyond perform_relocation. */ break; @@ -1908,7 +1947,19 @@ riscv_elf_relocate_section (bfd *output_bfd, } } relocation = sec_addr (htab->elf.sgot) + off; - if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, relocation)) + 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; break; @@ -1923,6 +1974,7 @@ riscv_elf_relocate_section (bfd *output_bfd, } break; + case R_RISCV_SUB6: case R_RISCV_SUB8: case R_RISCV_SUB16: case R_RISCV_SUB32: @@ -1934,10 +1986,27 @@ riscv_elf_relocate_section (bfd *output_bfd, } break; - case R_RISCV_CALL_PLT: case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + /* Handle a call to an undefined weak function. This won't be + relaxed, so we have to handle it here. */ + if (h != NULL && h->root.type == bfd_link_hash_undefweak + && (!bfd_link_pic (info) || h->plt.offset == MINUS_ONE)) + { + /* We can use x0 as the base register. */ + bfd_vma insn = bfd_get_32 (input_bfd, + contents + rel->r_offset + 4); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (input_bfd, insn, contents + rel->r_offset + 4); + /* Set the relocation value so that we get 0 after the pc + relative adjustment. */ + relocation = sec_addr (input_section) + rel->r_offset; + } + /* Fall through. */ + case R_RISCV_JAL: case R_RISCV_RVC_JUMP: + /* This line has to match the check in _bfd_riscv_relax_section. */ if (bfd_link_pic (info) && h != NULL && h->plt.offset != MINUS_ONE) { /* Refer to the PLT entry. */ @@ -1952,6 +2021,11 @@ riscv_elf_relocate_section (bfd *output_bfd, case R_RISCV_TPREL_LO12_I: case R_RISCV_TPREL_LO12_S: + relocation = tpoff (info, relocation); + break; + + case R_RISCV_TPREL_I: + case R_RISCV_TPREL_S: relocation = tpoff (info, relocation); if (VALID_ITYPE_IMM (relocation + rel->r_addend)) { @@ -1961,6 +2035,8 @@ riscv_elf_relocate_section (bfd *output_bfd, insn |= X_TP << OP_SH_RS1; bfd_put_32 (input_bfd, insn, contents + rel->r_offset); } + else + r = bfd_reloc_overflow; break; case R_RISCV_GPREL_I: @@ -1986,13 +2062,39 @@ riscv_elf_relocate_section (bfd *output_bfd, } case R_RISCV_PCREL_HI20: - if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, - relocation + rel->r_addend)) + 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 + rel->r_addend, + absolute)) r = bfd_reloc_overflow; break; case R_RISCV_PCREL_LO12_I: case R_RISCV_PCREL_LO12_S: + /* We don't allow section symbols plus addends as the auipc address, + because then riscv_relax_delete_bytes would have to search through + all relocs to update these addends. This is also ambiguous, as + we do allow offsets to be added to the target address, which are + not to be used to find the auipc address. */ + if (((sym != NULL && (ELF_ST_TYPE (sym->st_info) == STT_SECTION)) + || (h != NULL && h->type == STT_SECTION)) + && rel->r_addend) + { + msg = _("%pcrel_lo section symbol with an addend"); + r = bfd_reloc_dangerous; + break; + } + if (riscv_record_pcrel_lo_reloc (&pcrel_relocs, input_section, info, howto, rel, relocation, name, contents)) @@ -2012,7 +2114,8 @@ riscv_elf_relocate_section (bfd *output_bfd, if ((bfd_link_pic (info) && (h == NULL - || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + && !resolved_to_zero) || h->root.type != bfd_link_hash_undefweak) && (! howto->pc_relative || !SYMBOL_CALLS_LOCAL (info, h))) @@ -2108,7 +2211,7 @@ riscv_elf_relocate_section (bfd *output_bfd, } /* The GOT entries have not been initialized yet. Do it - now, and emit any relocations. */ + now, and emit any relocations. */ if ((bfd_link_pic (info) || indx != 0) && (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT @@ -2183,7 +2286,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)) + if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, + relocation, FALSE)) r = bfd_reloc_overflow; unresolved_reloc = FALSE; break; @@ -2201,20 +2305,42 @@ riscv_elf_relocate_section (bfd *output_bfd, && _bfd_elf_section_offset (output_bfd, info, input_section, rel->r_offset) != (bfd_vma) -1) { - (*_bfd_error_handler) - (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"), - input_bfd, - input_section, - (long) rel->r_offset, - howto->name, - h->root.root.string); - continue; + switch (r_type) + { + case R_RISCV_CALL: + case R_RISCV_JAL: + case R_RISCV_RVC_JUMP: + if (asprintf (&msg_buf, + _("%%X%%P: relocation %s against `%s' can " + "not be used when making a shared object; " + "recompile with -fPIC\n"), + howto->name, + h->root.root.string) == -1) + msg_buf = NULL; + break; + + default: + if (asprintf (&msg_buf, + _("%%X%%P: unresolvable %s relocation against " + "symbol `%s'\n"), + howto->name, + h->root.root.string) == -1) + msg_buf = NULL; + break; + } + + msg = msg_buf; + r = bfd_reloc_notsupported; } if (r == bfd_reloc_ok) r = perform_relocation (howto, rel, relocation, input_section, input_bfd, contents); + /* We should have already detected the error and set message before. + If the error message isn't set since the linker runs out of memory + or we don't set it before, then we should set the default message + with the "internal error" string here. */ switch (r) { case bfd_reloc_ok: @@ -2233,30 +2359,44 @@ riscv_elf_relocate_section (bfd *output_bfd, break; case bfd_reloc_outofrange: - msg = _("internal error: out of range error"); + if (msg == NULL) + msg = _("%X%P: internal error: out of range error\n"); break; case bfd_reloc_notsupported: - msg = _("internal error: unsupported relocation error"); + if (msg == NULL) + msg = _("%X%P: internal error: unsupported relocation error\n"); break; case bfd_reloc_dangerous: - msg = _("internal error: dangerous relocation"); + /* The error message should already be set. */ + if (msg == NULL) + msg = _("dangerous relocation error"); + info->callbacks->reloc_dangerous + (info, msg, input_bfd, input_section, rel->r_offset); break; default: - msg = _("internal error: unknown error"); + msg = _("%X%P: internal error: unknown error\n"); break; } - if (msg) - info->callbacks->warning - (info, msg, name, input_bfd, input_section, rel->r_offset); + /* Do not report error message for the dangerous relocation again. */ + if (msg && r != bfd_reloc_dangerous) + info->callbacks->einfo (msg); + + /* Free the unused `msg_buf` if needed. */ + if (msg_buf) + free (msg_buf); + + /* We already reported the error via a callback, so don't try to report + it again by returning false. That leads to spurious errors. */ + ret = TRUE; goto out; } ret = riscv_resolve_pcrel_lo_relocs (&pcrel_relocs); -out: + out: riscv_free_pcrel_relocs (&pcrel_relocs); return ret; } @@ -2296,8 +2436,11 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, loc = htab->elf.splt->contents + h->plt.offset; /* Fill in the PLT entry itself. */ - riscv_make_plt_entry (got_address, header_address + h->plt.offset, - plt_entry); + if (! riscv_make_plt_entry (output_bfd, got_address, + header_address + h->plt.offset, + plt_entry)) + return FALSE; + for (i = 0; i < PLT_ENTRY_INSNS; i++) bfd_put_32 (output_bfd, plt_entry[i], loc + 4*i); @@ -2329,7 +2472,8 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, } if (h->got.offset != (bfd_vma) -1 - && !(riscv_elf_hash_entry(h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE))) + && !(riscv_elf_hash_entry (h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE)) + && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)) { asection *sgot; asection *srela; @@ -2343,15 +2487,15 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, rela.r_offset = sec_addr (sgot) + (h->got.offset &~ (bfd_vma) 1); - /* If this is a -Bsymbolic link, and the symbol is defined - locally, we just want to emit a RELATIVE reloc. Likewise if + /* If this is a local symbol reference, we just want to emit a RELATIVE + reloc. This can happen if it is a -Bsymbolic link, or a pie link, or the symbol was forced to be local because of a version file. The entry in the global offset table will already have been initialized in the relocate_section function. */ if (bfd_link_pic (info) - && (info->symbolic || h->dynindx == -1) - && h->def_regular) + && SYMBOL_REFERENCES_LOCAL (info, h)) { + BFD_ASSERT((h->got.offset & 1) != 0); asection *sec = h->root.u.def.section; rela.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE); rela.r_addend = (h->root.u.def.value @@ -2360,6 +2504,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, } else { + BFD_ASSERT((h->got.offset & 1) == 0); BFD_ASSERT (h->dynindx != -1); rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_NN); rela.r_addend = 0; @@ -2373,6 +2518,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, if (h->needs_copy) { Elf_Internal_Rela rela; + asection *s; /* This symbols needs a copy reloc. Set it up. */ BFD_ASSERT (h->dynindx != -1); @@ -2380,7 +2526,11 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, rela.r_offset = sec_addr (h->root.u.def.section) + h->root.u.def.value; rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_COPY); rela.r_addend = 0; - riscv_elf_append_rela (output_bfd, htab->srelbss, &rela); + if (h->root.u.def.section == htab->elf.sdynrelro) + s = htab->elf.sreldynrelro; + else + s = htab->elf.srelbss; + riscv_elf_append_rela (output_bfd, s, &rela); } /* Mark some specially defined symbols as absolute. */ @@ -2457,7 +2607,7 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd, ret = riscv_finish_dyn (output_bfd, info, dynobj, sdyn); - if (ret != TRUE) + if (!ret) return ret; /* Fill in the head and tail entries in the procedure linkage table. */ @@ -2465,15 +2615,18 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd, { int i; uint32_t plt_header[PLT_HEADER_INSNS]; - riscv_make_plt_header (sec_addr (htab->elf.sgotplt), - sec_addr (splt), plt_header); + ret = riscv_make_plt_header (output_bfd, + sec_addr (htab->elf.sgotplt), + sec_addr (splt), plt_header); + if (!ret) + return ret; for (i = 0; i < PLT_HEADER_INSNS; i++) bfd_put_32 (output_bfd, plt_header[i], splt->contents + 4*i); - } - elf_section_data (splt->output_section)->this_hdr.sh_entsize - = PLT_ENTRY_SIZE; + elf_section_data (splt->output_section)->this_hdr.sh_entsize + = PLT_ENTRY_SIZE; + } } if (htab->elf.sgotplt) @@ -2483,7 +2636,7 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd, if (bfd_is_abs_section (output_section)) { (*_bfd_error_handler) - (_("discarded output section: `%A'"), htab->elf.sgotplt); + (_("discarded output section: `%pA'"), htab->elf.sgotplt); return FALSE; } @@ -2545,63 +2698,566 @@ riscv_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED, } } -/* Merge backend specific data from an object file to the output - object file when linking. */ +/* 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 attribute. */ +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 bfd_boolean -_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) +riscv_std_ext_p (const char *name) { - bfd *obfd = info->output_bfd; - flagword new_flags = elf_elfheader (ibfd)->e_flags; - flagword old_flags = elf_elfheader (obfd)->e_flags; + return (strlen (name) == 1) && (name[0] != 'x') && (name[0] != 's'); +} - if (!is_riscv_elf (ibfd) || !is_riscv_elf (obfd)) - return TRUE; +/* Error handler when version mis-match. */ - if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0) +static void +riscv_version_mismatch (bfd *ibfd, + struct riscv_subset_t *in, + struct riscv_subset_t *out) +{ + _bfd_error_handler + (_("error: %pB: Mis-matched ISA version for '%s' extension. " + "%d.%d vs %d.%d"), + ibfd, in->name, + in->major_version, in->minor_version, + out->major_version, out->minor_version); +} + +/* Return true if subset is 'i' or 'e'. */ + +static bfd_boolean +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) - (_("%B: 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)); + _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; +} - if (!_bfd_elf_merge_object_attributes (ibfd, info)) +/* Merge standard extensions. + + Return Value: + Return FALSE if failed to merge. + + Arguments: + `bfd`: bfd handler. + `in_arch`: Raw arch string for input object. + `out_arch`: Raw arch string for output object. + `pin`: subset list for input object, and it'll skip all merged subset after + merge. + `pout`: Like `pin`, but for output object. */ + +static bfd_boolean +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 = riscv_supported_std_ext (); + 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 (! elf_flags_init (obfd)) + if (!riscv_i_or_e_p (ibfd, out_arch, out)) + return FALSE; + + if (in->name[0] != out->name[0]) { - elf_flags_init (obfd) = TRUE; - elf_elfheader (obfd)->e_flags = new_flags; - return TRUE; + /* 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; } + else if ((in->major_version != out->major_version) || + (in->minor_version != out->minor_version)) + { + /* TODO: Allow different merge policy. */ + riscv_version_mismatch (ibfd, in, out); + return FALSE; + } + else + riscv_add_subset (&merged_subsets, + in->name, in->major_version, in->minor_version); - /* Disallow linking soft-float and hard-float. */ - if ((old_flags ^ new_flags) & EF_RISCV_SOFT_FLOAT) + in = in->next; + out = out->next; + + /* Handle standard extension first. */ + for (p = standard_exts; *p; ++p) { - (*_bfd_error_handler) - (_("%B: can't link hard-float modules with soft-float modules"), ibfd); - goto fail; + char find_ext[2] = {*p, '\0'}; + struct riscv_subset_t *find_in = + riscv_lookup_subset (&in_subsets, find_ext); + struct riscv_subset_t *find_out = + riscv_lookup_subset (&out_subsets, find_ext); + + if (find_in == NULL && find_out == NULL) + continue; + + /* Check version is same or not. */ + /* TODO: Allow different merge policy. */ + if ((find_in != NULL && find_out != NULL) + && ((find_in->major_version != find_out->major_version) + || (find_in->minor_version != find_out->minor_version))) + { + riscv_version_mismatch (ibfd, in, out); + return FALSE; + } + + struct riscv_subset_t *merged = find_in ? find_in : find_out; + riscv_add_subset (&merged_subsets, merged->name, + merged->major_version, merged->minor_version); } - /* Allow linking RVC and non-RVC, and keep the RVC flag. */ - elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC; + /* 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; +} -fail: - bfd_set_error (bfd_error_bad_value); - return FALSE; +/* If C is a prefix class, then return the EXT string without the prefix. + Otherwise return the entire EXT string. */ + +static const char * +riscv_skip_prefix (const char *ext, riscv_isa_ext_class_t c) +{ + switch (c) + { + case RV_ISA_CLASS_X: return &ext[1]; + case RV_ISA_CLASS_S: return &ext[1]; + case RV_ISA_CLASS_Z: return &ext[1]; + default: return ext; + } } -/* Delete some bytes from a section while relaxing. */ +/* Compare prefixed extension names canonically. */ -static bfd_boolean -riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count) +static int +riscv_prefix_cmp (const char *a, const char *b) { - unsigned int i, symcount; - bfd_vma toaddr = sec->size; + riscv_isa_ext_class_t ca = riscv_get_prefix_class (a); + riscv_isa_ext_class_t cb = riscv_get_prefix_class (b); + + /* Extension name without prefix */ + const char *anp = riscv_skip_prefix (a, ca); + const char *bnp = riscv_skip_prefix (b, cb); + + if (ca == cb) + return strcasecmp (anp, bnp); + + return (int)ca - (int)cb; +} + +/* 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 bfd_boolean +riscv_merge_multi_letter_ext (bfd *ibfd, + 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_prefix_cmp (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. */ + if ((in->major_version != out->major_version) + || (in->minor_version != out->minor_version)) + { + riscv_version_mismatch (ibfd, in, out); + return FALSE; + } + + 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; + char *merged_arch_str; + + unsigned xlen_in, xlen_out; + merged_subsets.head = NULL; + merged_subsets.tail = NULL; + + riscv_parse_subset_t rpe_in; + riscv_parse_subset_t rpe_out; + + rpe_in.subset_list = &in_subsets; + rpe_in.error_handler = _bfd_error_handler; + rpe_in.xlen = &xlen_in; + + rpe_out.subset_list = &out_subsets; + rpe_out.error_handler = _bfd_error_handler; + rpe_out.xlen = &xlen_out; + + 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 arch string. */ + if (!riscv_parse_subset (&rpe_in, in_arch)) + return NULL; + + if (!riscv_parse_subset (&rpe_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 (ibfd, &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; + } + + merged_arch_str = riscv_arch_str (ARCH_SIZE, &merged_subsets); + + /* 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 bfd_boolean +riscv_merge_attributes (bfd *ibfd, struct bfd_link_info *info) +{ + bfd *obfd = info->output_bfd; + obj_attribute *in_attr; + obj_attribute *out_attr; + bfd_boolean result = TRUE; + 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 arch 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 (out_attr[i].i != in_attr[i].i) + { + _bfd_error_handler + (_("error: %pB: conflicting priv spec version " + "(major/minor/revision)."), ibfd); + result = FALSE; + } + 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 bfd_boolean +_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) || !is_riscv_elf (obfd)) + 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; + + 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; + } + + /* 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)) + { + bfd_boolean null_input_bfd = TRUE; + bfd_boolean only_data_sections = TRUE; + asection *sec; + + for (sec = ibfd->sections; sec != NULL; sec = sec->next) + { + if ((bfd_section_flags (sec) + & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) + == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) + only_data_sections = FALSE; + + null_input_bfd = FALSE; + break; + } + + if (null_input_bfd || only_data_sections) + 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; + + return TRUE; + + fail: + bfd_set_error (bfd_error_bad_value); + return FALSE; +} + +/* Delete some bytes from a section while relaxing. */ + +static bfd_boolean +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); @@ -2632,10 +3288,16 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t 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. */ - if (sym->st_value <= addr - && sym->st_value + sym->st_size > addr - && sym->st_value + sym->st_size <= toaddr) + 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; } } @@ -2648,6 +3310,34 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count) { 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) @@ -2658,9 +3348,9 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count) sym_hash->root.u.def.value -= count; /* As above, adjust the size if needed. */ - 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) + 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; } } @@ -2668,6 +3358,143 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count) return TRUE; } +/* A second format for recording PC-relative hi relocations. This stores the + information required to relax them to GP-relative addresses. */ + +typedef struct riscv_pcgp_hi_reloc riscv_pcgp_hi_reloc; +struct riscv_pcgp_hi_reloc +{ + bfd_vma hi_sec_off; + bfd_vma hi_addend; + bfd_vma hi_addr; + unsigned hi_sym; + asection *sym_sec; + bfd_boolean undefined_weak; + riscv_pcgp_hi_reloc *next; +}; + +typedef struct riscv_pcgp_lo_reloc riscv_pcgp_lo_reloc; +struct riscv_pcgp_lo_reloc +{ + bfd_vma hi_sec_off; + riscv_pcgp_lo_reloc *next; +}; + +typedef struct +{ + riscv_pcgp_hi_reloc *hi; + riscv_pcgp_lo_reloc *lo; +} riscv_pcgp_relocs; + +/* Initialize the pcgp reloc info in P. */ + +static bfd_boolean +riscv_init_pcgp_relocs (riscv_pcgp_relocs *p) +{ + p->hi = NULL; + p->lo = NULL; + return TRUE; +} + +/* Free the pcgp reloc info in P. */ + +static void +riscv_free_pcgp_relocs (riscv_pcgp_relocs *p, + bfd *abfd ATTRIBUTE_UNUSED, + asection *sec ATTRIBUTE_UNUSED) +{ + riscv_pcgp_hi_reloc *c; + riscv_pcgp_lo_reloc *l; + + for (c = p->hi; c != NULL;) + { + riscv_pcgp_hi_reloc *next = c->next; + free (c); + c = next; + } + + for (l = p->lo; l != NULL;) + { + riscv_pcgp_lo_reloc *next = l->next; + free (l); + l = next; + } +} + +/* Record pcgp hi part reloc info in P, using HI_SEC_OFF as the lookup index. + The HI_ADDEND, HI_ADDR, HI_SYM, and SYM_SEC args contain info required to + relax the corresponding lo part reloc. */ + +static bfd_boolean +riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off, + bfd_vma hi_addend, bfd_vma hi_addr, + unsigned hi_sym, asection *sym_sec, + bfd_boolean undefined_weak) +{ + riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new)); + if (!new) + return FALSE; + new->hi_sec_off = hi_sec_off; + new->hi_addend = hi_addend; + new->hi_addr = hi_addr; + new->hi_sym = hi_sym; + new->sym_sec = sym_sec; + new->undefined_weak = undefined_weak; + new->next = p->hi; + p->hi = new; + return TRUE; +} + +/* Look up hi part pcgp reloc info in P, using HI_SEC_OFF as the lookup index. + This is used by a lo part reloc to find the corresponding hi part reloc. */ + +static riscv_pcgp_hi_reloc * +riscv_find_pcgp_hi_reloc(riscv_pcgp_relocs *p, bfd_vma hi_sec_off) +{ + riscv_pcgp_hi_reloc *c; + + for (c = p->hi; c != NULL; c = c->next) + if (c->hi_sec_off == hi_sec_off) + return c; + return NULL; +} + +/* Record pcgp lo part reloc info in P, using HI_SEC_OFF as the lookup info. + This is used to record relocs that can't be relaxed. */ + +static bfd_boolean +riscv_record_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off) +{ + riscv_pcgp_lo_reloc *new = bfd_malloc (sizeof(*new)); + if (!new) + return FALSE; + new->hi_sec_off = hi_sec_off; + new->next = p->lo; + p->lo = new; + return TRUE; +} + +/* Look up lo part pcgp reloc info in P, using HI_SEC_OFF as the lookup index. + This is used by a hi part reloc to find the corresponding lo part reloc. */ + +static bfd_boolean +riscv_find_pcgp_lo_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off) +{ + riscv_pcgp_lo_reloc *c; + + for (c = p->lo; c != NULL; c = c->next) + if (c->hi_sec_off == hi_sec_off) + return TRUE; + return FALSE; +} + +typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *, + struct bfd_link_info *, + Elf_Internal_Rela *, + bfd_vma, bfd_vma, bfd_vma, bfd_boolean *, + riscv_pcgp_relocs *, + bfd_boolean undefined_weak); + /* Relax AUIPC + JALR into JAL. */ static bfd_boolean @@ -2675,8 +3502,11 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, struct bfd_link_info *link_info, Elf_Internal_Rela *rel, bfd_vma symval, - unsigned int max_alignment, - bfd_boolean *again) + bfd_vma max_alignment, + bfd_vma reserve_size ATTRIBUTE_UNUSED, + bfd_boolean *again, + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset); @@ -2685,9 +3515,16 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC; /* If the call crosses section boundaries, an alignment directive could - cause the PC-relative offset to later increase. */ - if (VALID_UJTYPE_IMM (foff) && sym_sec->output_section != sec->output_section) - foff += (foff < 0 ? -max_alignment : max_alignment); + cause the PC-relative offset to later increase, so we need to add in the + max alignment of any section inclusive from the call to the target. + Otherwise, we only need to use the alignment of the current section. */ + if (VALID_UJTYPE_IMM (foff)) + { + if (sym_sec->output_section == sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) + max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; + foff += (foff < 0 ? -max_alignment : max_alignment); + } /* See if this function call can be shortened. */ if (!VALID_UJTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero)) @@ -2699,9 +3536,12 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, auipc = bfd_get_32 (abfd, contents + rel->r_offset); jalr = bfd_get_32 (abfd, contents + rel->r_offset + 4); rd = (jalr >> OP_SH_RD) & OP_MASK_RD; - rvc = rvc && VALID_RVC_J_IMM (foff) && ARCH_SIZE == 32; + rvc = rvc && VALID_RVC_J_IMM (foff); + + /* C.J exists on RV32 and RV64, but C.JAL is RV32-only. */ + rvc = rvc && (rd == 0 || (rd == X_RA && ARCH_SIZE == 32)); - if (rvc && (rd == 0 || rd == X_RA)) + if (rvc) { /* Relax to C.J[AL] rd, addr. */ r_type = R_RISCV_RVC_JUMP; @@ -2728,12 +3568,13 @@ _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); + return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - len, + link_info); } /* Traverse all output sections and return the max alignment. */ -static unsigned int +static bfd_vma _bfd_riscv_get_max_alignment (asection *sec) { unsigned int max_alignment_power = 0; @@ -2745,7 +3586,7 @@ _bfd_riscv_get_max_alignment (asection *sec) max_alignment_power = o->alignment_power; } - return 1 << max_alignment_power; + return (bfd_vma) 1 << max_alignment_power; } /* Relax non-PIC global variable references. */ @@ -2757,41 +3598,72 @@ _bfd_riscv_relax_lui (bfd *abfd, struct bfd_link_info *link_info, Elf_Internal_Rela *rel, bfd_vma symval, - unsigned int max_alignment, - bfd_boolean *again) + bfd_vma max_alignment, + bfd_vma reserve_size, + bfd_boolean *again, + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma gp = riscv_global_pointer_value (link_info); int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC; - /* Mergeable symbols and code might later move out of range. */ - if (sym_sec->flags & (SEC_MERGE | SEC_CODE)) - return TRUE; - BFD_ASSERT (rel->r_offset + 4 <= sec->size); + if (gp) + { + /* If gp and the symbol are in the same output section, which is not the + abs section, then consider only that output section's alignment. */ + struct bfd_link_hash_entry *h = + bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, + TRUE); + if (h->u.def.section->output_section == sym_sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) + max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; + } + /* Is the reference in range of x0 or gp? Valid gp range conservatively because of alignment issue. */ - if (VALID_ITYPE_IMM (symval) - || (symval >= gp && VALID_ITYPE_IMM (symval - gp + max_alignment)) - || (symval < gp && VALID_ITYPE_IMM (symval - gp - max_alignment))) + if (undefined_weak + || (VALID_ITYPE_IMM (symval) + || (symval >= gp + && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) + || (symval < gp + && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))) { unsigned sym = ELFNN_R_SYM (rel->r_info); switch (ELFNN_R_TYPE (rel->r_info)) { case R_RISCV_LO12_I: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); + if (undefined_weak) + { + /* Change the RS1 to zero. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + } + else + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); return TRUE; case R_RISCV_LO12_S: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); + if (undefined_weak) + { + /* Change the RS1 to zero. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + } + else + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); return TRUE; case R_RISCV_HI20: /* We can delete the unnecessary LUI 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); + return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4, + link_info); default: abort (); @@ -2799,15 +3671,21 @@ _bfd_riscv_relax_lui (bfd *abfd, } /* Can we relax LUI to C.LUI? Alignment might move the section forward; - account for this assuming page alignment at worst. */ + account for this assuming page alignment at worst. In the presence of + RELRO segment the linker aligns it by one page size, therefore sections + after the segment can be moved more than one page. */ + if (use_rvc && ELFNN_R_TYPE (rel->r_info) == R_RISCV_HI20 && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval)) - && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval + ELF_MAXPAGESIZE))) + && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval) + + (link_info->relro ? 2 * ELF_MAXPAGESIZE + : ELF_MAXPAGESIZE))) { - /* Replace LUI with C.LUI if legal (i.e., rd != x2/sp). */ + /* Replace LUI with C.LUI if legal (i.e., rd != x0 and rd != x2/sp). */ bfd_vma lui = bfd_get_32 (abfd, contents + rel->r_offset); - if (((lui >> OP_SH_RD) & OP_MASK_RD) == X_SP) + unsigned rd = ((unsigned)lui >> OP_SH_RD) & OP_MASK_RD; + if (rd == 0 || rd == X_SP) return TRUE; lui = (lui & (OP_MASK_RD << OP_SH_RD)) | MATCH_C_LUI; @@ -2817,7 +3695,8 @@ _bfd_riscv_relax_lui (bfd *abfd, rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_RVC_LUI); *again = TRUE; - return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2); + return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + 2, 2, + link_info); } return TRUE; @@ -2832,32 +3711,52 @@ _bfd_riscv_relax_tls_le (bfd *abfd, struct bfd_link_info *link_info, Elf_Internal_Rela *rel, bfd_vma symval, - unsigned int max_alignment ATTRIBUTE_UNUSED, - bfd_boolean *again) + bfd_vma max_alignment ATTRIBUTE_UNUSED, + bfd_vma reserve_size ATTRIBUTE_UNUSED, + bfd_boolean *again, + riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { /* See if this symbol is in range of tp. */ if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0) return TRUE; - /* We can delete the unnecessary LUI and tp add. The LO12 reloc will be - made directly tp-relative. */ BFD_ASSERT (rel->r_offset + 4 <= sec->size); - rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE); + switch (ELFNN_R_TYPE (rel->r_info)) + { + case R_RISCV_TPREL_LO12_I: + rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TPREL_I); + return TRUE; - *again = TRUE; - return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4); + case R_RISCV_TPREL_LO12_S: + rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), R_RISCV_TPREL_S); + return TRUE; + + case R_RISCV_TPREL_HI20: + case R_RISCV_TPREL_ADD: + /* 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); + + default: + abort (); + } } /* Implement R_RISCV_ALIGN by deleting excess alignment NOPs. */ static bfd_boolean _bfd_riscv_relax_align (bfd *abfd, asection *sec, - asection *sym_sec ATTRIBUTE_UNUSED, - struct bfd_link_info *link_info ATTRIBUTE_UNUSED, + asection *sym_sec, + struct bfd_link_info *link_info, Elf_Internal_Rela *rel, bfd_vma symval, - unsigned int max_alignment ATTRIBUTE_UNUSED, - bfd_boolean *again ATTRIBUTE_UNUSED) + bfd_vma max_alignment ATTRIBUTE_UNUSED, + bfd_vma reserve_size ATTRIBUTE_UNUSED, + bfd_boolean *again ATTRIBUTE_UNUSED, + riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma alignment = 1, pos; @@ -2873,7 +3772,15 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec, /* Make sure there are enough NOPs to actually achieve the alignment. */ if (rel->r_addend < nop_bytes) - return FALSE; + { + _bfd_error_handler + (_("%pB(%pA+%#" PRIx64 "): %" PRId64 " bytes required for alignment " + "to %" PRId64 "-byte boundary, but only %" PRId64 " present"), + abfd, sym_sec, (uint64_t) rel->r_offset, + (int64_t) nop_bytes, (int64_t) alignment, (int64_t) rel->r_addend); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } /* Delete the reloc. */ rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE); @@ -2892,11 +3799,186 @@ _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); + rel->r_addend - nop_bytes, link_info); } -/* Relax a section. Pass 0 shortens code sequences unless disabled. - Pass 1, which cannot be disabled, handles code alignment directives. */ +/* Relax PC-relative references to GP-relative references. */ + +static bfd_boolean +_bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + asection *sym_sec, + struct bfd_link_info *link_info, + Elf_Internal_Rela *rel, + bfd_vma symval, + bfd_vma max_alignment, + bfd_vma reserve_size, + bfd_boolean *again ATTRIBUTE_UNUSED, + riscv_pcgp_relocs *pcgp_relocs, + bfd_boolean undefined_weak) +{ + bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; + bfd_vma gp = riscv_global_pointer_value (link_info); + + BFD_ASSERT (rel->r_offset + 4 <= sec->size); + + /* Chain the _LO relocs to their cooresponding _HI reloc to compute the + * actual target address. */ + riscv_pcgp_hi_reloc hi_reloc; + memset (&hi_reloc, 0, sizeof (hi_reloc)); + switch (ELFNN_R_TYPE (rel->r_info)) + { + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + { + /* If the %lo has an addend, it isn't for the label pointing at the + hi part instruction, but rather for the symbol pointed at by the + hi part instruction. So we must subtract it here for the lookup. + It is still used below in the final symbol address. */ + bfd_vma hi_sec_off = symval - sec_addr (sym_sec) - rel->r_addend; + riscv_pcgp_hi_reloc *hi = riscv_find_pcgp_hi_reloc (pcgp_relocs, + hi_sec_off); + if (hi == NULL) + { + riscv_record_pcgp_lo_reloc (pcgp_relocs, hi_sec_off); + return TRUE; + } + + hi_reloc = *hi; + symval = hi_reloc.hi_addr; + sym_sec = hi_reloc.sym_sec; + + /* We can not know whether the undefined weak symbol is referenced + according to the information of R_RISCV_PCREL_LO12_I/S. Therefore, + we have to record the 'undefined_weak' flag when handling the + corresponding R_RISCV_HI20 reloc in riscv_record_pcgp_hi_reloc. */ + undefined_weak = hi_reloc.undefined_weak; + } + break; + + case R_RISCV_PCREL_HI20: + /* Mergeable symbols and code might later move out of range. */ + if (! undefined_weak + && sym_sec->flags & (SEC_MERGE | SEC_CODE)) + return TRUE; + + /* If the cooresponding lo relocation has already been seen then it's not + * safe to relax this relocation. */ + if (riscv_find_pcgp_lo_reloc (pcgp_relocs, rel->r_offset)) + return TRUE; + + break; + + default: + abort (); + } + + if (gp) + { + /* If gp and the symbol are in the same output section, which is not the + abs section, then consider only that output section's alignment. */ + struct bfd_link_hash_entry *h = + bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, + TRUE); + if (h->u.def.section->output_section == sym_sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) + max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; + } + + /* Is the reference in range of x0 or gp? + Valid gp range conservatively because of alignment issue. */ + if (undefined_weak + || (VALID_ITYPE_IMM (symval) + || (symval >= gp + && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) + || (symval < gp + && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))) + { + unsigned sym = hi_reloc.hi_sym; + switch (ELFNN_R_TYPE (rel->r_info)) + { + case R_RISCV_PCREL_LO12_I: + if (undefined_weak) + { + /* Change the RS1 to zero, and then modify the relocation + type to R_RISCV_LO12_I. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_I); + rel->r_addend = hi_reloc.hi_addend; + } + else + { + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); + rel->r_addend += hi_reloc.hi_addend; + } + return TRUE; + + case R_RISCV_PCREL_LO12_S: + if (undefined_weak) + { + /* Change the RS1 to zero, and then modify the relocation + type to R_RISCV_LO12_S. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_S); + rel->r_addend = hi_reloc.hi_addend; + } + else + { + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); + rel->r_addend += hi_reloc.hi_addend; + } + return TRUE; + + case R_RISCV_PCREL_HI20: + riscv_record_pcgp_hi_reloc (pcgp_relocs, + rel->r_offset, + rel->r_addend, + symval, + ELFNN_R_SYM(rel->r_info), + sym_sec, + undefined_weak); + /* We can delete the unnecessary AUIPC and reloc. */ + rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE); + rel->r_addend = 4; + return TRUE; + + default: + abort (); + } + } + + return TRUE; +} + +/* Relax PC-relative references to GP-relative references. */ + +static bfd_boolean +_bfd_riscv_relax_delete (bfd *abfd, + asection *sec, + asection *sym_sec ATTRIBUTE_UNUSED, + struct bfd_link_info *link_info, + Elf_Internal_Rela *rel, + bfd_vma symval ATTRIBUTE_UNUSED, + bfd_vma max_alignment ATTRIBUTE_UNUSED, + bfd_vma reserve_size ATTRIBUTE_UNUSED, + bfd_boolean *again ATTRIBUTE_UNUSED, + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) +{ + if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend, + link_info)) + return FALSE; + rel->r_info = ELFNN_R_INFO(0, R_RISCV_NONE); + return TRUE; +} + +/* Relax a section. Pass 0 shortens code sequences unless disabled. Pass 1 + deletes the bytes that pass 0 made obselete. Pass 2, which cannot be + disabled, handles code alignment directives. */ static bfd_boolean _bfd_riscv_relax_section (bfd *abfd, asection *sec, @@ -2909,7 +3991,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, Elf_Internal_Rela *relocs; bfd_boolean ret = FALSE; unsigned int i; - unsigned int max_alignment; + bfd_vma max_alignment, reserve_size = 0; + riscv_pcgp_relocs pcgp_relocs; *again = FALSE; @@ -2921,6 +4004,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, && info->relax_pass == 0)) return TRUE; + riscv_init_pcgp_relocs (&pcgp_relocs); + /* Read this BFD's relocs if we haven't done so already. */ if (data->relocs) relocs = data->relocs; @@ -2928,17 +4013,30 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, info->keep_memory))) goto fail; - max_alignment = _bfd_riscv_get_max_alignment (sec); + if (htab) + { + max_alignment = htab->max_alignment; + if (max_alignment == (bfd_vma) -1) + { + max_alignment = _bfd_riscv_get_max_alignment (sec); + htab->max_alignment = max_alignment; + } + } + else + max_alignment = _bfd_riscv_get_max_alignment (sec); /* Examine and consider relaxing each reloc. */ for (i = 0; i < sec->reloc_count; i++) { asection *sym_sec; Elf_Internal_Rela *rel = relocs + i; - typeof (&_bfd_riscv_relax_call) relax_func = NULL; + relax_func_t relax_func; int type = ELFNN_R_TYPE (rel->r_info); bfd_vma symval; + char symtype; + bfd_boolean undefined_weak = FALSE; + relax_func = NULL; if (info->relax_pass == 0) { if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT) @@ -2947,13 +4045,33 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, || type == R_RISCV_LO12_I || type == R_RISCV_LO12_S) relax_func = _bfd_riscv_relax_lui; - else if (type == R_RISCV_TPREL_HI20 || type == R_RISCV_TPREL_ADD) + 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 if (type == R_RISCV_TPREL_HI20 + || type == R_RISCV_TPREL_ADD + || type == R_RISCV_TPREL_LO12_I + || type == R_RISCV_TPREL_LO12_S) relax_func = _bfd_riscv_relax_tls_le; + else + continue; + + /* 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 + || rel->r_offset != (rel + 1)->r_offset) + continue; + + /* Skip over the R_RISCV_RELAX. */ + i++; } - else if (type == R_RISCV_ALIGN) + 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; - - if (!relax_func) + else continue; data->relocs = relocs; @@ -2978,17 +4096,26 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, /* A local symbol. */ Elf_Internal_Sym *isym = ((Elf_Internal_Sym *) symtab_hdr->contents + ELFNN_R_SYM (rel->r_info)); + reserve_size = (isym->st_size - rel->r_addend) > isym->st_size + ? 0 : isym->st_size - rel->r_addend; if (isym->st_shndx == SHN_UNDEF) - sym_sec = sec, symval = sec_addr (sec) + rel->r_offset; + sym_sec = sec, symval = rel->r_offset; else { BFD_ASSERT (isym->st_shndx < elf_numsections (abfd)); sym_sec = elf_elfsections (abfd)[isym->st_shndx]->bfd_section; +#if 0 + /* The purpose of this code is unknown. It breaks linker scripts + for embedded development that place sections at address zero. + This code is believed to be unnecessary. Disabling it but not + yet removing it, in case something breaks. */ if (sec_addr (sym_sec) == 0) continue; - symval = sec_addr (sym_sec) + isym->st_value; +#endif + symval = isym->st_value; } + symtype = ELF_ST_TYPE (isym->st_info); } else { @@ -3002,36 +4129,107 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; - if (h->plt.offset != MINUS_ONE) - symval = sec_addr (htab->elf.splt) + h->plt.offset; - else if (h->root.u.def.section->output_section == NULL - || (h->root.type != bfd_link_hash_defined - && h->root.type != bfd_link_hash_defweak)) - continue; + if (h->root.type == bfd_link_hash_undefweak + && (relax_func == _bfd_riscv_relax_lui + || relax_func == _bfd_riscv_relax_pc)) + { + /* For the lui and auipc relaxations, since the symbol + value of an undefined weak symbol is always be zero, + we can optimize the patterns into a single LI/MV/ADDI + instruction. + + Note that, creating shared libraries and pie output may + break the rule above. Fortunately, since we do not relax + pc relocs when creating shared libraries and pie output, + and the absolute address access for R_RISCV_HI20 isn't + allowed when "-fPIC" is set, the problem of creating shared + libraries can not happen currently. Once we support the + auipc relaxations when creating shared libraries, then we will + need the more rigorous checking for this optimization. */ + undefined_weak = TRUE; + } + + /* This line has to match the check in riscv_elf_relocate_section + in the R_RISCV_CALL[_PLT] case. */ + if (bfd_link_pic (info) && h->plt.offset != MINUS_ONE) + { + sym_sec = htab->elf.splt; + symval = h->plt.offset; + } + else if (undefined_weak) + { + symval = 0; + sym_sec = bfd_und_section_ptr; + } + else if ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && h->root.u.def.section != NULL + && h->root.u.def.section->output_section != NULL) + { + symval = h->root.u.def.value; + sym_sec = h->root.u.def.section; + } else - symval = sec_addr (h->root.u.def.section) + h->root.u.def.value; + continue; - sym_sec = h->root.u.def.section; + if (h->type != STT_FUNC) + reserve_size = + (h->size - rel->r_addend) > h->size ? 0 : h->size - rel->r_addend; + symtype = h->type; } - symval += rel->r_addend; + if (sym_sec->sec_info_type == SEC_INFO_TYPE_MERGE + && (sym_sec->flags & SEC_MERGE)) + { + /* At this stage in linking, no SEC_MERGE symbol has been + adjusted, so all references to such symbols need to be + passed through _bfd_merged_section_offset. (Later, in + relocate_section, all SEC_MERGE symbols *except* for + section symbols have been adjusted.) + + gas may reduce relocations against symbols in SEC_MERGE + sections to a relocation against the section symbol when + the original addend was zero. When the reloc is against + a section symbol we should include the addend in the + offset passed to _bfd_merged_section_offset, since the + location of interest is the original symbol. On the + other hand, an access to "sym+addend" where "sym" is not + a section symbol should not include the addend; Such an + access is presumed to be an offset from "sym"; The + location of interest is just "sym". */ + if (symtype == STT_SECTION) + symval += rel->r_addend; + + symval = _bfd_merged_section_offset (abfd, &sym_sec, + elf_section_data (sym_sec)->sec_info, + symval); + + if (symtype != STT_SECTION) + symval += rel->r_addend; + } + else + symval += rel->r_addend; + + symval += sec_addr (sym_sec); if (!relax_func (abfd, sec, sym_sec, info, rel, symval, - max_alignment, again)) + max_alignment, reserve_size, again, + &pcgp_relocs, undefined_weak)) goto fail; } ret = TRUE; -fail: + fail: if (relocs != data->relocs) free (relocs); + riscv_free_pcgp_relocs(&pcgp_relocs, abfd, sec); return ret; } #if ARCH_SIZE == 32 -# define PRSTATUS_SIZE 0 /* FIXME */ +# define PRSTATUS_SIZE 204 # define PRSTATUS_OFFSET_PR_CURSIG 12 # define PRSTATUS_OFFSET_PR_PID 24 # define PRSTATUS_OFFSET_PR_REG 72 @@ -3116,6 +4314,27 @@ riscv_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) return TRUE; } +/* Set the right mach type. */ +static bfd_boolean +riscv_elf_object_p (bfd *abfd) +{ + /* There are only two mach types in RISCV currently. */ + if (strcmp (abfd->xvec->name, "elf32-littleriscv") == 0) + bfd_default_set_arch_mach (abfd, bfd_arch_riscv, bfd_mach_riscv32); + else + bfd_default_set_arch_mach (abfd, bfd_arch_riscv, bfd_mach_riscv64); + + return TRUE; +} + +/* Determine whether an object attribute tag takes an integer, a + string or both. */ + +static int +riscv_elf_obj_attrs_arg_type (int tag) +{ + return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL; +} #define TARGET_LITTLE_SYM riscv_elfNN_vec #define TARGET_LITTLE_NAME "elfNN-littleriscv" @@ -3137,10 +4356,10 @@ riscv_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) #define elf_backend_finish_dynamic_symbol riscv_elf_finish_dynamic_symbol #define elf_backend_finish_dynamic_sections riscv_elf_finish_dynamic_sections #define elf_backend_gc_mark_hook riscv_elf_gc_mark_hook -#define elf_backend_gc_sweep_hook riscv_elf_gc_sweep_hook #define elf_backend_plt_sym_val riscv_elf_plt_sym_val -#define elf_backend_grok_prstatus riscv_elf_grok_prstatus -#define elf_backend_grok_psinfo riscv_elf_grok_psinfo +#define elf_backend_grok_prstatus riscv_elf_grok_prstatus +#define elf_backend_grok_psinfo riscv_elf_grok_psinfo +#define elf_backend_object_p riscv_elf_object_p #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 @@ -3154,7 +4373,17 @@ riscv_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) #define elf_backend_plt_alignment 4 #define elf_backend_want_plt_sym 1 #define elf_backend_got_header_size (ARCH_SIZE / 8) +#define elf_backend_want_dynrelro 1 #define elf_backend_rela_normal 1 #define elf_backend_default_execstack 0 +#undef elf_backend_obj_attrs_vendor +#define elf_backend_obj_attrs_vendor "riscv" +#undef elf_backend_obj_attrs_arg_type +#define elf_backend_obj_attrs_arg_type riscv_elf_obj_attrs_arg_type +#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" + #include "elfNN-target.h"