From 2928edd4da284bff42c770cb29dbf94f83e7f69c Mon Sep 17 00:00:00 2001 From: liuzhensong Date: Fri, 15 Jul 2022 16:07:48 +0800 Subject: [PATCH] LoongArch: Move ifunc info to rela.dyn from rela.plt Delete R_LARCH_IRELATIVE from dynamic loader (glibc ld.so) when loading lazy function (rela.plt section). In dynamic programs, move ifunc dynamic relocate info to section srelgot from srelplt. --- bfd/elfnn-loongarch.c | 338 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 311 insertions(+), 27 deletions(-) diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c index 5b44901b9e0..7c0b88f22de 100644 --- a/bfd/elfnn-loongarch.c +++ b/bfd/elfnn-loongarch.c @@ -1196,6 +1196,259 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) return true; } +/* Based function _bfd_elf_allocate_ifunc_dyn_relocs. + For local def and ref ifunc, + dynamic relocations are stored in + 1. rel[a].irelifunc section in PIC object. + 2. rel[a].srelgot section in dynamic executable. + 3. rel[a].irelplt section in static executable. + Change ifunc dynamic info from srelplt to srelgot. + In loader, remove R_LARCH_IRELACTIVE from rela lazy in ld.so. */ + +static bool +local_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, + struct elf_link_hash_entry *h, + struct elf_dyn_relocs **head, + unsigned int plt_entry_size, + unsigned int plt_header_size, + unsigned int got_entry_size, + bool avoid_plt) +{ + asection *plt, *gotplt, *relplt; + struct elf_dyn_relocs *p; + unsigned int sizeof_reloc; + const struct elf_backend_data *bed; + struct elf_link_hash_table *htab; + /* If AVOID_PLT is TRUE, don't use PLT if possible. */ + bool use_plt = !avoid_plt || h->plt.refcount > 0; + bool need_dynreloc = !use_plt || bfd_link_pic (info); + + /* When a PIC object references a STT_GNU_IFUNC symbol defined + in executable or it isn't referenced via PLT, the address of + the resolved function may be used. But in non-PIC executable, + the address of its plt slot may be used. Pointer equality may + not work correctly. PIE or non-PLT reference should be used if + pointer equality is required here. + + If STT_GNU_IFUNC symbol is defined in position-dependent executable, + backend should change it to the normal function and set its address + to its PLT entry which should be resolved by R_*_IRELATIVE at + run-time. All external references should be resolved to its PLT in + executable. */ + if (!need_dynreloc + && !(bfd_link_pde (info) && h->def_regular) + && (h->dynindx != -1 + || info->export_dynamic) + && h->pointer_equality_needed) + { + info->callbacks->einfo + /* xgettext:c-format. */ + (_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer " + "equality in `%pB' can not be used when making an " + "executable; recompile with -fPIE and relink with -pie\n"), + h->root.root.string, + h->root.u.def.section->owner); + bfd_set_error (bfd_error_bad_value); + return false; + } + + htab = elf_hash_table (info); + + /* When the symbol is marked with regular reference, if PLT isn't used + or we are building a PIC object, we must keep dynamic relocation + if there is non-GOT reference and use PLT if there is PC-relative + reference. */ + if (need_dynreloc && h->ref_regular) + { + bool keep = false; + for (p = *head; p != NULL; p = p->next) + if (p->count) + { + h->non_got_ref = 1; + /* Need dynamic relocations for non-GOT reference. */ + keep = true; + if (p->pc_count) + { + /* Must use PLT for PC-relative reference. */ + use_plt = true; + need_dynreloc = bfd_link_pic (info); + break; + } + } + if (keep) + goto keep; + } + + /* Support garbage collection against STT_GNU_IFUNC symbols. */ + if (h->plt.refcount <= 0 && h->got.refcount <= 0) + { + h->got = htab->init_got_offset; + h->plt = htab->init_plt_offset; + *head = NULL; + return true; + } + + /* Return and discard space for dynamic relocations against it if + it is never referenced. */ + if (!h->ref_regular) + { + if (h->plt.refcount > 0 + || h->got.refcount > 0) + abort (); + h->got = htab->init_got_offset; + h->plt = htab->init_plt_offset; + *head = NULL; + return true; + } + + keep: + bed = get_elf_backend_data (info->output_bfd); + if (bed->rela_plts_and_copies_p) + sizeof_reloc = bed->s->sizeof_rela; + else + sizeof_reloc = bed->s->sizeof_rel; + + /* When building a static executable, use iplt, igot.plt and + rel[a].iplt sections for STT_GNU_IFUNC symbols. */ + if (htab->splt != NULL) + { + plt = htab->splt; + gotplt = htab->sgotplt; + /* Change dynamic info of ifunc gotplt from srelplt to srelgot. */ + relplt = htab->srelgot; + + /* If this is the first plt entry and PLT is used, make room for + the special first entry. */ + if (plt->size == 0 && use_plt) + plt->size += plt_header_size; + } + else + { + plt = htab->iplt; + gotplt = htab->igotplt; + relplt = htab->irelplt; + } + + if (use_plt) + { + /* Don't update value of STT_GNU_IFUNC symbol to PLT. We need + the original value for R_*_IRELATIVE. */ + h->plt.offset = plt->size; + + /* Make room for this entry in the plt/iplt section. */ + plt->size += plt_entry_size; + + /* We also need to make an entry in the got.plt/got.iplt section, + which will be placed in the got section by the linker script. */ + gotplt->size += got_entry_size; + } + + /* We also need to make an entry in the rel[a].plt/.rel[a].iplt + section for GOTPLT relocation if PLT is used. */ + if (use_plt) + { + relplt->size += sizeof_reloc; + relplt->reloc_count++; + } + + /* We need dynamic relocation for STT_GNU_IFUNC symbol only when + there is a non-GOT reference in a PIC object or PLT isn't used. */ + if (!need_dynreloc || !h->non_got_ref) + *head = NULL; + + /* Finally, allocate space. */ + p = *head; + if (p != NULL) + { + bfd_size_type count = 0; + do + { + count += p->count; + p = p->next; + } + while (p != NULL); + + htab->ifunc_resolvers = count != 0; + + /* Dynamic relocations are stored in + 1. rel[a].srelgot section in PIC object. + 2. rel[a].srelgot section in dynamic executable. + 3. rel[a].irelplt section in static executable. */ + if (htab->splt != NULL) + htab->srelgot->size += count * sizeof_reloc; + else + { + relplt->size += count * sizeof_reloc; + relplt->reloc_count += count; + } + } + + /* For STT_GNU_IFUNC symbol, got.plt has the real function address + and got has the PLT entry adddress. We will load the GOT entry + with the PLT entry in finish_dynamic_symbol if it is used. For + branch, it uses got.plt. For symbol value, if PLT is used, + 1. Use got.plt in a PIC object if it is forced local or not + dynamic. + 2. Use got.plt in a non-PIC object if pointer equality isn't + needed. + 3. Use got.plt in PIE. + 4. Use got.plt if got isn't used. + 5. Otherwise use got so that it can be shared among different + objects at run-time. + If PLT isn't used, always use got for symbol value. + We only need to relocate got entry in PIC object or in dynamic + executable without PLT. */ + if (use_plt + && (h->got.refcount <= 0 + || (bfd_link_pic (info) + && (h->dynindx == -1 + || h->forced_local)) + || ( + !h->pointer_equality_needed) + || htab->sgot == NULL)) + { + /* Use got.plt. */ + h->got.offset = (bfd_vma) -1; + } + else + { + if (!use_plt) + { + /* PLT isn't used. */ + h->plt.offset = (bfd_vma) -1; + } + if (h->got.refcount <= 0) + { + /* GOT isn't need when there are only relocations for static + pointers. */ + h->got.offset = (bfd_vma) -1; + } + else + { + h->got.offset = htab->sgot->size; + htab->sgot->size += got_entry_size; + /* Need to relocate the GOT entry in a PIC object or PLT isn't + used. Otherwise, the GOT entry will be filled with the PLT + entry and dynamic GOT relocation isn't needed. */ + if (need_dynreloc) + { + /* For non-static executable, dynamic GOT relocation is in + rel[a].got section, but for static executable, it is + in rel[a].iplt section. */ + if (htab->splt != NULL) + htab->srelgot->size += sizeof_reloc; + else + { + relplt->size += sizeof_reloc; + relplt->reloc_count++; + } + } + } + } + + return true; +} + /* Allocate space in .plt, .got and associated reloc sections for ifunc dynamic relocs. */ @@ -1223,14 +1476,25 @@ elfNN_loongarch_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h, /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it here if it is defined and referenced in a non-shared object. */ - if (h->type == STT_GNU_IFUNC - && h->def_regular) - return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, + if (h->type == STT_GNU_IFUNC && h->def_regular) + { + if (SYMBOL_REFERENCES_LOCAL (info, h)) + return local_allocate_ifunc_dyn_relocs (info, h, &h->dyn_relocs, PLT_ENTRY_SIZE, PLT_HEADER_SIZE, GOT_ENTRY_SIZE, false); + else + return _bfd_elf_allocate_ifunc_dyn_relocs (info, h, + &h->dyn_relocs, + PLT_ENTRY_SIZE, + PLT_HEADER_SIZE, + GOT_ENTRY_SIZE, + false); + + } + return true; } @@ -2150,13 +2414,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); - /* Dynamic relocations are stored in - 1. .rela.ifunc section in PIC object. - 2. .rela.got section in dynamic executable. - 3. .rela.iplt section in static executable. */ - if (bfd_link_pic (info)) - sreloc = htab->elf.irelifunc; - else if (htab->elf.splt != NULL) + if (htab->elf.splt != NULL) sreloc = htab->elf.srelgot; else sreloc = htab->elf.irelplt; @@ -2427,7 +2685,9 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, if (h != NULL) { - off = h->got.offset; + bfd_vma alt_off = h->got.offset; + struct bfd_section *alt_got = got; + off = alt_off; if (off == MINUS_ONE && h->type != STT_GNU_IFUNC) @@ -2450,20 +2710,19 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, } bfd_vma plt_index = h->plt.offset / PLT_ENTRY_SIZE; - off = plt_index * GOT_ENTRY_SIZE; + alt_off = plt_index * GOT_ENTRY_SIZE; if (htab->elf.splt != NULL) { /* Section .plt header is 2 times of plt entry. */ - off = sec_addr(htab->elf.sgotplt) + off - - sec_addr(htab->elf.sgot); + alt_got = htab->elf.sgotplt; } else { /* Section iplt not has plt header. */ - off = sec_addr(htab->elf.igotplt) + off - - sec_addr(htab->elf.sgot); + alt_got = htab->elf.igotplt; } + off = sec_addr(alt_got) + alt_off - sec_addr(htab->elf.sgot); } if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (is_dyn, is_pic, h) @@ -2525,7 +2784,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, outrel.r_addend = relocation; /* Link-time addr. */ loongarch_elf_append_rela (output_bfd, s, &outrel); } - bfd_put_NN (output_bfd, relocation, got->contents + off); + bfd_put_NN (output_bfd, relocation, alt_got->contents + alt_off); h->got.offset |= 1; } } @@ -2860,7 +3119,10 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd, plt = htab->elf.splt; gotplt = htab->elf.sgotplt; - relplt = htab->elf.srelplt; + if (h->type == STT_GNU_IFUNC && SYMBOL_REFERENCES_LOCAL (info, h)) + relplt = htab->elf.srelgot; + else + relplt = htab->elf.srelplt; plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE; got_address = sec_addr (gotplt) + GOTPLT_HEADER_SIZE + plt_idx * GOT_ENTRY_SIZE; @@ -2896,23 +3158,45 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd, rela.r_offset = got_address; /* TRUE if this is a PLT reference to a local IFUNC. */ - if (PLT_LOCAL_IFUNC_P(info, h)) + if (PLT_LOCAL_IFUNC_P (info, h) + && (relplt == htab->elf.srelgot + || relplt == htab->elf.irelplt)) { - rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE); - rela.r_addend = (h->root.u.def.value - + h->root.u.def.section->output_section->vma - + h->root.u.def.section->output_offset); + { + rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE); + rela.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + + /* Find the space after dyn sort. */ + { + Elf_Internal_Rela *dyn = (Elf_Internal_Rela *)relplt->contents; + bool fill = false; + for (;dyn < dyn + relplt->size / sizeof (*dyn); dyn++) + { + if (0 == dyn->r_offset) + { + bed->s->swap_reloca_out (output_bfd, &rela, + (bfd_byte *)dyn); + relplt->reloc_count++; + fill = true; + break; + } + } + BFD_ASSERT (fill); + } + } else { - /* Fill in the entry in the .rela.plt section. */ + /* Fill in the entry in the rela.plt section. */ rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_JUMP_SLOT); rela.r_addend = 0; + loc = relplt->contents + plt_idx * sizeof (ElfNN_External_Rela); + bed->s->swap_reloca_out (output_bfd, &rela, loc); } - loc = relplt->contents + plt_idx * sizeof (ElfNN_External_Rela); - bed->s->swap_reloca_out (output_bfd, &rela, loc); - if (!h->def_regular) { /* Mark the symbol as undefined, rather than as defined in -- 2.37.0