diff options
Diffstat (limited to 'bfd/elfnn-riscv.c')
-rw-r--r-- | bfd/elfnn-riscv.c | 143 |
1 files changed, 116 insertions, 27 deletions
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index ccf904a..4ffe6a3 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -3319,6 +3319,7 @@ struct riscv_pcgp_hi_reloc bfd_vma hi_addr; unsigned hi_sym; asection *sym_sec; + bfd_boolean undefined_weak; riscv_pcgp_hi_reloc *next; }; @@ -3377,7 +3378,8 @@ riscv_free_pcgp_relocs (riscv_pcgp_relocs *p, 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) + unsigned hi_sym, asection *sym_sec, + bfd_boolean undefined_weak) { riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new)); if (!new) @@ -3387,6 +3389,7 @@ riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off, 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; @@ -3439,7 +3442,8 @@ 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 *); + riscv_pcgp_relocs *, + bfd_boolean undefined_weak); /* Relax AUIPC + JALR into JAL. */ @@ -3451,7 +3455,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, bfd_vma max_alignment, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + 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); @@ -3539,7 +3544,8 @@ _bfd_riscv_relax_lui (bfd *abfd, bfd_vma max_alignment, bfd_vma reserve_size, bfd_boolean *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + 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); @@ -3561,21 +3567,38 @@ _bfd_riscv_relax_lui (bfd *abfd, /* 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 + reserve_size)) - || (symval < gp - && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))) + 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: @@ -3634,7 +3657,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again, - riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED) + 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) @@ -3674,7 +3698,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcrel_relocs 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; @@ -3732,8 +3757,10 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, bfd_vma max_alignment, bfd_vma reserve_size, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcgp_relocs) + 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); @@ -3763,12 +3790,19 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, 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 (sym_sec->flags & (SEC_MERGE | SEC_CODE)) + 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 @@ -3796,23 +3830,50 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, /* 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 + reserve_size)) - || (symval < gp - && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))) + 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: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); - rel->r_addend += hi_reloc.hi_addend; + 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: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); - rel->r_addend += hi_reloc.hi_addend; + 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: @@ -3821,7 +3882,8 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, rel->r_addend, symval, ELFNN_R_SYM(rel->r_info), - sym_sec); + 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; @@ -3847,7 +3909,8 @@ _bfd_riscv_relax_delete (bfd *abfd, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcgp_relocs 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)) @@ -3914,6 +3977,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, 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) @@ -4008,11 +4072,36 @@ _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->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; + } + if (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.u.def.section->output_section == NULL || (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak)) @@ -4065,7 +4154,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, if (!relax_func (abfd, sec, sym_sec, info, rel, symval, max_alignment, reserve_size, again, - &pcgp_relocs)) + &pcgp_relocs, undefined_weak)) goto fail; } |