diff options
Diffstat (limited to 'bfd/elfnn-riscv.c')
-rw-r--r-- | bfd/elfnn-riscv.c | 302 |
1 files changed, 267 insertions, 35 deletions
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index 1c494f5..790f0397 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -176,6 +176,11 @@ struct _bfd_riscv_elf_obj_tdata /* tls_type for each local got entry. */ char *local_got_tls_type; + + /* All GNU_PROPERTY_RISCV_FEATURE_1_AND properties. */ + uint32_t gnu_and_prop; + /* PLT type. */ + riscv_plt_type plt_type; }; #define _bfd_riscv_elf_tdata(abfd) \ @@ -232,6 +237,15 @@ struct riscv_elf_link_hash_table /* Relocations for variant CC symbols may be present. */ int variant_cc; + + /* The number of bytes in the PLT header and enties. */ + bfd_size_type plt_header_size; + bfd_size_type plt_entry_size; + + /* Functions to make PLT header and entries. */ + bool (*make_plt_header) (bfd *output_bfd, struct riscv_elf_link_hash_table *htab); + bool (*make_plt_entry) (bfd *output_bfd, asection *got, bfd_vma got_offset, + asection *plt, bfd_vma plt_offset); }; /* Instruction access functions. */ @@ -252,6 +266,12 @@ struct riscv_elf_link_hash_table && elf_hash_table_id (elf_hash_table (p)) == RISCV_ELF_DATA) \ ? (struct riscv_elf_link_hash_table *) (p)->hash : NULL) +/* Forward declaration PLT related functions. */ +static bool +riscv_make_plt_header (bfd *, struct riscv_elf_link_hash_table *); +static bool +riscv_make_plt_entry (bfd *, asection *, bfd_vma, asection *, bfd_vma); + void riscv_elfNN_set_options (struct bfd_link_info *link_info, struct riscv_elf_params *params) @@ -297,6 +317,12 @@ riscv_is_insn_reloc (const reloc_howto_type *howto) #define PLT_ENTRY_INSNS 4 #define PLT_HEADER_SIZE (PLT_HEADER_INSNS * 4) #define PLT_ENTRY_SIZE (PLT_ENTRY_INSNS * 4) + +#define PLT_ZICFILP_UNLABELED_HEADER_INSNS 12 +#define PLT_ZICFILP_UNLABELED_ENTRY_INSNS 4 +#define PLT_ZICFILP_UNLABELED_HEADER_SIZE (PLT_ZICFILP_UNLABELED_HEADER_INSNS * 4) +#define PLT_ZICFILP_UNLABELED_ENTRY_SIZE (PLT_ZICFILP_UNLABELED_ENTRY_INSNS * 4) + #define GOT_ENTRY_SIZE RISCV_ELF_WORD_BYTES #define TLS_GD_GOT_ENTRY_SIZE (RISCV_ELF_WORD_BYTES * 2) #define TLS_IE_GOT_ENTRY_SIZE RISCV_ELF_WORD_BYTES @@ -314,12 +340,42 @@ riscv_is_insn_reloc (const reloc_howto_type *howto) # define MATCH_LREG MATCH_LD #endif + +/* Check whether the compact PLT is used in this object. Tools need this + to dump the correct PLT header contents. */ + +static long +elfNN_riscv_get_synthetic_symtab (bfd *abfd, + long symcount, + asymbol **syms, + long dynsymcount, + asymbol **dynsyms, + asymbol **ret) +{ + /* Check Zicfilp PLT. */ + elf_property *prop; + prop = _bfd_elf_get_property (abfd, GNU_PROPERTY_RISCV_FEATURE_1_AND, 4); + if (prop) + { + if (prop->u.number & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) + _bfd_riscv_elf_tdata (abfd)->plt_type |= PLT_ZICFILP_UNLABELED; + } + + return _bfd_elf_get_synthetic_symtab (abfd, symcount, syms, + dynsymcount, dynsyms, ret); +} + /* Generate a PLT header. */ static bool -riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr, - uint32_t *entry) +riscv_make_plt_header (bfd *output_bfd, struct riscv_elf_link_hash_table *htab) { + asection *splt = htab->elf.splt; + bfd_vma addr = sec_addr (splt); + + asection *sgotplt = htab->elf.sgotplt; + bfd_vma gotplt_addr = sec_addr (sgotplt); + bfd_vma gotplt_offset_high = RISCV_PCREL_HIGH_PART (gotplt_addr, addr); bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, addr); @@ -340,6 +396,7 @@ riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr, l[w|d] t0, PTRSIZE(t0) # link map jr t3 */ + uint32_t entry[PLT_HEADER_INSNS]; entry[0] = RISCV_UTYPE (AUIPC, X_T2, gotplt_offset_high); entry[1] = RISCV_RTYPE (SUB, X_T1, X_T1, X_T3); entry[2] = RISCV_ITYPE (LREG, X_T3, X_T2, gotplt_offset_low); @@ -349,15 +406,78 @@ riscv_make_plt_header (bfd *output_bfd, bfd_vma gotplt_addr, bfd_vma addr, entry[6] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES); entry[7] = RISCV_ITYPE (JALR, 0, X_T3, 0); + for (int i = 0; i < PLT_HEADER_INSNS; i++) + bfd_putl32 (entry[i], splt->contents + 4 * i); + + return true; +} + +static bool +riscv_make_plt_zicfilp_unlabeled_header (bfd *output_bfd, + struct riscv_elf_link_hash_table *htab) +{ + /* + lpad 0 # disable label checking + auipc t2, %hi(.got.plt) # Rewrite this to using + sub t1, t1, t3 # shifted .got.plt offset + hdr size + 16 + l[w|d] t3, %lo(1b)(t2) # _dl_runtime_resolve + addi t1, t1, -(hdr size + 12) # shifted .got.plt offset + addi t0, t2, %pcrel_lo(1b) # &.got.plt + srli t1, t1, log2(16/PTRSIZE) # .got.plt offset + l[w|d] t0, PTRSIZE(t0) # link map + jr t3 + nop + nop + nop */ + + /* RVE has no t3 register, so this won't work, and is not supported. */ + if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE) + { + _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"), + output_bfd); + return false; + } + + asection *gotplt = htab->elf.sgotplt; + bfd_vma gotplt_addr = sec_addr (gotplt); + + asection *splt = htab->elf.splt; + bfd_vma plt_header_addr = sec_addr (splt); + + bfd_vma auipc_addr = plt_header_addr + 4; + /* Add INSN_BYTES to skip the lpad instruction. */ + bfd_vma gotplt_offset_high = RISCV_PCREL_HIGH_PART (gotplt_addr, auipc_addr); + bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, auipc_addr); + + uint32_t header[PLT_ZICFILP_UNLABELED_HEADER_INSNS]; + header[0] = RISCV_UTYPE (LPAD, X_ZERO, 0); + header[1] = RISCV_UTYPE (AUIPC, X_T2, gotplt_offset_high); + header[2] = RISCV_RTYPE (SUB, X_T1, X_T1, X_T3); + header[3] = RISCV_ITYPE (LREG, X_T3, X_T2, gotplt_offset_low); + header[4] = RISCV_ITYPE (ADDI, X_T1, X_T1, + (uint32_t) -(PLT_ZICFILP_UNLABELED_HEADER_SIZE + 16)); + header[5] = RISCV_ITYPE (ADDI, X_T0, X_T2, gotplt_offset_low); + header[6] = RISCV_ITYPE (SRLI, X_T1, X_T1, 4 - RISCV_ELF_LOG_WORD_BYTES); + header[7] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES); + header[8] = RISCV_ITYPE (JALR, 0, X_T3, 0); + header[9] = RISCV_NOP; + header[10] = RISCV_NOP; + header[11] = RISCV_NOP; + + for (int i = 0; i < PLT_ZICFILP_UNLABELED_HEADER_INSNS; i++) + bfd_putl32 (header[i], splt->contents + 4 * i); + return true; } /* Generate a PLT entry. */ static bool -riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr, - uint32_t *entry) +riscv_make_plt_entry (bfd *output_bfd, asection *gotsec, bfd_vma got_offset, + asection *pltsec, bfd_vma plt_offset) { + bfd_vma got = sec_addr (gotsec) + got_offset; + bfd_vma addr = sec_addr (pltsec) + plt_offset; /* RVE has no t3 register, so this won't work, and is not supported. */ if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE) { @@ -371,11 +491,50 @@ riscv_make_plt_entry (bfd *output_bfd, bfd_vma got, bfd_vma addr, jalr t1, t3 nop */ + uint32_t entry[PLT_ENTRY_INSNS]; entry[0] = RISCV_UTYPE (AUIPC, X_T3, RISCV_PCREL_HIGH_PART (got, addr)); entry[1] = RISCV_ITYPE (LREG, X_T3, X_T3, RISCV_PCREL_LOW_PART (got, addr)); entry[2] = RISCV_ITYPE (JALR, X_T1, X_T3, 0); entry[3] = RISCV_NOP; + bfd_byte *loc = pltsec->contents + plt_offset; + for (int i = 0; i < PLT_ENTRY_INSNS; i++) + bfd_putl32 (entry[i], loc + 4 * i); + + return true; +} + +static bool +riscv_make_plt_zicfilp_unlabeled_entry (bfd *output_bfd, asection *got, + bfd_vma got_offset, asection *plt, + bfd_vma plt_offset) +{ + /* lpad 0 + 1: auipc t3, %pcrel_hi(function@.got.plt) + l[w|d] t3, %pcrel_lo(1b)(t3) + jalr t1, t3 */ + + /* RVE has no t3 register, so this won't work, and is not supported. */ + if (elf_elfheader (output_bfd)->e_flags & EF_RISCV_RVE) + { + _bfd_error_handler (_("%pB: warning: RVE PLT generation not supported"), + output_bfd); + return false; + } + + bfd_vma got_entry_addr = sec_addr(got) + got_offset; + bfd_vma plt_entry_addr = sec_addr(plt) + plt_offset; + bfd_vma auipc_addr = plt_entry_addr + 4; + uint32_t entry[PLT_ZICFILP_UNLABELED_ENTRY_INSNS]; + entry[0] = RISCV_UTYPE (LPAD, X_ZERO, 0); + entry[1] = RISCV_UTYPE (AUIPC, X_T3, RISCV_PCREL_HIGH_PART (got_entry_addr, auipc_addr)); + entry[2] = RISCV_ITYPE (LREG, X_T3, X_T3, RISCV_PCREL_LOW_PART (got_entry_addr, auipc_addr)); + entry[3] = RISCV_ITYPE (JALR, X_T1, X_T3, 0); + + bfd_byte *loc = plt->contents + plt_offset; + for (int i = 0; i < PLT_ZICFILP_UNLABELED_ENTRY_INSNS; i++) + bfd_putl32 (entry[i], loc + 4 * i); + return true; } @@ -489,6 +648,38 @@ riscv_elf_link_hash_table_free (bfd *obfd) _bfd_elf_link_hash_table_free (obfd); } +/* Set up the PLT generation stubs in the hash table. */ + +static void +setup_plt_values (struct bfd *output_bfd, + struct riscv_elf_link_hash_table *htab, + unsigned plt_type) +{ + switch (plt_type) + { + case PLT_NORMAL: + htab->plt_header_size = PLT_HEADER_SIZE; + htab->plt_entry_size = PLT_ENTRY_SIZE; + htab->make_plt_header = riscv_make_plt_header; + htab->make_plt_entry = riscv_make_plt_entry; + break; + + case PLT_ZICFILP_UNLABELED: + htab->plt_header_size = PLT_ZICFILP_UNLABELED_HEADER_SIZE; + htab->plt_entry_size = PLT_ZICFILP_UNLABELED_ENTRY_SIZE; + htab->make_plt_header = riscv_make_plt_zicfilp_unlabeled_header; + htab->make_plt_entry = riscv_make_plt_zicfilp_unlabeled_entry; + break; + + default: + _bfd_error_handler (_("%pB: error: unsupported PLT type: %u"), + output_bfd, + plt_type); + bfd_set_error (bfd_error_bad_value); + break; + } +} + /* Create a RISC-V ELF linker hash table. */ static struct bfd_link_hash_table * @@ -511,6 +702,8 @@ riscv_elf_link_hash_table_create (bfd *abfd) ret->max_alignment = (bfd_vma) -1; ret->max_alignment_for_gp = (bfd_vma) -1; + setup_plt_values (abfd, ret, PLT_NORMAL); + /* Create hash table for local ifunc. */ ret->loc_hash_table = htab_try_create (1024, riscv_elf_local_htab_hash, @@ -1259,12 +1452,12 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) asection *s = htab->elf.splt; if (s->size == 0) - s->size = PLT_HEADER_SIZE; + s->size = htab->plt_header_size; h->plt.offset = s->size; /* Make room for this entry. */ - s->size += PLT_ENTRY_SIZE; + s->size += htab->plt_entry_size; /* We also need to make an entry in the .got.plt section. */ htab->elf.sgotplt->size += GOT_ENTRY_SIZE; @@ -1456,6 +1649,7 @@ allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h, void *inf) { struct bfd_link_info *info; + struct riscv_elf_link_hash_table *htab; if (h->root.type == bfd_link_hash_indirect) return true; @@ -1464,6 +1658,7 @@ allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h, h = (struct elf_link_hash_entry *) h->root.u.i.link; info = (struct bfd_link_info *) inf; + htab = riscv_elf_hash_table (info); /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it here if it is defined and referenced in a non-shared object. */ @@ -1471,8 +1666,8 @@ allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h, && h->def_regular) return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, &h->dyn_relocs, - PLT_ENTRY_SIZE, - PLT_HEADER_SIZE, + htab->plt_entry_size, + htab->plt_header_size, GOT_ENTRY_SIZE, true); return true; @@ -2472,14 +2667,14 @@ riscv_elf_relocate_section (bfd *output_bfd, if (htab->elf.splt != NULL) { - plt_idx = (h->plt.offset - PLT_HEADER_SIZE) - / PLT_ENTRY_SIZE; + plt_idx = (h->plt.offset - htab->plt_header_size) + / htab->plt_entry_size; off = GOTPLT_HEADER_SIZE + (plt_idx * GOT_ENTRY_SIZE); base_got = htab->elf.sgotplt; } else { - plt_idx = h->plt.offset / PLT_ENTRY_SIZE; + plt_idx = h->plt.offset / htab->plt_entry_size; off = plt_idx * GOT_ENTRY_SIZE; base_got = htab->elf.igotplt; } @@ -3247,8 +3442,7 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, { /* We've decided to create a PLT entry for this symbol. */ bfd_byte *loc; - bfd_vma i, header_address, plt_idx, got_offset, got_address; - uint32_t plt_entry[PLT_ENTRY_INSNS]; + bfd_vma plt_idx, got_offset, got_address; Elf_Internal_Rela rela; asection *plt, *gotplt, *relplt; @@ -3278,36 +3472,29 @@ riscv_elf_finish_dynamic_symbol (bfd *output_bfd, || relplt == NULL) abort (); - /* Calculate the address of the PLT header. */ - header_address = sec_addr (plt); - /* Calculate the index of the entry and the offset of .got.plt entry. For static executables, we don't reserve anything. */ if (plt == htab->elf.splt) { - plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE; + plt_idx = (h->plt.offset - htab->plt_header_size) + / htab->plt_entry_size; got_offset = GOTPLT_HEADER_SIZE + (plt_idx * GOT_ENTRY_SIZE); } else { - plt_idx = h->plt.offset / PLT_ENTRY_SIZE; + plt_idx = h->plt.offset / htab->plt_entry_size; got_offset = plt_idx * GOT_ENTRY_SIZE; } /* Calculate the address of the .got.plt entry. */ got_address = sec_addr (gotplt) + got_offset; - /* Find out where the .plt entry should go. */ - loc = plt->contents + h->plt.offset; /* Fill in the PLT entry itself. */ - if (! riscv_make_plt_entry (output_bfd, got_address, - header_address + h->plt.offset, - plt_entry)) + if (! htab->make_plt_entry (output_bfd, gotplt, got_offset, + plt, h->plt.offset)) return false; - for (i = 0; i < PLT_ENTRY_INSNS; i++) - bfd_putl32 (plt_entry[i], loc + 4*i); /* Fill in the initial value of the .got.plt entry. */ loc = gotplt->contents + (got_address - sec_addr (gotplt)); @@ -3595,19 +3782,12 @@ riscv_elf_finish_dynamic_sections (bfd *output_bfd, /* Fill in the head and tail entries in the procedure linkage table. */ if (splt->size > 0) { - int i; - uint32_t plt_header[PLT_HEADER_INSNS]; - ret = riscv_make_plt_header (output_bfd, - sec_addr (htab->elf.sgotplt), - sec_addr (splt), plt_header); + ret = htab->make_plt_header (output_bfd, htab); if (!ret) return ret; - for (i = 0; i < PLT_HEADER_INSNS; i++) - bfd_putl32 (plt_header[i], splt->contents + 4*i); - elf_section_data (splt->output_section)->this_hdr.sh_entsize - = PLT_ENTRY_SIZE; + = htab->plt_entry_size; } } @@ -3661,7 +3841,18 @@ static bfd_vma riscv_elf_plt_sym_val (bfd_vma i, const asection *plt, const arelent *rel ATTRIBUTE_UNUSED) { - return plt->vma + PLT_HEADER_SIZE + i * PLT_ENTRY_SIZE; + unsigned plt_type = _bfd_riscv_elf_tdata (plt->owner)->plt_type; + switch (plt_type) + { + case PLT_NORMAL: + return plt->vma + (PLT_HEADER_SIZE) + (i * PLT_ENTRY_SIZE); + + case PLT_ZICFILP_UNLABELED: + return plt->vma + PLT_ZICFILP_UNLABELED_HEADER_SIZE + (i * PLT_ZICFILP_UNLABELED_ENTRY_SIZE); + + default: + abort (); + } } /* Used to decide how to sort relocs in an optimal manner for the @@ -5753,6 +5944,43 @@ riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h, h->other |= STO_RISCV_VARIANT_CC; } +/* Implement elf_backend_setup_gnu_properties for RISC-V. It serves as a + wrapper function for _bfd_riscv_elf_link_setup_gnu_properties to account + for the effect of GNU properties of the output_bfd. */ + +static bfd * +elfNN_riscv_link_setup_gnu_properties (struct bfd_link_info *info) +{ + uint32_t and_prop = _bfd_riscv_elf_tdata (info->output_bfd)->gnu_and_prop; + + bfd *pbfd = _bfd_riscv_elf_link_setup_gnu_properties (info, &and_prop); + + _bfd_riscv_elf_tdata (info->output_bfd)->gnu_and_prop = and_prop; + + if (and_prop & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) + _bfd_riscv_elf_tdata (info->output_bfd)->plt_type = PLT_ZICFILP_UNLABELED; + + setup_plt_values (info->output_bfd, riscv_elf_hash_table (info), + _bfd_riscv_elf_tdata (info->output_bfd)->plt_type); + + return pbfd; +} + +/* Implement elf_backend_merge_gnu_properties for RISC-V. It serves as a + wrapper function for _bfd_riscv_elf_merge_gnu_properties to account + for the effect of GNU properties of the output_bfd. */ + +static bool +elfNN_riscv_merge_gnu_properties (struct bfd_link_info *info, bfd *abfd, + bfd *bbfd ATTRIBUTE_UNUSED, + elf_property *aprop, elf_property *bprop) +{ + uint32_t and_prop = _bfd_riscv_elf_tdata (info->output_bfd)->gnu_and_prop; + + return _bfd_riscv_elf_merge_gnu_properties (info, abfd, aprop, bprop, + and_prop); +} + #define TARGET_LITTLE_SYM riscv_elfNN_vec #define TARGET_LITTLE_NAME "elfNN-littleriscv" #define TARGET_BIG_SYM riscv_elfNN_be_vec @@ -5785,6 +6013,7 @@ riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h, #define elf_info_to_howto riscv_info_to_howto_rela #define bfd_elfNN_bfd_relax_section _bfd_riscv_relax_section #define bfd_elfNN_mkobject elfNN_riscv_mkobject +#define bfd_elfNN_get_synthetic_symtab elfNN_riscv_get_synthetic_symtab #define elf_backend_additional_program_headers \ riscv_elf_additional_program_headers #define elf_backend_modify_segment_map riscv_elf_modify_segment_map @@ -5792,6 +6021,9 @@ riscv_elf_merge_symbol_attribute (struct elf_link_hash_entry *h, #define elf_backend_init_index_section _bfd_elf_init_1_index_section +#define elf_backend_setup_gnu_properties elfNN_riscv_link_setup_gnu_properties +#define elf_backend_merge_gnu_properties elfNN_riscv_merge_gnu_properties + #define elf_backend_can_gc_sections 1 #define elf_backend_can_refcount 1 #define elf_backend_want_got_plt 1 |