This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
powerpc STT_GNU_IFUNC support, 1 of 2
- From: Alan Modra <amodra at bigpond dot net dot au>
- To: libc-alpha at sourceware dot org
- Cc: Steven Munroe <sjmunroe at us dot ibm dot com>
- Date: Thu, 30 Jul 2009 22:13:50 +0930
- Subject: powerpc STT_GNU_IFUNC support, 1 of 2
This patch implements ld.so STT_GNU_IFUNC support for powerpc and
powerpc64. A followup patch modifies the ifunc testcases for
powerpc, which then all pass on powerpc64. powerpc fails any test
that has non-pic indirect function addresses, ifuncmain1static,
ifuncmain4static, ifuncmain5static, ifuncmain7static, ifuncmain4, and
ifuncmain7. All others pass. For powerpc, non-pic function pointers
are formed in code using addis, addi insn pairs. If we were to allow
ifunc pointers then we'd need dynamic text relocs, and thus at some
point during ld.so processing a writable (to update the insns) and
executable (to run the ifunc) text segment. I figure that isn't
acceptable, so ld complains at link time.
2009-07-30 Alan Modra <amodra@bigpond.net.au>
* elf/elf.h (R_PPC_NUM, R_PPC64_NUM): Delete unused and incorrect.
(R_PPC_REL16*): Correct comments.
(R_PPC_IRELATIVE, R_PPC64_IRELATIVE, R_PPC64_JMP_IREL): Define.
(R_PPC64_REL16, R_PPC64_REL16_LO, R_PPC64_REL16_HI,
R_PPC64_REL16_HA): Define.
* sysdeps/powerpc/powerpc32/dl-irel.h: New file.
* sysdeps/powerpc/powerpc64/dl-irel.h: New file.
* sysdeps/powerpc/powerpc32/dl-machine.c (__elf_machine_fixup_plt):
Delete unused "reloc" param.
(__process_machine_rela): Handle R_PPC_IRELATIVE.
* sysdeps/powerpc/powerpc32/dl-machine.h (__elf_machine_fixup_plt):
Delete "reloc" param.
(elf_machine_rela): Handle STT_GNU_IFUNC functions and
R_PPC_IRELATIVE.
* sysdeps/powerpc/powerpc64/dl-machine.h (resolve_ifunc): New function.
(elf_machine_rela): Handle STT_GNU_IFUNC functions and new ifunc
relocations.
diff --git a/elf/elf.h b/elf/elf.h
index 7efdede..220eb45 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -2038,9 +2038,6 @@ typedef Elf32_Addr Elf32_Conflict;
#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */
#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */
-/* Keep this the last entry. */
-#define R_PPC_NUM 95
-
/* The remaining relocs are from the Embedded ELF ABI, and are not
in the SVR4 ELF ABI. */
#define R_PPC_EMB_NADDR32 101
@@ -2068,11 +2065,14 @@ typedef Elf32_Addr Elf32_Conflict;
#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
+/* GNU extension to support local ifunc. */
+#define R_PPC_IRELATIVE 248
+
/* GNU relocs used in PIC code sequences. */
-#define R_PPC_REL16 249 /* word32 (sym-.) */
-#define R_PPC_REL16_LO 250 /* half16 (sym-.)@l */
-#define R_PPC_REL16_HI 251 /* half16 (sym-.)@h */
-#define R_PPC_REL16_HA 252 /* half16 (sym-.)@ha */
+#define R_PPC_REL16 249 /* half16 (sym+add-.) */
+#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */
+#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */
+#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */
/* This is a phony reloc to handle any old fashioned TOC16 references
that may still be in object files. */
@@ -2194,8 +2194,13 @@ typedef Elf32_Addr Elf32_Conflict;
#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */
#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */
-/* Keep this the last entry. */
-#define R_PPC64_NUM 107
+/* GNU extension to support local ifunc. */
+#define R_PPC64_JMP_IREL 247
+#define R_PPC64_IRELATIVE 248
+#define R_PPC64_REL16 249 /* half16 (sym+add-.) */
+#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */
+#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */
+#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */
/* PowerPC64 specific values for the Dyn d_tag field. */
#define DT_PPC64_GLINK (DT_LOPROC + 0)
diff --git a/sysdeps/powerpc/powerpc32/dl-irel.h b/sysdeps/powerpc/powerpc32/dl-irel.h
new file mode 100644
index 0000000..3f204cd
--- /dev/null
+++ b/sysdeps/powerpc/powerpc32/dl-irel.h
@@ -0,0 +1,45 @@
+/* Machine-dependent ELF indirect relocation inline functions.
+ PowerPC version.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _DL_IREL_H
+#define _DL_IREL_H
+
+#include <stdio.h>
+#include <unistd.h>
+
+#define ELF_MACHINE_IRELA 1
+
+static inline void
+__attribute ((always_inline))
+elf_irela (const Elf32_Rela *reloc)
+{
+ unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
+
+ if (__builtin_expect (r_type == R_PPC_IRELATIVE, 1))
+ {
+ Elf32_Addr *const reloc_addr = (void *) reloc->r_offset;
+ Elf32_Addr value = ((Elf32_Addr (*) (void)) reloc->r_addend) ();
+ *reloc_addr = value;
+ }
+ else
+ __libc_fatal ("unexpected reloc type in static binary");
+}
+
+#endif /* dl-irel.h */
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.c b/sysdeps/powerpc/powerpc32/dl-machine.c
index 71540bd..ee4c3e0 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.c
+++ b/sysdeps/powerpc/powerpc32/dl-machine.c
@@ -337,7 +337,7 @@ __elf_machine_runtime_setup (struct link_map *map, int lazy, int profile)
}
Elf32_Addr
-__elf_machine_fixup_plt (struct link_map *map, const Elf32_Rela *reloc,
+__elf_machine_fixup_plt (struct link_map *map,
Elf32_Addr *reloc_addr, Elf32_Addr finaladdr)
{
Elf32_Sword delta = finaladdr - (Elf32_Word) reloc_addr;
@@ -430,6 +430,10 @@ __process_machine_rela (struct link_map *map,
*reloc_addr = finaladdr;
return;
+ case R_PPC_IRELATIVE:
+ *reloc_addr = ((Elf32_Addr (*) (void)) finaladdr) ();
+ return;
+
case R_PPC_UADDR32:
((char *) reloc_addr)[0] = finaladdr >> 24;
((char *) reloc_addr)[1] = finaladdr >> 16;
diff --git a/sysdeps/powerpc/powerpc32/dl-machine.h b/sysdeps/powerpc/powerpc32/dl-machine.h
index a50ffdd..6f8d0f5 100644
--- a/sysdeps/powerpc/powerpc32/dl-machine.h
+++ b/sysdeps/powerpc/powerpc32/dl-machine.h
@@ -226,7 +226,6 @@ elf_machine_runtime_setup (struct link_map *map,
/* Change the PLT entry whose reloc is 'reloc' to call the actual routine. */
extern Elf32_Addr __elf_machine_fixup_plt (struct link_map *map,
- const Elf32_Rela *reloc,
Elf32_Addr *reloc_addr,
Elf32_Addr finaladdr);
@@ -237,7 +236,7 @@ elf_machine_fixup_plt (struct link_map *map, lookup_t t,
{
if (map->l_info[DT_PPC(GOT)] == 0)
/* Handle old style PLT. */
- return __elf_machine_fixup_plt (map, reloc, reloc_addr, finaladdr);
+ return __elf_machine_fixup_plt (map, reloc_addr, finaladdr);
*reloc_addr = finaladdr;
return finaladdr;
@@ -317,6 +316,11 @@ elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
value = reloc->r_addend;
#endif
+ if (sym != NULL
+ && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)
+ && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1))
+ value = ((Elf32_Addr (*) (void)) value) ();
+
/* A small amount of code is duplicated here for speed. In libc,
more than 90% of the relocs are R_PPC_RELATIVE; in the X11 shared
libraries, 60% are R_PPC_RELATIVE, 24% are R_PPC_GLOB_DAT or
diff --git a/sysdeps/powerpc/powerpc64/dl-irel.h b/sysdeps/powerpc/powerpc64/dl-irel.h
new file mode 100644
index 0000000..6cded50
--- /dev/null
+++ b/sysdeps/powerpc/powerpc64/dl-irel.h
@@ -0,0 +1,58 @@
+/* Machine-dependent ELF indirect relocation inline functions.
+ PowerPC64 version.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _DL_IREL_H
+#define _DL_IREL_H
+
+#include <stdio.h>
+#include <unistd.h>
+
+#define ELF_MACHINE_IRELA 1
+
+typedef struct
+{
+ Elf64_Addr fd_func;
+ Elf64_Addr fd_toc;
+ Elf64_Addr fd_aux;
+} Elf64_FuncDesc;
+
+static inline void
+__attribute ((always_inline))
+elf_irela (const Elf64_Rela *reloc)
+{
+ unsigned int r_type = ELF64_R_TYPE (reloc->r_info);
+
+ if (__builtin_expect (r_type == R_PPC64_IRELATIVE, 1))
+ {
+ Elf64_Addr *const reloc_addr = (void *) reloc->r_offset;
+ Elf64_Addr value = ((Elf64_Addr (*) (void)) reloc->r_addend) ();
+ *reloc_addr = value;
+ }
+ else if (__builtin_expect (r_type == R_PPC64_JMP_IREL, 1))
+ {
+ Elf64_Addr *const reloc_addr = (void *) reloc->r_offset;
+ Elf64_Addr value = ((Elf64_Addr (*) (void)) reloc->r_addend) ();
+ *(Elf64_FuncDesc *) reloc_addr = *(Elf64_FuncDesc *) value;
+ }
+ else
+ __libc_fatal ("unexpected reloc type in static binary");
+}
+
+#endif /* dl-irel.h */
diff --git a/sysdeps/powerpc/powerpc64/dl-machine.h b/sysdeps/powerpc/powerpc64/dl-machine.h
index b674dbe..8a720ae 100644
--- a/sysdeps/powerpc/powerpc64/dl-machine.h
+++ b/sysdeps/powerpc/powerpc64/dl-machine.h
@@ -526,6 +526,29 @@ elf_machine_tprel (struct link_map *map,
}
#endif
+/* Call function at address VALUE (an OPD entry) to resolve ifunc relocs. */
+auto inline Elf64_Addr __attribute__ ((always_inline))
+resolve_ifunc (Elf64_Addr value,
+ const struct link_map *map, const struct link_map *sym_map)
+{
+ /* The function we are calling may not yet have its opd entry relocated. */
+ Elf64_FuncDesc opd;
+ if (map != sym_map
+#if !defined RTLD_BOOTSTRAP && defined SHARED
+ /* Bootstrap map doesn't have l_relocated set for it. */
+ && sym_map != &GL(dl_rtld_map)
+#endif
+ && !sym_map->l_relocated)
+ {
+ Elf64_FuncDesc *func = (Elf64_FuncDesc *) value;
+ opd.fd_func = func->fd_func + sym_map->l_addr;
+ opd.fd_toc = func->fd_toc + sym_map->l_addr;
+ opd.fd_aux = func->fd_aux;
+ value = (Elf64_Addr) &opd;
+ }
+ return ((Elf64_Addr (*) (void)) value) ();
+}
+
/* Perform the relocation specified by RELOC and SYM (which is fully
resolved). MAP is the object containing the reloc. */
auto inline void __attribute__ ((always_inline))
@@ -550,11 +573,17 @@ elf_machine_rela (struct link_map *map,
if (__builtin_expect (r_type == R_PPC64_NONE, 0))
return;
- /* We need SYM_MAP even in the absence of TLS, for elf_machine_fixup_plt. */
+ /* We need SYM_MAP even in the absence of TLS, for elf_machine_fixup_plt
+ and STT_GNU_IFUNC. */
struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
Elf64_Addr value = ((sym_map == NULL ? 0 : sym_map->l_addr + sym->st_value)
+ reloc->r_addend);
+ if (sym != NULL
+ && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0)
+ && __builtin_expect (sym->st_shndx != SHN_UNDEF, 1))
+ value = resolve_ifunc (value, map, sym_map);
+
/* For relocs that don't edit code, return.
For relocs that might edit instructions, break from the switch. */
switch (r_type)
@@ -564,6 +593,14 @@ elf_machine_rela (struct link_map *map,
*reloc_addr = value;
return;
+ case R_PPC64_IRELATIVE:
+ value = resolve_ifunc (value, map, sym_map);
+ *reloc_addr = value;
+ return;
+
+ case R_PPC64_JMP_IREL:
+ value = resolve_ifunc (value, map, sym_map);
+ /* Fall thru */
case R_PPC64_JMP_SLOT:
#ifdef RESOLVE_CONFLICT_FIND_MAP
elf_machine_plt_conflict (reloc_addr, value);
--
Alan Modra
Australia Development Lab, IBM