From 45f764234a71431b581340957a3c8338e0593fdb Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Sun, 18 Dec 2016 22:53:48 -0800 Subject: [PATCH] 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. --- bfd/ChangeLog | 30 ++++++ bfd/bfd-in2.h | 9 ++ bfd/elfnn-riscv.c | 97 +++++++++++++++---- bfd/elfxx-riscv.c | 128 +++++++++++++++++++++++++ bfd/libbfd.h | 9 ++ bfd/reloc.c | 18 ++++ gas/ChangeLog | 19 ++++ gas/config/tc-riscv.c | 213 +++++++++++++++++++++++++++++++----------- gas/config/tc-riscv.h | 7 +- include/ChangeLog | 5 + include/elf/riscv.h | 8 ++ 11 files changed, 472 insertions(+), 71 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 500003dff58..b6bba2ac3e0 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,33 @@ +2016-12-20 Andrew Waterman + Kuan-Lin Chen + + * 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. + 2016-12-20 Andrew Waterman * elfnn-riscv.c: Formatting and comment fixes throughout. diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 1c6b70fc56c..b5ac178cb7c 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -4737,6 +4737,15 @@ number for the SBIC, SBIS, SBI and CBI instructions */ BFD_RELOC_RISCV_RVC_LUI, BFD_RELOC_RISCV_GPREL_I, BFD_RELOC_RISCV_GPREL_S, + BFD_RELOC_RISCV_TPREL_I, + BFD_RELOC_RISCV_TPREL_S, + BFD_RELOC_RISCV_RELAX, + BFD_RELOC_RISCV_CFA, + BFD_RELOC_RISCV_SUB6, + BFD_RELOC_RISCV_SET6, + BFD_RELOC_RISCV_SET8, + BFD_RELOC_RISCV_SET16, + BFD_RELOC_RISCV_SET32, /* Renesas RL78 Relocations. */ BFD_RELOC_RL78_NEG8, diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index f9b3e2ceeee..51a2a10e03a 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: @@ -1952,6 +1965,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 +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; } diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c index 0fb250d0f56..3d935cfb6d3 100644 --- a/bfd/elfxx-riscv.c +++ b/bfd/elfxx-riscv.c @@ -713,6 +713,126 @@ static reloc_howto_type howto_table[] = 0, /* src_mask */ ENCODE_STYPE_IMM (-1U), /* dst_mask */ FALSE), /* pcrel_offset */ + + /* TP-relative TLS LE load. */ + HOWTO (R_RISCV_TPREL_I, /* type */ + 0, /* rightshift */ + 2, /* size */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_TPREL_I", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + ENCODE_ITYPE_IMM (-1U), /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* TP-relative TLS LE store. */ + HOWTO (R_RISCV_TPREL_S, /* type */ + 0, /* rightshift */ + 2, /* size */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_TPREL_S", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + ENCODE_STYPE_IMM (-1U), /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* The paired relocation may be relaxed. */ + HOWTO (R_RISCV_RELAX, /* type */ + 0, /* rightshift */ + 3, /* size */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_RELAX", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* 6-bit in-place addition, for local label subtraction. */ + HOWTO (R_RISCV_SUB6, /* type */ + 0, /* rightshift */ + 0, /* size */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_SUB6", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0x3f, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* 6-bit in-place setting, for local label subtraction. */ + HOWTO (R_RISCV_SET6, /* type */ + 0, /* rightshift */ + 0, /* size */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_SET6", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0x3f, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* 8-bit in-place setting, for local label subtraction. */ + HOWTO (R_RISCV_SET8, /* type */ + 0, /* rightshift */ + 0, /* size */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_SET8", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + MINUS_ONE, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* 16-bit in-place setting, for local label subtraction. */ + HOWTO (R_RISCV_SET16, /* type */ + 0, /* rightshift */ + 1, /* size */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_SET16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + MINUS_ONE, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* 32-bit in-place setting, for local label subtraction. */ + HOWTO (R_RISCV_SET32, /* type */ + 0, /* rightshift */ + 2, /* size */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_RISCV_SET32", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + MINUS_ONE, /* dst_mask */ + FALSE), /* pcrel_offset */ }; /* A mapping from BFD reloc types to RISC-V ELF reloc types. */ @@ -766,6 +886,14 @@ static const struct elf_reloc_map riscv_reloc_map[] = { BFD_RELOC_RISCV_RVC_LUI, R_RISCV_RVC_LUI }, { BFD_RELOC_RISCV_GPREL_I, R_RISCV_GPREL_I }, { BFD_RELOC_RISCV_GPREL_S, R_RISCV_GPREL_S }, + { BFD_RELOC_RISCV_TPREL_I, R_RISCV_TPREL_I }, + { BFD_RELOC_RISCV_TPREL_S, R_RISCV_TPREL_S }, + { BFD_RELOC_RISCV_RELAX, R_RISCV_RELAX }, + { BFD_RELOC_RISCV_SUB6, R_RISCV_SUB6 }, + { BFD_RELOC_RISCV_SET6, R_RISCV_SET6 }, + { BFD_RELOC_RISCV_SET8, R_RISCV_SET8 }, + { BFD_RELOC_RISCV_SET16, R_RISCV_SET16 }, + { BFD_RELOC_RISCV_SET32, R_RISCV_SET32 }, }; /* Given a BFD reloc type, return a howto structure. */ diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 5ec39939c39..76bbd09c8b6 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -2204,6 +2204,15 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_RISCV_RVC_LUI", "BFD_RELOC_RISCV_GPREL_I", "BFD_RELOC_RISCV_GPREL_S", + "BFD_RELOC_RISCV_TPREL_I", + "BFD_RELOC_RISCV_TPREL_S", + "BFD_RELOC_RISCV_RELAX", + "BFD_RELOC_RISCV_CFA", + "BFD_RELOC_RISCV_SUB6", + "BFD_RELOC_RISCV_SET6", + "BFD_RELOC_RISCV_SET8", + "BFD_RELOC_RISCV_SET16", + "BFD_RELOC_RISCV_SET32", "BFD_RELOC_RL78_NEG8", "BFD_RELOC_RL78_NEG16", "BFD_RELOC_RL78_NEG24", diff --git a/bfd/reloc.c b/bfd/reloc.c index 56cd79b6254..3c7b6060b09 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -5124,6 +5124,24 @@ ENUMX BFD_RELOC_RISCV_GPREL_I ENUMX BFD_RELOC_RISCV_GPREL_S +ENUMX + BFD_RELOC_RISCV_TPREL_I +ENUMX + BFD_RELOC_RISCV_TPREL_S +ENUMX + BFD_RELOC_RISCV_RELAX +ENUMX + BFD_RELOC_RISCV_CFA +ENUMX + BFD_RELOC_RISCV_SUB6 +ENUMX + BFD_RELOC_RISCV_SET6 +ENUMX + BFD_RELOC_RISCV_SET8 +ENUMX + BFD_RELOC_RISCV_SET16 +ENUMX + BFD_RELOC_RISCV_SET32 ENUMDOC RISC-V relocations. diff --git a/gas/ChangeLog b/gas/ChangeLog index 36f1c2fb573..0e652b13469 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,22 @@ +2016-12-20 Andrew Waterman + Kuan-Lin Chen + + * 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. + 2016-12-20 Andrew Waterman * config/tc-riscv.c: Formatting and comment fixes throughout. diff --git a/gas/config/tc-riscv.c b/gas/config/tc-riscv.c index d8a627d14ab..d011864fd70 100644 --- a/gas/config/tc-riscv.c +++ b/gas/config/tc-riscv.c @@ -74,12 +74,14 @@ struct riscv_set_options { int pic; /* Generate position-independent code. */ int rvc; /* Generate RVC code. */ + int relax; /* Emit relocs the linker is allowed to relax. */ }; static struct riscv_set_options riscv_opts = { 0, /* pic */ 0, /* rvc */ + 1, /* relax */ }; static void @@ -648,6 +650,28 @@ md_begin (void) record_alignment (text_section, riscv_opts.rvc ? 1 : 2); } +static insn_t +riscv_apply_const_reloc (bfd_reloc_code_real_type reloc_type, bfd_vma value) +{ + switch (reloc_type) + { + case BFD_RELOC_32: + return value; + + case BFD_RELOC_RISCV_HI20: + return ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)); + + case BFD_RELOC_RISCV_LO12_S: + return ENCODE_STYPE_IMM (value); + + case BFD_RELOC_RISCV_LO12_I: + return ENCODE_ITYPE_IMM (value); + + default: + abort (); + } +} + /* Output an instruction. IP is the instruction information. ADDRESS_EXPR is an operand of the instruction to be used with RELOC_TYPE. */ @@ -676,43 +700,22 @@ append_insn (struct riscv_cl_insn *ip, expressionS *address_expr, return; } else if (address_expr->X_op == O_constant) + ip->insn_opcode |= riscv_apply_const_reloc (reloc_type, + address_expr->X_add_number); + else { - switch (reloc_type) - { - case BFD_RELOC_32: - ip->insn_opcode |= address_expr->X_add_number; - goto append; + howto = bfd_reloc_type_lookup (stdoutput, reloc_type); + if (howto == NULL) + as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type); - case BFD_RELOC_RISCV_HI20: - { - insn_t imm = RISCV_CONST_HIGH_PART (address_expr->X_add_number); - ip->insn_opcode |= ENCODE_UTYPE_IMM (imm); - goto append; - } - - case BFD_RELOC_RISCV_LO12_S: - ip->insn_opcode |= ENCODE_STYPE_IMM (address_expr->X_add_number); - goto append; + ip->fixp = fix_new_exp (ip->frag, ip->where, + bfd_get_reloc_size (howto), + address_expr, FALSE, reloc_type); - case BFD_RELOC_RISCV_LO12_I: - ip->insn_opcode |= ENCODE_ITYPE_IMM (address_expr->X_add_number); - goto append; - - default: - break; - } + ip->fixp->fx_tcbit = riscv_opts.relax; } - - howto = bfd_reloc_type_lookup (stdoutput, reloc_type); - if (howto == NULL) - as_bad (_("Unsupported RISC-V relocation number %d"), reloc_type); - - ip->fixp = fix_new_exp (ip->frag, ip->where, - bfd_get_reloc_size (howto), - address_expr, FALSE, reloc_type); } -append: add_fixed_insn (ip); install_insn (ip); } @@ -1085,7 +1088,8 @@ parse_relocation (char **str, bfd_reloc_code_real_type *reloc, /* Check whether the output BFD supports this relocation. If not, issue an error and fall back on something safe. */ - if (!bfd_reloc_type_lookup (stdoutput, percent_op->reloc)) + if (*reloc != BFD_RELOC_UNUSED + && !bfd_reloc_type_lookup (stdoutput, *reloc)) { as_bad ("relocation %s isn't supported by the current ABI", percent_op->str); @@ -1826,45 +1830,56 @@ md_pcrel_from (fixS *fixP) void md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) { + unsigned int subtype; bfd_byte *buf = (bfd_byte *) (fixP->fx_frag->fr_literal + fixP->fx_where); + bfd_boolean relaxable = FALSE; /* Remember value for tc_gen_reloc. */ fixP->fx_addnumber = *valP; switch (fixP->fx_r_type) { - case BFD_RELOC_RISCV_TLS_GOT_HI20: - case BFD_RELOC_RISCV_TLS_GD_HI20: - case BFD_RELOC_RISCV_TLS_DTPREL32: - case BFD_RELOC_RISCV_TLS_DTPREL64: - case BFD_RELOC_RISCV_TPREL_HI20: - case BFD_RELOC_RISCV_TPREL_LO12_I: - case BFD_RELOC_RISCV_TPREL_LO12_S: - case BFD_RELOC_RISCV_TPREL_ADD: - S_SET_THREAD_LOCAL (fixP->fx_addsy); - /* Fall through. */ - - case BFD_RELOC_RISCV_GOT_HI20: - case BFD_RELOC_RISCV_PCREL_HI20: case BFD_RELOC_RISCV_HI20: case BFD_RELOC_RISCV_LO12_I: case BFD_RELOC_RISCV_LO12_S: + bfd_putl32 (riscv_apply_const_reloc (fixP->fx_r_type, *valP) + | bfd_getl32 (buf), buf); + relaxable = TRUE; + break; + + case BFD_RELOC_RISCV_GOT_HI20: + case BFD_RELOC_RISCV_PCREL_HI20: case BFD_RELOC_RISCV_ADD8: case BFD_RELOC_RISCV_ADD16: case BFD_RELOC_RISCV_ADD32: case BFD_RELOC_RISCV_ADD64: + case BFD_RELOC_RISCV_SUB6: case BFD_RELOC_RISCV_SUB8: case BFD_RELOC_RISCV_SUB16: case BFD_RELOC_RISCV_SUB32: case BFD_RELOC_RISCV_SUB64: - gas_assert (fixP->fx_addsy != NULL); - /* Nothing needed to do. The value comes from the reloc entry. */ + case BFD_RELOC_RISCV_RELAX: + break; + + case BFD_RELOC_RISCV_TPREL_HI20: + case BFD_RELOC_RISCV_TPREL_LO12_I: + case BFD_RELOC_RISCV_TPREL_LO12_S: + case BFD_RELOC_RISCV_TPREL_ADD: + relaxable = TRUE; + /* Fall through. */ + + case BFD_RELOC_RISCV_TLS_GOT_HI20: + case BFD_RELOC_RISCV_TLS_GD_HI20: + case BFD_RELOC_RISCV_TLS_DTPREL32: + case BFD_RELOC_RISCV_TLS_DTPREL64: + S_SET_THREAD_LOCAL (fixP->fx_addsy); break; case BFD_RELOC_64: case BFD_RELOC_32: case BFD_RELOC_16: case BFD_RELOC_8: + case BFD_RELOC_RISCV_CFA: if (fixP->fx_addsy && fixP->fx_subsy) { fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP)); @@ -1895,6 +1910,49 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8; break; + case BFD_RELOC_RISCV_CFA: + /* Load the byte to get the subtype. */ + subtype = bfd_get_8 (NULL, &fixP->fx_frag->fr_literal[fixP->fx_where]); + switch (subtype) + { + case DW_CFA_advance_loc1: + fixP->fx_where++; + fixP->fx_next->fx_where++; + fixP->fx_r_type = BFD_RELOC_RISCV_SET8; + fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB8; + break; + + case DW_CFA_advance_loc2: + fixP->fx_size = 2; + fixP->fx_where++; + fixP->fx_next->fx_size = 2; + fixP->fx_next->fx_where++; + fixP->fx_r_type = BFD_RELOC_RISCV_SET16; + fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB16; + break; + + case DW_CFA_advance_loc4: + fixP->fx_size = 4; + fixP->fx_where++; + fixP->fx_next->fx_size = 4; + fixP->fx_next->fx_where++; + fixP->fx_r_type = BFD_RELOC_RISCV_SET32; + fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB32; + break; + + default: + if (subtype < 0x80 && (subtype & 0x40)) + { + /* DW_CFA_advance_loc */ + fixP->fx_r_type = BFD_RELOC_RISCV_SET6; + fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_SUB6; + } + else + as_fatal (_("internal error: bad CFA value #%d"), subtype); + break; + } + break; + default: /* This case is unreachable. */ abort (); @@ -1954,10 +2012,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) } break; - case BFD_RELOC_RISCV_PCREL_LO12_S: - case BFD_RELOC_RISCV_PCREL_LO12_I: case BFD_RELOC_RISCV_CALL: case BFD_RELOC_RISCV_CALL_PLT: + relaxable = TRUE; + break; + + case BFD_RELOC_RISCV_PCREL_LO12_S: + case BFD_RELOC_RISCV_PCREL_LO12_I: case BFD_RELOC_RISCV_ALIGN: break; @@ -1966,8 +2027,54 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) if (bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type) != NULL) as_fatal (_("internal error: bad relocation #%d"), fixP->fx_r_type); } + + /* Add an R_RISCV_RELAX reloc if the reloc is relaxable. */ + if (relaxable && fixP->fx_tcbit && fixP->fx_addsy != NULL) + { + fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP)); + fixP->fx_next->fx_addsy = fixP->fx_next->fx_subsy = NULL; + fixP->fx_next->fx_r_type = BFD_RELOC_RISCV_RELAX; + } } +/* Because the value of .cfi_remember_state may changed after relaxation, + we insert a fix to relocate it again in link-time. */ + +void +riscv_pre_output_hook (void) +{ + const frchainS *frch; + const asection *s; + + for (s = stdoutput->sections; s; s = s->next) + for (frch = seg_info (s)->frchainP; frch; frch = frch->frch_next) + { + const fragS *frag; + + for (frag = frch->frch_root; frag; frag = frag->fr_next) + { + if (frag->fr_type == rs_cfa) + { + const fragS *loc4_frag; + expressionS exp; + + symbolS *add_symbol = frag->fr_symbol->sy_value.X_add_symbol; + symbolS *op_symbol = frag->fr_symbol->sy_value.X_op_symbol; + + exp.X_op = O_subtract; + exp.X_add_symbol = add_symbol; + exp.X_add_number = 0; + exp.X_op_symbol = op_symbol; + + loc4_frag = (fragS *) frag->fr_opcode; + fix_new_exp (loc4_frag, (int) frag->fr_offset, 1, &exp, 0, + BFD_RELOC_RISCV_CFA); + } + } + } +} + + /* This structure is used to hold a stack of .option values. */ struct riscv_option_stack @@ -1998,10 +2105,10 @@ s_riscv_option (int x ATTRIBUTE_UNUSED) riscv_opts.pic = TRUE; else if (strcmp (name, "nopic") == 0) riscv_opts.pic = FALSE; - else if (strcmp (name, "soft-float") == 0) - float_mode = FLOAT_MODE_SOFT; - else if (strcmp (name, "hard-float") == 0) - float_mode = FLOAT_MODE_HARD; + else if (strcmp (name, "relax") == 0) + riscv_opts.relax = TRUE; + else if (strcmp (name, "norelax") == 0) + riscv_opts.relax = FALSE; else if (strcmp (name, "push") == 0) { struct riscv_option_stack *s; diff --git a/gas/config/tc-riscv.h b/gas/config/tc-riscv.h index c2a11cefe9a..32cf3eea403 100644 --- a/gas/config/tc-riscv.h +++ b/gas/config/tc-riscv.h @@ -61,6 +61,9 @@ extern void riscv_after_parse_args (void); #define md_parse_long_option(arg) riscv_parse_long_option (arg) extern int riscv_parse_long_option (const char *); +#define md_pre_output_hook riscv_pre_output_hook() +extern void riscv_pre_output_hook (void); + /* Let the linker resolve all the relocs due to relaxation. */ #define tc_fix_adjustable(fixp) 0 #define md_allow_local_subtract(l,r,s) 0 @@ -93,7 +96,9 @@ extern int tc_riscv_regname_to_dw2regnum (char *); extern unsigned xlen; #define DWARF2_DEFAULT_RETURN_COLUMN X_RA -#define DWARF2_CIE_DATA_ALIGNMENT (-(int) (xlen / 8)) + +/* Even on RV64, use 4-byte alignment, as F registers may be only 32 bits. */ +#define DWARF2_CIE_DATA_ALIGNMENT -4 #define elf_tc_final_processing riscv_elf_final_processing extern void riscv_elf_final_processing (void); diff --git a/include/ChangeLog b/include/ChangeLog index 7e69c42f486..51792be23bc 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,8 @@ +2016-12-20 Andrew Waterman + Kuan-Lin Chen + + * elf/riscv.h: Add R_RISCV_TPREL_I through R_RISCV_SET32. + 2016-12-16 fincs * bfdlink.h (struct bfd_link_info): Add gc_keep_exported. diff --git a/include/elf/riscv.h b/include/elf/riscv.h index 8415659092b..44076119400 100644 --- a/include/elf/riscv.h +++ b/include/elf/riscv.h @@ -79,6 +79,14 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type) RELOC_NUMBER (R_RISCV_RVC_LUI, 46) RELOC_NUMBER (R_RISCV_GPREL_I, 47) RELOC_NUMBER (R_RISCV_GPREL_S, 48) + RELOC_NUMBER (R_RISCV_TPREL_I, 49) + RELOC_NUMBER (R_RISCV_TPREL_S, 50) + RELOC_NUMBER (R_RISCV_RELAX, 51) + RELOC_NUMBER (R_RISCV_SUB6, 52) + RELOC_NUMBER (R_RISCV_SET6, 53) + RELOC_NUMBER (R_RISCV_SET8, 54) + RELOC_NUMBER (R_RISCV_SET16, 55) + RELOC_NUMBER (R_RISCV_SET32, 56) END_RELOC_NUMBERS (R_RISCV_max) /* Processor specific flags for the ELF header e_flags field. */ -- 2.30.2