aboutsummaryrefslogtreecommitdiff
path: root/bfd/elfnn-riscv.c
diff options
context:
space:
mode:
authorAndrew Waterman <andrew@sifive.com>2016-12-18 22:53:48 -0800
committerAlan Modra <amodra@gmail.com>2016-12-20 12:26:33 +1030
commit45f764234a71431b581340957a3c8338e0593fdb (patch)
tree15fdb770f8d357dde9e570452a2afed2ba0772d0 /bfd/elfnn-riscv.c
parent1d65abb5e2cb1624b358dda27a53a070bec685cc (diff)
Rework RISC-V relocations
Before this commit we didn't cleanly support CFI directives because the internal offsets used to get relaxed which broke them. This patch significantly reworks how we handle linker relaxations: * DWARF is now properly supported * There is a ".option norelax" to disable relaxations, for when users write assembly that can't be relaxed (if it's to be later patched up, for example). * There is an additional _RELAX relocation that specifies when previous relocations can be relaxed. We're in the process of documenting the RISC-V ELF ABI, which will include documentation of our relocations https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md but we expect that this relocation set will remain ABI compatible in the future (ie, it's safe to release). Thanks to Kuan-Lin Chen for figuring out how to correctly relax the debug info! include/ * elf/riscv.h: Add R_RISCV_TPREL_I through R_RISCV_SET32. bfd/ * reloc.c (BFD_RELOC_RISCV_TPREL_I): New relocation. (BFD_RELOC_RISCV_TPREL_S): Likewise. (BFD_RELOC_RISCV_RELAX): Likewise. (BFD_RELOC_RISCV_CFA): Likewise. (BFD_RELOC_RISCV_SUB6): Likewise. (BFD_RELOC_RISCV_SET8): Likewise. (BFD_RELOC_RISCV_SET8): Likewise. (BFD_RELOC_RISCV_SET16): Likewise. (BFD_RELOC_RISCV_SET32): Likewise. * elfnn-riscv.c (perform_relocation): Handle the new relocations. (_bfd_riscv_relax_tls_le): Likewise. (_bfd_riscv_relax_align): Likewise. (_bfd_riscv_relax_section): Likewise. (howto_table): Likewise. (riscv_reloc_map): Likewise. (relax_func_t): New type. (_bfd_riscv_relax_call): Add reserve_size argument, which controls the maximal offset pessimism. Correct type of max_alignment. (_bfd_riscv_relax_lui): Likewise. (_bfd_riscv_relax_tls_le): Likewise. (_bfd_riscv_relax_align): Likewise. (_bfd_riscv_relax_section): Compute the required reserve size when relocating and use it to when calling relax_func. * bfd-in2.h: Regenerate. * libbfd.h: Likewise. gas/ * config/tc-riscv.c (riscv_set_options): Add relax. (riscv_opts): Likewise. (s_riscv_option): Add relax and norelax. (riscv_apply_const_reloc): New function. (append_insn): Move constant relocation handling to riscv_apply_const_reloc. (md_pcrel_from): Likewise. (parse_relocation): Skip BFD_RELOC_UNUSED. (md_pcrel_from): Handle BFD_RELOC_RISCV_SUB6, BFD_RELOC_RISCV_RELAX, BFD_RELOC_RISCV_CFA. (md_apply_fix): Likewise. (riscv_pre_output_hook): New function. * config/tc-riscv.h (md_pre_output_hook): Define. (riscv_pre_output_hook): Declare. (DWARF_CIE_DATA_ALIGNMENT): Always -4.
Diffstat (limited to 'bfd/elfnn-riscv.c')
-rw-r--r--bfd/elfnn-riscv.c97
1 files changed, 80 insertions, 17 deletions
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
index f9b3e2ceee..51a2a10e03 100644
--- a/bfd/elfnn-riscv.c
+++ b/bfd/elfnn-riscv.c
@@ -1493,6 +1493,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 +1501,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;
@@ -1548,10 +1550,15 @@ 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_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
break;
@@ -1817,6 +1824,7 @@ riscv_elf_relocate_section (bfd *output_bfd,
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 +1838,10 @@ 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:
/* These require no special handling beyond perform_relocation. */
break;
@@ -1923,6 +1935,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:
@@ -1953,6 +1966,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))
{
/* We can use tp as the base register. */
@@ -1961,6 +1979,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:
@@ -2668,6 +2688,11 @@ riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count)
return TRUE;
}
+typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *,
+ struct bfd_link_info *,
+ Elf_Internal_Rela *,
+ bfd_vma, bfd_vma, bfd_vma, bfd_boolean *);
+
/* Relax AUIPC + JALR into JAL. */
static bfd_boolean
@@ -2675,7 +2700,8 @@ _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_vma max_alignment,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
bfd_boolean *again)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -2757,7 +2783,8 @@ _bfd_riscv_relax_lui (bfd *abfd,
struct bfd_link_info *link_info,
Elf_Internal_Rela *rel,
bfd_vma symval,
- unsigned int max_alignment,
+ bfd_vma max_alignment,
+ bfd_vma reserve_size,
bfd_boolean *again)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -2773,8 +2800,10 @@ _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))
- || (symval < gp && VALID_ITYPE_IMM (symval - gp - max_alignment)))
+ || (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))
@@ -2832,20 +2861,35 @@ _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_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
bfd_boolean *again)
{
/* 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);
+
+ default:
+ abort ();
+ }
}
/* Implement R_RISCV_ALIGN by deleting excess alignment NOPs. */
@@ -2856,7 +2900,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec,
struct bfd_link_info *link_info ATTRIBUTE_UNUSED,
Elf_Internal_Rela *rel,
bfd_vma symval,
- unsigned int max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma max_alignment ATTRIBUTE_UNUSED,
+ bfd_vma reserve_size ATTRIBUTE_UNUSED,
bfd_boolean *again ATTRIBUTE_UNUSED)
{
bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
@@ -2909,7 +2954,7 @@ _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;
*again = FALSE;
@@ -2935,7 +2980,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
{
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;
@@ -2947,13 +2992,26 @@ _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 (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)
relax_func = _bfd_riscv_relax_align;
-
- if (!relax_func)
+ else
continue;
data->relocs = relocs;
@@ -2978,6 +3036,8 @@ _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;
@@ -3011,13 +3071,16 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,
else
symval = sec_addr (h->root.u.def.section) + h->root.u.def.value;
+ if (h->type != STT_FUNC)
+ reserve_size =
+ (h->size - rel->r_addend) > h->size ? 0 : h->size - rel->r_addend;
sym_sec = h->root.u.def.section;
}
symval += rel->r_addend;
if (!relax_func (abfd, sec, sym_sec, info, rel, symval,
- max_alignment, again))
+ max_alignment, reserve_size, again))
goto fail;
}