/* Helper to size leading part of tv array and set it up. */ static void set_tv_header (struct ld_plugin_tv *tv) { size_t i; /* Version info. */ static const unsigned int major = (unsigned)(BFD_VERSION / 100000000UL); static const unsigned int minor = (unsigned)(BFD_VERSION / 1000000UL) % 100; for (i = 0; i < tv_header_size; i++) { tv[i].tv_tag = tv_header_tags[i]; #define TVU(x) tv[i].tv_u.tv_ ## x switch (tv[i].tv_tag) { case LDPT_MESSAGE: TVU(message) = message; break; case LDPT_API_VERSION: TVU(val) = LD_PLUGIN_API_VERSION; break; case LDPT_GNU_LD_VERSION: TVU(val) = major * 100 + minor; break; case LDPT_LINKER_OUTPUT: TVU(val) = (bfd_link_relocatable (&link_info) ? LDPO_REL : bfd_link_pde (&link_info) ? LDPO_EXEC : bfd_link_pie (&link_info) ? LDPO_PIE : LDPO_DYN); break; case LDPT_OUTPUT_NAME: TVU(string) = output_filename; break; case LDPT_REGISTER_CLAIM_FILE_HOOK: TVU(register_claim_file) = register_claim_file; break; case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: TVU(register_all_symbols_read) = register_all_symbols_read; break; case LDPT_REGISTER_CLEANUP_HOOK: TVU(register_cleanup) = register_cleanup; break; case LDPT_ADD_SYMBOLS: TVU(add_symbols) = add_symbols; break; case LDPT_GET_INPUT_FILE: TVU(get_input_file) = get_input_file; break; case LDPT_GET_VIEW: TVU(get_view) = get_view; break; case LDPT_RELEASE_INPUT_FILE: TVU(release_input_file) = release_input_file; break; case LDPT_GET_SYMBOLS: TVU(get_symbols) = get_symbols_v1; break; case LDPT_GET_SYMBOLS_V2: TVU(get_symbols) = get_symbols_v2; break; case LDPT_ADD_INPUT_FILE: TVU(add_input_file) = add_input_file; break; case LDPT_ADD_INPUT_LIBRARY: TVU(add_input_library) = add_input_library; break; case LDPT_SET_EXTRA_LIBRARY_PATH: TVU(set_extra_library_path) = set_extra_library_path; break; default: /* Added a new entry to the array without adding a new case to set up its value is a bug. */ FAIL (); } #undef TVU } }
static bfd_boolean s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, struct elf_link_hash_entry *h) { struct elf_dyn_relocs *p; struct elf_link_hash_table *htab; struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h; struct elf_dyn_relocs **head = &eh->dyn_relocs; htab = elf_hash_table (info); eh->ifunc_resolver_address = h->root.u.def.value; eh->ifunc_resolver_section = h->root.u.def.section; /* Support garbage collection against STT_GNU_IFUNC symbols. */ if (h->plt.refcount <= 0 && h->got.refcount <= 0) { /* When building shared library, we need to handle the case where it is marked with regular reference, but not non-GOT reference. It may happen if we didn't see STT_GNU_IFUNC symbol at the time when checking relocations. */ if (bfd_link_pic (info) && !h->non_got_ref && h->ref_regular) for (p = *head; p != NULL; p = p->next) if (p->count) { h->non_got_ref = 1; goto keep; } 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 in a non-shared object. */ 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: /* Without checking h->plt.refcount here we allocate a PLT slot. When setting plt.refcount in check_relocs it might not have been known that this will be an IFUNC symol. */ h->plt.offset = htab->iplt->size; h->needs_plt = 1; htab->iplt->size += PLT_ENTRY_SIZE; htab->igotplt->size += GOT_ENTRY_SIZE; htab->irelplt->size += RELA_ENTRY_SIZE; htab->irelplt->reloc_count++; /* In order to make pointer equality work with IFUNC symbols defined in a non-PIE executable and referenced in a shared lib, we turn the symbol into a STT_FUNC symbol and make the symbol value to point to the IPLT slot. That way the referencing shared lib will always get the PLT slot address when resolving the respective R_390_GLOB_DAT/R_390_64 relocs on that symbol. */ if (bfd_link_pde (info) && h->def_regular && h->ref_dynamic) { h->root.u.def.section = htab->iplt; h->root.u.def.value = h->plt.offset; h->size = PLT_ENTRY_SIZE; h->type = STT_FUNC; } if (!bfd_link_pic (info)) *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->irelifunc->size += count * RELA_ENTRY_SIZE; } /* Decide whether the got.iplt slot can be used. This has to be avoided if the values in the GOT slots could differ for pointer equality reasons. */ if (h->got.refcount <= 0 || (bfd_link_pic (info) && (h->dynindx == -1 || h->forced_local)) || bfd_link_pie (info) || htab->sgot == NULL) { /* Use .got.iplt. */ h->got.offset = (bfd_vma) -1; } else { h->got.offset = htab->sgot->size; htab->sgot->size += GOT_ENTRY_SIZE; if (bfd_link_pic (info)) htab->srelgot->size += RELA_ENTRY_SIZE; } return TRUE; }
bfd_boolean _bfd_elf_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) { 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; /* When a shared library references a STT_GNU_IFUNC symbol defined in executable, the address of the resolved function may be used. But in non-shared executable, the address of its .plt slot may be used. Pointer equality may not work correctly. PIE should be used if pointer equality is required here. */ if (!bfd_link_pic (info) && (h->dynindx != -1 || info->export_dynamic) && h->pointer_equality_needed) { info->callbacks->einfo (_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer " "equality in `%B' 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 building shared library, we need to handle the case where it is marked with regular reference, but not non-GOT reference since the non-GOT reference bit may not be set here. */ if (bfd_link_pic (info) && !h->non_got_ref && h->ref_regular) for (p = *head; p != NULL; p = p->next) if (p->count) { h->non_got_ref = 1; 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 in a non-shared object. */ 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; relplt = htab->srelplt; /* If this is the first .plt entry, make room for the special first entry. */ if (plt->size == 0) plt->size += plt_header_size; } else { plt = htab->iplt; gotplt = htab->igotplt; relplt = htab->irelplt; } /* 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. */ 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 shared object. */ if (!bfd_link_pic (info) || !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->irelifunc->size += count * sizeof_reloc; } /* 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, 1. Use .got.plt in a shared object if it is forced local or not dynamic. 2. Use .got.plt in a non-shared 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. We only need to relocate .got entry in shared object. */ if (h->got.refcount <= 0 || (bfd_link_pic (info) && (h->dynindx == -1 || h->forced_local)) || (!bfd_link_pic (info) && !h->pointer_equality_needed) || bfd_link_pie (info) || htab->sgot == NULL) { /* Use .got.plt. */ h->got.offset = (bfd_vma) -1; } else { h->got.offset = htab->sgot->size; htab->sgot->size += got_entry_size; if (bfd_link_pic (info)) htab->srelgot->size += sizeof_reloc; } return TRUE; }