From 50af352e5e0f5744f48e864661f880f401967eb2 Mon Sep 17 00:00:00 2001 From: Andrew Waterman Date: Sat, 10 Oct 2015 15:40:59 -0700 Subject: binutils: fix LUI relaxation bug Section merging could make a gp-relative reference go out of range after relaxation. Stop relaxing LUIs against mergeable symbols, and add error checking to catch things like this in the future. --- binutils/bfd/elfnn-riscv.c | 50 ++++++++++++++++++++++++++++++++------------ binutils/bfd/elfxx-riscv.c | 32 ++++++++++++++++++++++++++++ binutils/include/elf/riscv.h | 2 ++ 3 files changed, 71 insertions(+), 13 deletions(-) (limited to 'binutils') diff --git a/binutils/bfd/elfnn-riscv.c b/binutils/bfd/elfnn-riscv.c index bd086d6..46c24ba 100644 --- a/binutils/bfd/elfnn-riscv.c +++ b/binutils/bfd/elfnn-riscv.c @@ -1488,12 +1488,14 @@ perform_relocation (const reloc_howto_type *howto, break; case R_RISCV_LO12_I: + case R_RISCV_GPREL_I: case R_RISCV_TPREL_LO12_I: case R_RISCV_PCREL_LO12_I: value = ENCODE_ITYPE_IMM (value); break; case R_RISCV_LO12_S: + case R_RISCV_GPREL_S: case R_RISCV_TPREL_LO12_S: case R_RISCV_PCREL_LO12_S: value = ENCODE_STYPE_IMM (value); @@ -1810,6 +1812,8 @@ riscv_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, case R_RISCV_BRANCH: case R_RISCV_RVC_BRANCH: case R_RISCV_RVC_LUI: + case R_RISCV_LO12_I: + case R_RISCV_LO12_S: /* These require no special handling beyond perform_relocation. */ break; @@ -1943,8 +1947,8 @@ riscv_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, } break; - case R_RISCV_LO12_I: - case R_RISCV_LO12_S: + case R_RISCV_GPREL_I: + case R_RISCV_GPREL_S: { bfd_vma gp = riscv_global_pointer_value (info); bfd_boolean x0_base = VALID_ITYPE_IMM (relocation + rel->r_addend); @@ -1960,6 +1964,8 @@ riscv_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, } bfd_put_32 (input_bfd, insn, contents + rel->r_offset); } + else + r = bfd_reloc_overflow; break; } @@ -2712,8 +2718,7 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, /* Relax non-PIC global variable references. */ static bfd_boolean -_bfd_riscv_relax_lui (bfd *abfd, asection *sec, - asection *sym_sec ATTRIBUTE_UNUSED, +_bfd_riscv_relax_lui (bfd *abfd, asection *sec, asection *sym_sec, struct bfd_link_info *link_info, Elf_Internal_Rela *rel, bfd_vma symval, @@ -2723,22 +2728,41 @@ _bfd_riscv_relax_lui (bfd *abfd, asection *sec, bfd_vma gp = riscv_global_pointer_value (link_info); int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC; + /* Mergeable symbols might later move out of range. */ + if (sym_sec->flags & SEC_MERGE) + return TRUE; + BFD_ASSERT (rel->r_offset + 4 <= sec->size); /* Is the reference in range of x0 or gp? */ - if (VALID_ITYPE_IMM (symval - gp) || (symval < RISCV_IMM_REACH/2)) + if (VALID_ITYPE_IMM (symval) || VALID_ITYPE_IMM (symval - gp)) { - /* We can delete the unnecessary AUIPC. The corresponding LO12 reloc - will be converted to gp- or x0-relative during relocation. */ - rel->r_info = ELFNN_R_INFO (0, R_RISCV_NONE); + 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); + return TRUE; - *again = TRUE; - return riscv_relax_delete_bytes (abfd, sec, rel->r_offset, 4); + case R_RISCV_LO12_S: + 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); + + default: + abort (); + } } /* Can we relax LUI to C.LUI? Alignment might move the section forward; account for this assuming page alignment at worst. */ 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))) { @@ -2870,7 +2894,9 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, { if (type == R_RISCV_CALL || type == R_RISCV_CALL_PLT) relax_func = _bfd_riscv_relax_call; - else if (type == R_RISCV_HI20) + else if (type == R_RISCV_HI20 + || 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) relax_func = _bfd_riscv_relax_tls_le; @@ -2929,8 +2955,6 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, if (h->plt.offset != MINUS_ONE) symval = sec_addr (htab->elf.splt) + h->plt.offset; - else if (h->root.type == bfd_link_hash_undefweak) - symval = 0; else if (h->root.u.def.section->output_section == NULL || (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak)) diff --git a/binutils/bfd/elfxx-riscv.c b/binutils/bfd/elfxx-riscv.c index 447b791..e51514c 100644 --- a/binutils/bfd/elfxx-riscv.c +++ b/binutils/bfd/elfxx-riscv.c @@ -689,6 +689,36 @@ static reloc_howto_type howto_table[] = 0, /* src_mask */ ENCODE_RVC_IMM (-1U), /* dst_mask */ FALSE), /* pcrel_offset */ + + /* High 12 bits of 32-bit load or add. */ + HOWTO (R_RISCV_GPREL_I, /* 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_GPREL_I", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + ENCODE_ITYPE_IMM (-1U), /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* High 12 bits of 32-bit store. */ + HOWTO (R_RISCV_GPREL_S, /* 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_GPREL_S", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + ENCODE_STYPE_IMM (-1U), /* dst_mask */ + FALSE), /* pcrel_offset */ }; /* A mapping from BFD reloc types to RISC-V ELF reloc types. */ @@ -739,6 +769,8 @@ static const struct elf_reloc_map riscv_reloc_map[] = { BFD_RELOC_RISCV_RVC_BRANCH, R_RISCV_RVC_BRANCH }, { BFD_RELOC_RISCV_RVC_JUMP, R_RISCV_RVC_JUMP }, { 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 }, }; /* Given a BFD reloc type, return a howto structure. */ diff --git a/binutils/include/elf/riscv.h b/binutils/include/elf/riscv.h index c250b65..bb63287 100644 --- a/binutils/include/elf/riscv.h +++ b/binutils/include/elf/riscv.h @@ -77,6 +77,8 @@ START_RELOC_NUMBERS (elf_riscv_reloc_type) RELOC_NUMBER (R_RISCV_RVC_BRANCH, 44) RELOC_NUMBER (R_RISCV_RVC_JUMP, 45) RELOC_NUMBER (R_RISCV_RVC_LUI, 46) + RELOC_NUMBER (R_RISCV_GPREL_I, 47) + RELOC_NUMBER (R_RISCV_GPREL_S, 48) END_RELOC_NUMBERS (R_RISCV_max) /* Processor specific flags for the ELF header e_flags field. */ -- cgit v1.1