From b1308d2c3749cc454f00b70768ee33724d919527 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Wed, 16 Aug 2017 10:41:56 -0700 Subject: [PATCH] RISC-V: Support PCREL_* relocations agaist weak undefined symbols I recently modified our Linux port's base address such the absolute address 0 is no longer addressable as a 32-bit PC-relative offset. Since Linux links a weak undefined symbol in an intermediate binary, it needs to be able to reference absolute address 0. This patch changes R_RISCV_PCREL_* relocations to absolute relocations while resolving them in order to allow these symbols to be referenced in PC-relative programs linked at high addresses. Note that this doesn't apply to PIC, which also uses PC-relative relocations, just to position-dependent objects, which we use to allow programs to be linked at high addresses. In case some of our embedded users are using R_RISCV_PCREL_* as a hacked up method of getting position-independent binaries (which can work if you have very simple programs), we only convert the relocations when the PC-relative version would overflow. bfd/ChangeLog: 2017-09-07 Palmer Dabbelt * elfnn-riscv.c (riscv_zero_pcrel_hi_reloc): New function. (riscv_record_pcrel_hi_reloc): Add absolute argument. (riscv_elf_relocate_section): Call riscv_zero_pcrel_hi_reloc for R_RISCV_PCREL_HI20 relocs, and pass the result to riscv_record_pcrel_hi_reloc. --- bfd/ChangeLog | 8 ++++++ bfd/elfnn-riscv.c | 73 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index a8db91a3fa4..8096040a483 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,11 @@ +2017-09-07 Palmer Dabbelt + + * elfnn-riscv.c (riscv_zero_pcrel_hi_reloc): New function. + (riscv_record_pcrel_hi_reloc): Add absolute argument. + (riscv_elf_relocate_section): Call riscv_zero_pcrel_hi_reloc for + R_RISCV_PCREL_HI20 relocs, and pass the result to + riscv_record_pcrel_hi_reloc. + 2017-09-07 H.J. Lu * elf32-i386.c (elf_i386_convert_load_reloc): Add an argument, diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index 973f471e052..fdb151be827 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -1651,9 +1651,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) { - riscv_pcrel_hi_reloc entry = {addr, value - addr}; + /* 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) +{ + 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); @@ -1758,6 +1799,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; @@ -1931,7 +1973,17 @@ 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 (r_type); + if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, + relocation, absolute)) r = bfd_reloc_overflow; break; @@ -2017,8 +2069,18 @@ riscv_elf_relocate_section (bfd *output_bfd, } case R_RISCV_PCREL_HI20: + absolute = riscv_zero_pcrel_hi_reloc (rel, + info, + pc, + relocation, + contents, + howto, + input_bfd); + r_type = ELFNN_R_TYPE (rel->r_info); + howto = riscv_elf_rtype_to_howto (r_type); if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, - relocation + rel->r_addend)) + relocation + rel->r_addend, + absolute)) r = bfd_reloc_overflow; break; @@ -2214,7 +2276,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; -- 2.30.2