15796c8dcSSimon Schubert /* ELF STT_GNU_IFUNC support.
25796c8dcSSimon Schubert Copyright 2009
35796c8dcSSimon Schubert Free Software Foundation, Inc.
45796c8dcSSimon Schubert
55796c8dcSSimon Schubert This file is part of BFD, the Binary File Descriptor library.
65796c8dcSSimon Schubert
75796c8dcSSimon Schubert This program is free software; you can redistribute it and/or modify
85796c8dcSSimon Schubert it under the terms of the GNU General Public License as published by
95796c8dcSSimon Schubert the Free Software Foundation; either version 3 of the License, or
105796c8dcSSimon Schubert (at your option) any later version.
115796c8dcSSimon Schubert
125796c8dcSSimon Schubert This program is distributed in the hope that it will be useful,
135796c8dcSSimon Schubert but WITHOUT ANY WARRANTY; without even the implied warranty of
145796c8dcSSimon Schubert MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
155796c8dcSSimon Schubert GNU General Public License for more details.
165796c8dcSSimon Schubert
175796c8dcSSimon Schubert You should have received a copy of the GNU General Public License
185796c8dcSSimon Schubert along with this program; if not, write to the Free Software
195796c8dcSSimon Schubert Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
205796c8dcSSimon Schubert MA 02110-1301, USA. */
215796c8dcSSimon Schubert
225796c8dcSSimon Schubert #include "sysdep.h"
235796c8dcSSimon Schubert #include "bfd.h"
245796c8dcSSimon Schubert #include "bfdlink.h"
255796c8dcSSimon Schubert #include "libbfd.h"
265796c8dcSSimon Schubert #define ARCH_SIZE 0
275796c8dcSSimon Schubert #include "elf-bfd.h"
285796c8dcSSimon Schubert #include "safe-ctype.h"
295796c8dcSSimon Schubert #include "libiberty.h"
305796c8dcSSimon Schubert #include "objalloc.h"
315796c8dcSSimon Schubert
325796c8dcSSimon Schubert /* Create sections needed by STT_GNU_IFUNC symbol. */
335796c8dcSSimon Schubert
345796c8dcSSimon Schubert bfd_boolean
_bfd_elf_create_ifunc_sections(bfd * abfd,struct bfd_link_info * info)355796c8dcSSimon Schubert _bfd_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
365796c8dcSSimon Schubert {
375796c8dcSSimon Schubert flagword flags, pltflags;
385796c8dcSSimon Schubert asection *s;
395796c8dcSSimon Schubert const struct elf_backend_data *bed = get_elf_backend_data (abfd);
405796c8dcSSimon Schubert struct elf_link_hash_table *htab = elf_hash_table (info);
415796c8dcSSimon Schubert
425796c8dcSSimon Schubert if (htab->irelifunc != NULL || htab->iplt != NULL)
435796c8dcSSimon Schubert return TRUE;
445796c8dcSSimon Schubert
455796c8dcSSimon Schubert flags = bed->dynamic_sec_flags;
465796c8dcSSimon Schubert pltflags = flags;
475796c8dcSSimon Schubert if (bed->plt_not_loaded)
485796c8dcSSimon Schubert /* We do not clear SEC_ALLOC here because we still want the OS to
495796c8dcSSimon Schubert allocate space for the section; it's just that there's nothing
505796c8dcSSimon Schubert to read in from the object file. */
515796c8dcSSimon Schubert pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS);
525796c8dcSSimon Schubert else
535796c8dcSSimon Schubert pltflags |= SEC_ALLOC | SEC_CODE | SEC_LOAD;
545796c8dcSSimon Schubert if (bed->plt_readonly)
555796c8dcSSimon Schubert pltflags |= SEC_READONLY;
565796c8dcSSimon Schubert
575796c8dcSSimon Schubert if (info->shared)
585796c8dcSSimon Schubert {
595796c8dcSSimon Schubert /* We need to create .rel[a].ifunc for shared objects. */
605796c8dcSSimon Schubert const char *rel_sec = (bed->rela_plts_and_copies_p
615796c8dcSSimon Schubert ? ".rela.ifunc" : ".rel.ifunc");
625796c8dcSSimon Schubert
635796c8dcSSimon Schubert s = bfd_make_section_with_flags (abfd, rel_sec,
645796c8dcSSimon Schubert flags | SEC_READONLY);
655796c8dcSSimon Schubert if (s == NULL
665796c8dcSSimon Schubert || ! bfd_set_section_alignment (abfd, s,
675796c8dcSSimon Schubert bed->s->log_file_align))
685796c8dcSSimon Schubert return FALSE;
695796c8dcSSimon Schubert htab->irelifunc = s;
705796c8dcSSimon Schubert }
715796c8dcSSimon Schubert else
725796c8dcSSimon Schubert {
735796c8dcSSimon Schubert /* We need to create .iplt, .rel[a].iplt, .igot and .igot.plt
745796c8dcSSimon Schubert for static executables. */
755796c8dcSSimon Schubert s = bfd_make_section_with_flags (abfd, ".iplt", pltflags);
765796c8dcSSimon Schubert if (s == NULL
775796c8dcSSimon Schubert || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
785796c8dcSSimon Schubert return FALSE;
795796c8dcSSimon Schubert htab->iplt = s;
805796c8dcSSimon Schubert
815796c8dcSSimon Schubert s = bfd_make_section_with_flags (abfd,
825796c8dcSSimon Schubert (bed->rela_plts_and_copies_p
835796c8dcSSimon Schubert ? ".rela.iplt" : ".rel.iplt"),
845796c8dcSSimon Schubert flags | SEC_READONLY);
855796c8dcSSimon Schubert if (s == NULL
865796c8dcSSimon Schubert || ! bfd_set_section_alignment (abfd, s,
875796c8dcSSimon Schubert bed->s->log_file_align))
885796c8dcSSimon Schubert return FALSE;
895796c8dcSSimon Schubert htab->irelplt = s;
905796c8dcSSimon Schubert
915796c8dcSSimon Schubert /* We don't need the .igot section if we have the .igot.plt
925796c8dcSSimon Schubert section. */
935796c8dcSSimon Schubert if (bed->want_got_plt)
945796c8dcSSimon Schubert s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
955796c8dcSSimon Schubert else
965796c8dcSSimon Schubert s = bfd_make_section_with_flags (abfd, ".igot", flags);
975796c8dcSSimon Schubert if (s == NULL
985796c8dcSSimon Schubert || !bfd_set_section_alignment (abfd, s,
995796c8dcSSimon Schubert bed->s->log_file_align))
1005796c8dcSSimon Schubert return FALSE;
1015796c8dcSSimon Schubert htab->igotplt = s;
1025796c8dcSSimon Schubert }
1035796c8dcSSimon Schubert
1045796c8dcSSimon Schubert return TRUE;
1055796c8dcSSimon Schubert }
1065796c8dcSSimon Schubert
1075796c8dcSSimon Schubert /* For a STT_GNU_IFUNC symbol, create a dynamic reloc section, SRELOC,
1085796c8dcSSimon Schubert for the input section, SEC, and append this reloc to HEAD. */
1095796c8dcSSimon Schubert
1105796c8dcSSimon Schubert asection *
_bfd_elf_create_ifunc_dyn_reloc(bfd * abfd,struct bfd_link_info * info,asection * sec,asection * sreloc,struct elf_dyn_relocs ** head)1115796c8dcSSimon Schubert _bfd_elf_create_ifunc_dyn_reloc (bfd *abfd, struct bfd_link_info *info,
1125796c8dcSSimon Schubert asection *sec, asection *sreloc,
1135796c8dcSSimon Schubert struct elf_dyn_relocs **head)
1145796c8dcSSimon Schubert {
1155796c8dcSSimon Schubert struct elf_dyn_relocs *p;
1165796c8dcSSimon Schubert struct elf_link_hash_table *htab = elf_hash_table (info);
1175796c8dcSSimon Schubert
1185796c8dcSSimon Schubert if (sreloc == NULL)
1195796c8dcSSimon Schubert {
1205796c8dcSSimon Schubert const struct elf_backend_data *bed = get_elf_backend_data (abfd);
1215796c8dcSSimon Schubert
1225796c8dcSSimon Schubert if (htab->dynobj == NULL)
1235796c8dcSSimon Schubert htab->dynobj = abfd;
1245796c8dcSSimon Schubert
1255796c8dcSSimon Schubert sreloc = _bfd_elf_make_dynamic_reloc_section (sec, htab->dynobj,
1265796c8dcSSimon Schubert bed->s->log_file_align,
1275796c8dcSSimon Schubert abfd,
1285796c8dcSSimon Schubert bed->rela_plts_and_copies_p);
1295796c8dcSSimon Schubert if (sreloc == NULL)
1305796c8dcSSimon Schubert return NULL;
1315796c8dcSSimon Schubert }
1325796c8dcSSimon Schubert
1335796c8dcSSimon Schubert p = *head;
1345796c8dcSSimon Schubert if (p == NULL || p->sec != sec)
1355796c8dcSSimon Schubert {
1365796c8dcSSimon Schubert bfd_size_type amt = sizeof *p;
1375796c8dcSSimon Schubert
1385796c8dcSSimon Schubert p = ((struct elf_dyn_relocs *) bfd_alloc (htab->dynobj, amt));
1395796c8dcSSimon Schubert if (p == NULL)
1405796c8dcSSimon Schubert return NULL;
1415796c8dcSSimon Schubert p->next = *head;
1425796c8dcSSimon Schubert *head = p;
1435796c8dcSSimon Schubert p->sec = sec;
1445796c8dcSSimon Schubert p->count = 0;
1455796c8dcSSimon Schubert p->pc_count = 0;
1465796c8dcSSimon Schubert }
1475796c8dcSSimon Schubert p->count += 1;
1485796c8dcSSimon Schubert
1495796c8dcSSimon Schubert return sreloc;
1505796c8dcSSimon Schubert }
1515796c8dcSSimon Schubert
1525796c8dcSSimon Schubert /* Allocate space in .plt, .got and associated reloc sections for
1535796c8dcSSimon Schubert dynamic relocs against a STT_GNU_IFUNC symbol definition. */
1545796c8dcSSimon Schubert
1555796c8dcSSimon Schubert 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 got_entry_size)1565796c8dcSSimon Schubert _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
1575796c8dcSSimon Schubert struct elf_link_hash_entry *h,
1585796c8dcSSimon Schubert struct elf_dyn_relocs **head,
1595796c8dcSSimon Schubert unsigned int plt_entry_size,
1605796c8dcSSimon Schubert unsigned int got_entry_size)
1615796c8dcSSimon Schubert {
1625796c8dcSSimon Schubert asection *plt, *gotplt, *relplt;
1635796c8dcSSimon Schubert struct elf_dyn_relocs *p;
1645796c8dcSSimon Schubert unsigned int sizeof_reloc;
1655796c8dcSSimon Schubert const struct elf_backend_data *bed;
1665796c8dcSSimon Schubert struct elf_link_hash_table *htab;
1675796c8dcSSimon Schubert
1685796c8dcSSimon Schubert /* When a shared library references a STT_GNU_IFUNC symbol defined
1695796c8dcSSimon Schubert in executable, the address of the resolved function may be used.
1705796c8dcSSimon Schubert But in non-shared executable, the address of its .plt slot may
1715796c8dcSSimon Schubert be used. Pointer equality may not work correctly. PIE should
1725796c8dcSSimon Schubert be used if pointer equality is required here. */
1735796c8dcSSimon Schubert if (!info->shared
1745796c8dcSSimon Schubert && (h->dynindx != -1
1755796c8dcSSimon Schubert || info->export_dynamic)
1765796c8dcSSimon Schubert && h->pointer_equality_needed)
1775796c8dcSSimon Schubert {
1785796c8dcSSimon Schubert info->callbacks->einfo
1795796c8dcSSimon Schubert (_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer "
1805796c8dcSSimon Schubert "equality in `%B' can not be used when making an "
1815796c8dcSSimon Schubert "executable; recompile with -fPIE and relink with -pie\n"),
1825796c8dcSSimon Schubert h->root.root.string,
1835796c8dcSSimon Schubert h->root.u.def.section->owner);
1845796c8dcSSimon Schubert bfd_set_error (bfd_error_bad_value);
1855796c8dcSSimon Schubert return FALSE;
1865796c8dcSSimon Schubert }
1875796c8dcSSimon Schubert
1885796c8dcSSimon Schubert htab = elf_hash_table (info);
1895796c8dcSSimon Schubert
190c50c785cSJohn Marino /* Support garbage collection against STT_GNU_IFUNC symbols. */
191c50c785cSJohn Marino if (h->plt.refcount <= 0 && h->got.refcount <= 0)
192c50c785cSJohn Marino {
193c50c785cSJohn Marino /* When building shared library, we need to handle the case
194c50c785cSJohn Marino where it is marked with regular reference, but not non-GOT
195c50c785cSJohn Marino reference. It may happen if we didn't see STT_GNU_IFUNC
196c50c785cSJohn Marino symbol at the time when checking relocations. */
197c50c785cSJohn Marino if (info->shared
198c50c785cSJohn Marino && !h->non_got_ref
199c50c785cSJohn Marino && h->ref_regular)
200c50c785cSJohn Marino for (p = *head; p != NULL; p = p->next)
201c50c785cSJohn Marino if (p->count)
202c50c785cSJohn Marino {
203c50c785cSJohn Marino h->non_got_ref = 1;
204c50c785cSJohn Marino goto keep;
205c50c785cSJohn Marino }
206c50c785cSJohn Marino
207c50c785cSJohn Marino h->got = htab->init_got_offset;
208c50c785cSJohn Marino h->plt = htab->init_plt_offset;
209c50c785cSJohn Marino *head = NULL;
210c50c785cSJohn Marino return TRUE;
211c50c785cSJohn Marino }
212c50c785cSJohn Marino
2135796c8dcSSimon Schubert /* Return and discard space for dynamic relocations against it if
2145796c8dcSSimon Schubert it is never referenced in a non-shared object. */
2155796c8dcSSimon Schubert if (!h->ref_regular)
2165796c8dcSSimon Schubert {
2175796c8dcSSimon Schubert if (h->plt.refcount > 0
2185796c8dcSSimon Schubert || h->got.refcount > 0)
2195796c8dcSSimon Schubert abort ();
2205796c8dcSSimon Schubert h->got = htab->init_got_offset;
2215796c8dcSSimon Schubert h->plt = htab->init_plt_offset;
2225796c8dcSSimon Schubert *head = NULL;
2235796c8dcSSimon Schubert return TRUE;
2245796c8dcSSimon Schubert }
2255796c8dcSSimon Schubert
226c50c785cSJohn Marino keep:
2275796c8dcSSimon Schubert bed = get_elf_backend_data (info->output_bfd);
2285796c8dcSSimon Schubert if (bed->rela_plts_and_copies_p)
2295796c8dcSSimon Schubert sizeof_reloc = bed->s->sizeof_rela;
2305796c8dcSSimon Schubert else
2315796c8dcSSimon Schubert sizeof_reloc = bed->s->sizeof_rel;
2325796c8dcSSimon Schubert
2335796c8dcSSimon Schubert /* When building a static executable, use .iplt, .igot.plt and
2345796c8dcSSimon Schubert .rel[a].iplt sections for STT_GNU_IFUNC symbols. */
2355796c8dcSSimon Schubert if (htab->splt != NULL)
2365796c8dcSSimon Schubert {
2375796c8dcSSimon Schubert plt = htab->splt;
2385796c8dcSSimon Schubert gotplt = htab->sgotplt;
2395796c8dcSSimon Schubert relplt = htab->srelplt;
2405796c8dcSSimon Schubert
2415796c8dcSSimon Schubert /* If this is the first .plt entry, make room for the special
2425796c8dcSSimon Schubert first entry. */
2435796c8dcSSimon Schubert if (plt->size == 0)
2445796c8dcSSimon Schubert plt->size += plt_entry_size;
2455796c8dcSSimon Schubert }
2465796c8dcSSimon Schubert else
2475796c8dcSSimon Schubert {
2485796c8dcSSimon Schubert plt = htab->iplt;
2495796c8dcSSimon Schubert gotplt = htab->igotplt;
2505796c8dcSSimon Schubert relplt = htab->irelplt;
2515796c8dcSSimon Schubert }
2525796c8dcSSimon Schubert
2535796c8dcSSimon Schubert /* Don't update value of STT_GNU_IFUNC symbol to PLT. We need
2545796c8dcSSimon Schubert the original value for R_*_IRELATIVE. */
2555796c8dcSSimon Schubert h->plt.offset = plt->size;
2565796c8dcSSimon Schubert
2575796c8dcSSimon Schubert /* Make room for this entry in the .plt/.iplt section. */
2585796c8dcSSimon Schubert plt->size += plt_entry_size;
2595796c8dcSSimon Schubert
2605796c8dcSSimon Schubert /* We also need to make an entry in the .got.plt/.got.iplt section,
2615796c8dcSSimon Schubert which will be placed in the .got section by the linker script. */
2625796c8dcSSimon Schubert gotplt->size += got_entry_size;
2635796c8dcSSimon Schubert
2645796c8dcSSimon Schubert /* We also need to make an entry in the .rel[a].plt/.rel[a].iplt
2655796c8dcSSimon Schubert section. */
2665796c8dcSSimon Schubert relplt->size += sizeof_reloc;
2675796c8dcSSimon Schubert relplt->reloc_count++;
2685796c8dcSSimon Schubert
2695796c8dcSSimon Schubert /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
2705796c8dcSSimon Schubert there is a non-GOT reference in a shared object. */
2715796c8dcSSimon Schubert if (!info->shared
2725796c8dcSSimon Schubert || !h->non_got_ref)
2735796c8dcSSimon Schubert *head = NULL;
2745796c8dcSSimon Schubert
2755796c8dcSSimon Schubert /* Finally, allocate space. */
276c50c785cSJohn Marino p = *head;
277c50c785cSJohn Marino if (p != NULL)
278c50c785cSJohn Marino {
279c50c785cSJohn Marino bfd_size_type count = 0;
280c50c785cSJohn Marino do
281c50c785cSJohn Marino {
282c50c785cSJohn Marino count += p->count;
283c50c785cSJohn Marino p = p->next;
284c50c785cSJohn Marino }
285c50c785cSJohn Marino while (p != NULL);
286c50c785cSJohn Marino htab->irelifunc->size += count * sizeof_reloc;
287c50c785cSJohn Marino }
2885796c8dcSSimon Schubert
289c50c785cSJohn Marino /* For STT_GNU_IFUNC symbol, .got.plt has the real function address
2905796c8dcSSimon Schubert and .got has the PLT entry adddress. We will load the GOT entry
2915796c8dcSSimon Schubert with the PLT entry in finish_dynamic_symbol if it is used. For
2925796c8dcSSimon Schubert branch, it uses .got.plt. For symbol value,
2935796c8dcSSimon Schubert 1. Use .got.plt in a shared object if it is forced local or not
2945796c8dcSSimon Schubert dynamic.
2955796c8dcSSimon Schubert 2. Use .got.plt in a non-shared object if pointer equality isn't
2965796c8dcSSimon Schubert needed.
2975796c8dcSSimon Schubert 3. Use .got.plt in PIE.
2985796c8dcSSimon Schubert 4. Use .got.plt if .got isn't used.
2995796c8dcSSimon Schubert 5. Otherwise use .got so that it can be shared among different
3005796c8dcSSimon Schubert objects at run-time.
3015796c8dcSSimon Schubert We only need to relocate .got entry in shared object. */
302*a45ae5f8SJohn Marino if (h->got.refcount <= 0
303*a45ae5f8SJohn Marino || (info->shared
3045796c8dcSSimon Schubert && (h->dynindx == -1
3055796c8dcSSimon Schubert || h->forced_local))
3065796c8dcSSimon Schubert || (!info->shared
3075796c8dcSSimon Schubert && !h->pointer_equality_needed)
3085796c8dcSSimon Schubert || (info->executable && info->shared)
3095796c8dcSSimon Schubert || htab->sgot == NULL)
3105796c8dcSSimon Schubert {
3115796c8dcSSimon Schubert /* Use .got.plt. */
3125796c8dcSSimon Schubert h->got.offset = (bfd_vma) -1;
3135796c8dcSSimon Schubert }
3145796c8dcSSimon Schubert else
3155796c8dcSSimon Schubert {
3165796c8dcSSimon Schubert h->got.offset = htab->sgot->size;
3175796c8dcSSimon Schubert htab->sgot->size += got_entry_size;
3185796c8dcSSimon Schubert if (info->shared)
3195796c8dcSSimon Schubert htab->srelgot->size += sizeof_reloc;
3205796c8dcSSimon Schubert }
3215796c8dcSSimon Schubert
3225796c8dcSSimon Schubert return TRUE;
3235796c8dcSSimon Schubert }
324