* [PATCH] 2nd round of Mips ifunc support
@ 2013-11-16 0:44 Jack Carter
2013-11-16 1:11 ` Jack Carter
2013-11-18 12:29 ` Will Newton
0 siblings, 2 replies; 4+ messages in thread
From: Jack Carter @ 2013-11-16 0:44 UTC (permalink / raw)
To: libc-ports; +Cc: rdsandiford
[-- Attachment #1: Type: text/plain, Size: 107 bytes --]
Attached is the second pass for Mips ifunc support in glibc.
There is an abi attached as well.
Jack
[-- Attachment #2: ifunc_abi.txt --]
[-- Type: text/plain, Size: 8809 bytes --]
Mips GNU IFUNC ABI
I will use the terms STT_GNU_IFUNC and IFUNC interchangeably below.
***************************************************************
<Generic Definition> Taken from:
http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Function-Attributes.html
***************************************************************
ifunc ("resolver")
The ifunc attribute is used to mark a function as an indirect
function using the STT_GNU_IFUNC symbol type extension to the
ELF standard. This allows the resolution of the symbol value
to be determined dynamically at load time, and an optimized
version of the routine can be selected for the particular
processor or other system characteristics determined then.
To use this attribute, first define the implementation
functions available, and a resolver function that returns a
pointer to the selected implementation function. The
implementation functions' declarations must match the API of
the function being implemented, the resolver's declaration
is be a function returning pointer to void function returning
void:
void *my_memcpy (void *dst, const void *src, size_t len)
{
...
}
static void (*resolve_memcpy (void)) (void)
{
return my_memcpy; // we'll just always select this routine
}
The exported header file declaring the function the user calls
would contain:
extern void *memcpy (void *, const void *, size_t);
allowing the user to call this as a regular function, unaware
of the implementation. Finally, the indirect function needs
to be defined in the same translation unit as the resolver
function:
void *memcpy (void *, const void *, size_t)
__attribute__ ((ifunc ("resolve_memcpy")));
Indirect functions cannot be weak, and require a recent binutils
(at least version 2.20.1), and GNU C library (at least version 2.11.1).
***************************************************************
<Mips C Example>
***************************************************************
int func1 (void) __attribute__ ((ifunc ("func1_ifunc")));
static int f1_a(void){return 1;}
static int f1_b(void){return 2;}
static int f1_c(void){return 3;}
void *
func1_ifunc (void)
{
volatile int result;
struct utsname buf;
if ((uname(&buf)))
result = 0x30;
else
result = 0x3;
if (result & 0xf0)
return f1_a;
else if (result & 0x0f)
return f1_b;
else
return f1_c;
}
***************************************************************
<Resulting Assembly>
***************************************************************
A new symbol for the ifunc routine is created that associates it
to the real ifunc1_func() function.
.size func1_ifunc, .-func1_ifunc
.globl func1
.type func1, @gnu_indirect_function
func1 = func1_ifunc
***************************************************************
<Resulting Object>
***************************************************************
A new symbol func1 is created with the only difference from
func1_ifunc being the section type STT_GNU_IFUNC.
Section .symtab
[No] Value Size Type Bind Other Shndx Name
===============================================================
[22] 0xd8 0xbc FUNC GLOBAL DEFAULT (1) .text (42) func1_ifunc
[25] 0xd8 0xbc IFUNC GLOBAL DEFAULT (1) .text (69) func1
***************************************************************
<Static Linker Behavior and output>
***************************************************************
There are 3 general linker output variations that are addressed
here:
1) Static/Non-shared executables that do not use an independent
dynamic linker at startup time. Instead there is an embedded
startup routine to resolve IFUNC symbols or the R_MIPS_RELATIVE
relocation created for them.
2) Executables that use shared objects and thus the dynamic linker.
3) Shared objects
***
*1*
***
Static executable links will for each IFUNC symbol produce the
following:
*) Generate an iplt stub which is text and will be part of the .iplt
section. Currently the stub is the following instruction sequence:
lui $15, %hi(.got.iplt entry)
l[wd] $25, %lo(.got.iplt entry)($15)
jr $25
nop
The section attributes for .iplt are:
sh_name: (.iplt)
sh_type: SHT_PROGBITS
sh_info: 0
sh_flags: SHF_ALLOC|SHF_EXECINSTR
sh_addalign: 4
sh_entsize: 0
This stub will take the function address in the .igot.plt section
for this IFUNC symbol and jump to it.
*) Generate igot table (.igot.plt)
This is an array of IFUNC addresses that point to the resolver function
for this IFUNC. If the target ABI is 32 bit the entries will be 4 bytes
each and if the ABI is 64 bit the entries will be 8 bytes each. This of
course will preclude any linking of a combination of n32 and n64, but
that will have more problems than ifunc.
The section attributes for .iplt are:
sh_name: (.igot.plt)
sh_type: SHT_PROGBITS
sh_info: 0
sh_flags: SHF_ALLOC|SHF_WRITE *
sh_addalign: 4/8
sh_entsize: 4/8
(* Note, in spite of the name this is NOT a GP relative section)
*) Generate R_MIPS_IRELATIVE relocations for the igot table.
Produce a relocation of type R_MIPS_IRELATIVE (128) pointing at the
.igot.plt table entry for this IFUNC.
This goes into a .rel.dyn relocation section
sh_name: (.rel.dyn)
sh_type: SHT_REL
sh_info: 0
sh_flags: SHF_ALLOC
sh_addalign: 4/8
sh_entsize: 8/24
*) Generate the 2 symbols __rel_iplt_start and __rel_iplt_end which
point at the beginning and end of the relocations for the .igot.iplt
section. That is, first entry and last entry. Not last entry+1.
Type: STT_NOTYPE
Binding: STB_LOCAL
Other: STO_DEFAULT
Size: 0
Value: Address of begin or end of IFUNC relocations
At startup, the embedded dynamic linker will find the beginning and
end range of R_MIPS_IRELATIVE relocations through the special symbols
__rel_iplt_start and __rel_iplt_end.
The relocation entry contains the address of an associated entry in the
.igot.plt table, which initially contains the address of the associated
resolver function. The resolver is called and return value of the resolver
replaces the .igot.plt table entry.
Every call to the ifunc routine goes through the iplt at runtime.
***
*2*
***
Dynamically linked executables with ifunc definitions are for the most
part the same as static executables, with the following exceptions:
*) No __rel_iplt_start/end symbols. The dynamic linker handles dynamic
relocations.
*) Generate STT_GNU_IFUNC symbols in the .dynsym section with the value
being the iplt entry for this IFUNC and transform them into
STT_FUNC symbols. Any references to this IFUNC have to go through
the stub. The dynamic linker (ld.so) will be doing the fixup at start-
up.
***
*3*
***
Links producing dynamic shared objects do not produce iplt stubs, a
.igot.plt table, or R_MIPS_IRELATIVE relocations.
IFUNC resolution occurs during the processing of global GOT entries.
For .dynsym entries of type IFUNC, the load offset for the resolver
function stored in the GOT entry. The resolver function is called and
the address returned by the resolver function replaces resolver
offset value in the GOT entry.
***************************************************************
<Dynamic Linker Behavior>
***************************************************************
Entries in the GOT within the DT_MIPS_LOCAL_GOTNO should have their
own entries and not share a common local GOT page entry.
For global entries in the GOT that start at DT_MIPS_GOTSYM, the
entries in the dynamic symbol table are indexed along with the entries
in the GOT table. If the symbol is of type STT_GNU_IFUNC, the address
of the resolver routine is computed by adding the run-time load
address of the shared object and the offset for the resolver function,
which stored in the GOT entry. The resolver is then called and the
resultant address replaces the GOT entry.
IFUNC entries cannot be quickstarted/prelinked since the runtime value
is not known until load time.
Any dynamic relocation resolution using R_MIPS_IRELATIVE will take the
address pointed to by the relocation and assume it is the address of the
resolver, run the resolver and replace the resolver address with the
value returned by the resolver.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: mips_ifunc_glibc.patch --]
[-- Type: text/x-patch; name="mips_ifunc_glibc.patch", Size: 8469 bytes --]
This is the initial MIPS ifunc patch for glibc. This patch should be
applied in conjunction with the binutils patch.
This is targeting o32, n32 and 64 bit. Patches for MicroMips, Mips16,
and possibly vxworks will follow this patch.
Relocations using R_MIPS_IRELATIVE or referencing a symbol of type
STT_GNU_IFUNC have their affective address produced by executing the resolver
function pointed by either the R_MIPS_IRELATIVE target or the STT_GNU_IFUNC
target. The resultant address is the final function effective address that
will be used for the duration of the program execution.
There is an attached ABI that describes the runtime fixup in more detail.
***************************************
./ChangeLog
* elf/elf.h:
Define for R_MIPS_IRELATIVE and bump R_MIPS_NUM up to 129.
ports/ChangeLog
* sysdeps/mips/dl-irel.h: New file.
(elf_ifunc_invoke): Do the indirect reference.
(elf_irel): Call elf_ifunc_invoke() for acceptable relocations.
* sysdeps/mips/dl-machine.h:
Include new dl-irel.h.
(ELF_MACHINE_BEFORE_RTLD_RELOC): Add check for STT_GNU_IFUNC.
(elf_machine_reloc): Add skip_ifunc to parameter.
Add case for R_MIPS_IRELATIVE.
(elf_machine_rel): Add skip_ifunc to call to elf_machine_reloc().
(elf_machine_rela):Add skip_ifunc to call to elf_machine_reloc().
(RESOLVE_GOTSYM): Add check for STT_GNU_IFUNC.
(elf_machine_got_rel): Add check for STT_GNU_IFUNC.
* sysdeps/mips/dl-trampoline.c:
(__dl_runtime_resolve): Add check for STT_GNU_IFUNC.
diff --git a/elf/elf.h b/elf/elf.h
index a05ea3b..b3a89db 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1626,8 +1626,9 @@ typedef struct
#define R_MIPS_GLOB_DAT 51
#define R_MIPS_COPY 126
#define R_MIPS_JUMP_SLOT 127
+#define R_MIPS_IRELATIVE 128
/* Keep this the last entry. */
-#define R_MIPS_NUM 128
+#define R_MIPS_NUM 129
/* Legal values for p_type field of Elf32_Phdr. */
diff --git a/ports/sysdeps/mips/dl-irel.h b/ports/sysdeps/mips/dl-irel.h
new file mode 100644
index 0000000..1891de0
--- /dev/null
+++ b/ports/sysdeps/mips/dl-irel.h
@@ -0,0 +1,63 @@
+/* Machine-dependent ELF indirect relocation inline functions.
+ MIPS version.
+ Copyright (C) 2009-2013 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, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _DL_IREL_H
+#define _DL_IREL_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sgidefs.h>
+#include <link.h>
+#include <elf.h>
+#include <ldsodefs.h>
+
+#define ELF_MACHINE_IREL 1
+
+static inline ElfW(Addr)
+__attribute ((always_inline))
+elf_ifunc_invoke (ElfW(Addr) addr)
+{
+ /* Print some debugging info if wanted. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS, 0))
+ {
+ ElfW(Addr) t_addr =
+ ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+ GLRO(dl_debug_printf) ("In elf_ifunc_invoke(0x%lx), return(0x%lx)\n",
+ (unsigned long int)addr,
+ (unsigned long int)t_addr);
+ }
+
+ return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+}
+
+/* Allow either R_MIPS_RELATIVE or the nop R_MIPS_NONE */
+static inline void
+__attribute ((always_inline))
+elf_irel (const ElfW(Rel) *reloc)
+{
+ ElfW(Addr) *const reloc_addr = (void *) reloc->r_offset;
+ const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info);
+
+ if (__builtin_expect (r_type == R_MIPS_IRELATIVE, 1))
+ *reloc_addr = elf_ifunc_invoke (*reloc_addr);
+ else if (r_type)
+ __libc_fatal ("unexpected reloc type in static binary\n");
+}
+
+#endif /* dl-irel.h */
diff --git a/ports/sysdeps/mips/dl-machine.h b/ports/sysdeps/mips/dl-machine.h
index 722c8a0..671aa1f 100644
--- a/ports/sysdeps/mips/dl-machine.h
+++ b/ports/sysdeps/mips/dl-machine.h
@@ -32,6 +32,7 @@
#include <sgidefs.h>
#include <sys/asm.h>
#include <dl-tls.h>
+#include <dl-irel.h>
/* The offset of gp from GOT might be system-dependent. It's set by
ld. The same value is also */
@@ -204,6 +205,8 @@ do { \
else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC \
&& *got != sym->st_value) \
*got += map->l_addr; \
+ else if (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC) \
+ *got = elf_ifunc_invoke(sym->st_value); \
else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION) \
{ \
if (sym->st_other == 0) \
@@ -451,7 +454,8 @@ auto inline void
__attribute__ ((always_inline))
elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info,
const ElfW(Sym) *sym, const struct r_found_version *version,
- void *reloc_addr, ElfW(Addr) r_addend, int inplace_p)
+ void *reloc_addr, ElfW(Addr) r_addend, int inplace_p,
+ int skip_ifunc)
{
const unsigned long int r_type = ELFW(R_TYPE) (r_info);
ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
@@ -656,6 +660,14 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info,
break;
}
+ case R_MIPS_IRELATIVE:
+ /* The resolver routine is the symbol referenced by this relocation.
+ To get the address of the function to use at runtime, the resolver
+ routine is called and its return value is the address of the target
+ functon which is final relocation value. */
+ *addr_field = elf_ifunc_invoke (map->l_addr + *addr_field);
+ break;
+
#if _MIPS_SIM == _ABI64
case R_MIPS_64:
/* For full compliance with the ELF64 ABI, one must precede the
@@ -685,7 +697,8 @@ elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc,
const ElfW(Sym) *sym, const struct r_found_version *version,
void *const reloc_addr, int skip_ifunc)
{
- elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1);
+ elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1,
+ skip_ifunc);
}
auto inline void
@@ -726,7 +739,7 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
void *const reloc_addr, int skip_ifunc)
{
elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr,
- reloc->r_addend, 0);
+ reloc->r_addend, 0, skip_ifunc);
}
auto inline void
@@ -753,8 +766,15 @@ elf_machine_got_rel (struct link_map *map, int lazy)
const struct r_found_version *version __attribute__ ((unused)) \
= vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL; \
struct link_map *sym_map; \
+ ElfW(Addr) value; \
sym_map = RESOLVE_MAP (&ref, version, reloc); \
- ref ? sym_map->l_addr + ref->st_value : 0; \
+ if (ref) \
+ { \
+ value = sym_map->l_addr + ref->st_value; \
+ if (ELFW(ST_TYPE) (ref->st_info) == STT_GNU_IFUNC) \
+ value = elf_ifunc_invoke (value); \
+ } \
+ ref ? value : 0; \
})
if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
@@ -824,6 +844,8 @@ elf_machine_got_rel (struct link_map *map, int lazy)
if (sym->st_other == 0)
*got += map->l_addr;
}
+ else if (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
+ *got = elf_ifunc_invoke (map->l_addr + *got);
else
*got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
diff --git a/ports/sysdeps/mips/dl-trampoline.c b/ports/sysdeps/mips/dl-trampoline.c
index 605e44e..0a510f3 100644
--- a/ports/sysdeps/mips/dl-trampoline.c
+++ b/ports/sysdeps/mips/dl-trampoline.c
@@ -193,6 +193,10 @@ __dl_runtime_resolve (ElfW(Word) sym_index,
/* Currently value contains the base load address of the object
that defines sym. Now add in the symbol offset. */
value = (sym ? sym_map->l_addr + sym->st_value : 0);
+ if (sym != NULL
+ && __builtin_expect (ELFW(ST_TYPE) (sym->st_info)
+ == STT_GNU_IFUNC, 0))
+ value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
}
else
/* We already found the symbol. The module (and therefore its load
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] 2nd round of Mips ifunc support
2013-11-16 0:44 [PATCH] 2nd round of Mips ifunc support Jack Carter
@ 2013-11-16 1:11 ` Jack Carter
2013-11-18 12:29 ` Will Newton
1 sibling, 0 replies; 4+ messages in thread
From: Jack Carter @ 2013-11-16 1:11 UTC (permalink / raw)
To: libc-ports; +Cc: rdsandiford
[-- Attachment #1: Type: text/plain, Size: 107 bytes --]
Attached is the second pass for Mips ifunc support in glibc.
There is an abi attached as well.
Jack
[-- Attachment #2: ifunc_abi.txt --]
[-- Type: text/plain, Size: 8809 bytes --]
Mips GNU IFUNC ABI
I will use the terms STT_GNU_IFUNC and IFUNC interchangeably below.
***************************************************************
<Generic Definition> Taken from:
http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Function-Attributes.html
***************************************************************
ifunc ("resolver")
The ifunc attribute is used to mark a function as an indirect
function using the STT_GNU_IFUNC symbol type extension to the
ELF standard. This allows the resolution of the symbol value
to be determined dynamically at load time, and an optimized
version of the routine can be selected for the particular
processor or other system characteristics determined then.
To use this attribute, first define the implementation
functions available, and a resolver function that returns a
pointer to the selected implementation function. The
implementation functions' declarations must match the API of
the function being implemented, the resolver's declaration
is be a function returning pointer to void function returning
void:
void *my_memcpy (void *dst, const void *src, size_t len)
{
...
}
static void (*resolve_memcpy (void)) (void)
{
return my_memcpy; // we'll just always select this routine
}
The exported header file declaring the function the user calls
would contain:
extern void *memcpy (void *, const void *, size_t);
allowing the user to call this as a regular function, unaware
of the implementation. Finally, the indirect function needs
to be defined in the same translation unit as the resolver
function:
void *memcpy (void *, const void *, size_t)
__attribute__ ((ifunc ("resolve_memcpy")));
Indirect functions cannot be weak, and require a recent binutils
(at least version 2.20.1), and GNU C library (at least version 2.11.1).
***************************************************************
<Mips C Example>
***************************************************************
int func1 (void) __attribute__ ((ifunc ("func1_ifunc")));
static int f1_a(void){return 1;}
static int f1_b(void){return 2;}
static int f1_c(void){return 3;}
void *
func1_ifunc (void)
{
volatile int result;
struct utsname buf;
if ((uname(&buf)))
result = 0x30;
else
result = 0x3;
if (result & 0xf0)
return f1_a;
else if (result & 0x0f)
return f1_b;
else
return f1_c;
}
***************************************************************
<Resulting Assembly>
***************************************************************
A new symbol for the ifunc routine is created that associates it
to the real ifunc1_func() function.
.size func1_ifunc, .-func1_ifunc
.globl func1
.type func1, @gnu_indirect_function
func1 = func1_ifunc
***************************************************************
<Resulting Object>
***************************************************************
A new symbol func1 is created with the only difference from
func1_ifunc being the section type STT_GNU_IFUNC.
Section .symtab
[No] Value Size Type Bind Other Shndx Name
===============================================================
[22] 0xd8 0xbc FUNC GLOBAL DEFAULT (1) .text (42) func1_ifunc
[25] 0xd8 0xbc IFUNC GLOBAL DEFAULT (1) .text (69) func1
***************************************************************
<Static Linker Behavior and output>
***************************************************************
There are 3 general linker output variations that are addressed
here:
1) Static/Non-shared executables that do not use an independent
dynamic linker at startup time. Instead there is an embedded
startup routine to resolve IFUNC symbols or the R_MIPS_RELATIVE
relocation created for them.
2) Executables that use shared objects and thus the dynamic linker.
3) Shared objects
***
*1*
***
Static executable links will for each IFUNC symbol produce the
following:
*) Generate an iplt stub which is text and will be part of the .iplt
section. Currently the stub is the following instruction sequence:
lui $15, %hi(.got.iplt entry)
l[wd] $25, %lo(.got.iplt entry)($15)
jr $25
nop
The section attributes for .iplt are:
sh_name: (.iplt)
sh_type: SHT_PROGBITS
sh_info: 0
sh_flags: SHF_ALLOC|SHF_EXECINSTR
sh_addalign: 4
sh_entsize: 0
This stub will take the function address in the .igot.plt section
for this IFUNC symbol and jump to it.
*) Generate igot table (.igot.plt)
This is an array of IFUNC addresses that point to the resolver function
for this IFUNC. If the target ABI is 32 bit the entries will be 4 bytes
each and if the ABI is 64 bit the entries will be 8 bytes each. This of
course will preclude any linking of a combination of n32 and n64, but
that will have more problems than ifunc.
The section attributes for .iplt are:
sh_name: (.igot.plt)
sh_type: SHT_PROGBITS
sh_info: 0
sh_flags: SHF_ALLOC|SHF_WRITE *
sh_addalign: 4/8
sh_entsize: 4/8
(* Note, in spite of the name this is NOT a GP relative section)
*) Generate R_MIPS_IRELATIVE relocations for the igot table.
Produce a relocation of type R_MIPS_IRELATIVE (128) pointing at the
.igot.plt table entry for this IFUNC.
This goes into a .rel.dyn relocation section
sh_name: (.rel.dyn)
sh_type: SHT_REL
sh_info: 0
sh_flags: SHF_ALLOC
sh_addalign: 4/8
sh_entsize: 8/24
*) Generate the 2 symbols __rel_iplt_start and __rel_iplt_end which
point at the beginning and end of the relocations for the .igot.iplt
section. That is, first entry and last entry. Not last entry+1.
Type: STT_NOTYPE
Binding: STB_LOCAL
Other: STO_DEFAULT
Size: 0
Value: Address of begin or end of IFUNC relocations
At startup, the embedded dynamic linker will find the beginning and
end range of R_MIPS_IRELATIVE relocations through the special symbols
__rel_iplt_start and __rel_iplt_end.
The relocation entry contains the address of an associated entry in the
.igot.plt table, which initially contains the address of the associated
resolver function. The resolver is called and return value of the resolver
replaces the .igot.plt table entry.
Every call to the ifunc routine goes through the iplt at runtime.
***
*2*
***
Dynamically linked executables with ifunc definitions are for the most
part the same as static executables, with the following exceptions:
*) No __rel_iplt_start/end symbols. The dynamic linker handles dynamic
relocations.
*) Generate STT_GNU_IFUNC symbols in the .dynsym section with the value
being the iplt entry for this IFUNC and transform them into
STT_FUNC symbols. Any references to this IFUNC have to go through
the stub. The dynamic linker (ld.so) will be doing the fixup at start-
up.
***
*3*
***
Links producing dynamic shared objects do not produce iplt stubs, a
.igot.plt table, or R_MIPS_IRELATIVE relocations.
IFUNC resolution occurs during the processing of global GOT entries.
For .dynsym entries of type IFUNC, the load offset for the resolver
function stored in the GOT entry. The resolver function is called and
the address returned by the resolver function replaces resolver
offset value in the GOT entry.
***************************************************************
<Dynamic Linker Behavior>
***************************************************************
Entries in the GOT within the DT_MIPS_LOCAL_GOTNO should have their
own entries and not share a common local GOT page entry.
For global entries in the GOT that start at DT_MIPS_GOTSYM, the
entries in the dynamic symbol table are indexed along with the entries
in the GOT table. If the symbol is of type STT_GNU_IFUNC, the address
of the resolver routine is computed by adding the run-time load
address of the shared object and the offset for the resolver function,
which stored in the GOT entry. The resolver is then called and the
resultant address replaces the GOT entry.
IFUNC entries cannot be quickstarted/prelinked since the runtime value
is not known until load time.
Any dynamic relocation resolution using R_MIPS_IRELATIVE will take the
address pointed to by the relocation and assume it is the address of the
resolver, run the resolver and replace the resolver address with the
value returned by the resolver.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: mips_ifunc_glibc.patch --]
[-- Type: text/x-patch; name="mips_ifunc_glibc.patch", Size: 8469 bytes --]
This is the initial MIPS ifunc patch for glibc. This patch should be
applied in conjunction with the binutils patch.
This is targeting o32, n32 and 64 bit. Patches for MicroMips, Mips16,
and possibly vxworks will follow this patch.
Relocations using R_MIPS_IRELATIVE or referencing a symbol of type
STT_GNU_IFUNC have their affective address produced by executing the resolver
function pointed by either the R_MIPS_IRELATIVE target or the STT_GNU_IFUNC
target. The resultant address is the final function effective address that
will be used for the duration of the program execution.
There is an attached ABI that describes the runtime fixup in more detail.
***************************************
./ChangeLog
* elf/elf.h:
Define for R_MIPS_IRELATIVE and bump R_MIPS_NUM up to 129.
ports/ChangeLog
* sysdeps/mips/dl-irel.h: New file.
(elf_ifunc_invoke): Do the indirect reference.
(elf_irel): Call elf_ifunc_invoke() for acceptable relocations.
* sysdeps/mips/dl-machine.h:
Include new dl-irel.h.
(ELF_MACHINE_BEFORE_RTLD_RELOC): Add check for STT_GNU_IFUNC.
(elf_machine_reloc): Add skip_ifunc to parameter.
Add case for R_MIPS_IRELATIVE.
(elf_machine_rel): Add skip_ifunc to call to elf_machine_reloc().
(elf_machine_rela):Add skip_ifunc to call to elf_machine_reloc().
(RESOLVE_GOTSYM): Add check for STT_GNU_IFUNC.
(elf_machine_got_rel): Add check for STT_GNU_IFUNC.
* sysdeps/mips/dl-trampoline.c:
(__dl_runtime_resolve): Add check for STT_GNU_IFUNC.
diff --git a/elf/elf.h b/elf/elf.h
index a05ea3b..b3a89db 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -1626,8 +1626,9 @@ typedef struct
#define R_MIPS_GLOB_DAT 51
#define R_MIPS_COPY 126
#define R_MIPS_JUMP_SLOT 127
+#define R_MIPS_IRELATIVE 128
/* Keep this the last entry. */
-#define R_MIPS_NUM 128
+#define R_MIPS_NUM 129
/* Legal values for p_type field of Elf32_Phdr. */
diff --git a/ports/sysdeps/mips/dl-irel.h b/ports/sysdeps/mips/dl-irel.h
new file mode 100644
index 0000000..1891de0
--- /dev/null
+++ b/ports/sysdeps/mips/dl-irel.h
@@ -0,0 +1,63 @@
+/* Machine-dependent ELF indirect relocation inline functions.
+ MIPS version.
+ Copyright (C) 2009-2013 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, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _DL_IREL_H
+#define _DL_IREL_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sgidefs.h>
+#include <link.h>
+#include <elf.h>
+#include <ldsodefs.h>
+
+#define ELF_MACHINE_IREL 1
+
+static inline ElfW(Addr)
+__attribute ((always_inline))
+elf_ifunc_invoke (ElfW(Addr) addr)
+{
+ /* Print some debugging info if wanted. */
+ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS, 0))
+ {
+ ElfW(Addr) t_addr =
+ ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+ GLRO(dl_debug_printf) ("In elf_ifunc_invoke(0x%lx), return(0x%lx)\n",
+ (unsigned long int)addr,
+ (unsigned long int)t_addr);
+ }
+
+ return ((ElfW(Addr) (*) (unsigned long int)) (addr)) (GLRO(dl_hwcap));
+}
+
+/* Allow either R_MIPS_RELATIVE or the nop R_MIPS_NONE */
+static inline void
+__attribute ((always_inline))
+elf_irel (const ElfW(Rel) *reloc)
+{
+ ElfW(Addr) *const reloc_addr = (void *) reloc->r_offset;
+ const unsigned long int r_type = ELFW(R_TYPE) (reloc->r_info);
+
+ if (__builtin_expect (r_type == R_MIPS_IRELATIVE, 1))
+ *reloc_addr = elf_ifunc_invoke (*reloc_addr);
+ else if (r_type)
+ __libc_fatal ("unexpected reloc type in static binary\n");
+}
+
+#endif /* dl-irel.h */
diff --git a/ports/sysdeps/mips/dl-machine.h b/ports/sysdeps/mips/dl-machine.h
index 722c8a0..671aa1f 100644
--- a/ports/sysdeps/mips/dl-machine.h
+++ b/ports/sysdeps/mips/dl-machine.h
@@ -32,6 +32,7 @@
#include <sgidefs.h>
#include <sys/asm.h>
#include <dl-tls.h>
+#include <dl-irel.h>
/* The offset of gp from GOT might be system-dependent. It's set by
ld. The same value is also */
@@ -204,6 +205,8 @@ do { \
else if (ELFW(ST_TYPE) (sym->st_info) == STT_FUNC \
&& *got != sym->st_value) \
*got += map->l_addr; \
+ else if (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC) \
+ *got = elf_ifunc_invoke(sym->st_value); \
else if (ELFW(ST_TYPE) (sym->st_info) == STT_SECTION) \
{ \
if (sym->st_other == 0) \
@@ -451,7 +454,8 @@ auto inline void
__attribute__ ((always_inline))
elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info,
const ElfW(Sym) *sym, const struct r_found_version *version,
- void *reloc_addr, ElfW(Addr) r_addend, int inplace_p)
+ void *reloc_addr, ElfW(Addr) r_addend, int inplace_p,
+ int skip_ifunc)
{
const unsigned long int r_type = ELFW(R_TYPE) (r_info);
ElfW(Addr) *addr_field = (ElfW(Addr) *) reloc_addr;
@@ -656,6 +660,14 @@ elf_machine_reloc (struct link_map *map, ElfW(Addr) r_info,
break;
}
+ case R_MIPS_IRELATIVE:
+ /* The resolver routine is the symbol referenced by this relocation.
+ To get the address of the function to use at runtime, the resolver
+ routine is called and its return value is the address of the target
+ functon which is final relocation value. */
+ *addr_field = elf_ifunc_invoke (map->l_addr + *addr_field);
+ break;
+
#if _MIPS_SIM == _ABI64
case R_MIPS_64:
/* For full compliance with the ELF64 ABI, one must precede the
@@ -685,7 +697,8 @@ elf_machine_rel (struct link_map *map, const ElfW(Rel) *reloc,
const ElfW(Sym) *sym, const struct r_found_version *version,
void *const reloc_addr, int skip_ifunc)
{
- elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1);
+ elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr, 0, 1,
+ skip_ifunc);
}
auto inline void
@@ -726,7 +739,7 @@ elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
void *const reloc_addr, int skip_ifunc)
{
elf_machine_reloc (map, reloc->r_info, sym, version, reloc_addr,
- reloc->r_addend, 0);
+ reloc->r_addend, 0, skip_ifunc);
}
auto inline void
@@ -753,8 +766,15 @@ elf_machine_got_rel (struct link_map *map, int lazy)
const struct r_found_version *version __attribute__ ((unused)) \
= vernum ? &map->l_versions[vernum[sym_index] & 0x7fff] : NULL; \
struct link_map *sym_map; \
+ ElfW(Addr) value; \
sym_map = RESOLVE_MAP (&ref, version, reloc); \
- ref ? sym_map->l_addr + ref->st_value : 0; \
+ if (ref) \
+ { \
+ value = sym_map->l_addr + ref->st_value; \
+ if (ELFW(ST_TYPE) (ref->st_info) == STT_GNU_IFUNC) \
+ value = elf_ifunc_invoke (value); \
+ } \
+ ref ? value : 0; \
})
if (map->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
@@ -824,6 +844,8 @@ elf_machine_got_rel (struct link_map *map, int lazy)
if (sym->st_other == 0)
*got += map->l_addr;
}
+ else if (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC)
+ *got = elf_ifunc_invoke (map->l_addr + *got);
else
*got = RESOLVE_GOTSYM (sym, vernum, symidx, R_MIPS_32);
diff --git a/ports/sysdeps/mips/dl-trampoline.c b/ports/sysdeps/mips/dl-trampoline.c
index 605e44e..0a510f3 100644
--- a/ports/sysdeps/mips/dl-trampoline.c
+++ b/ports/sysdeps/mips/dl-trampoline.c
@@ -193,6 +193,10 @@ __dl_runtime_resolve (ElfW(Word) sym_index,
/* Currently value contains the base load address of the object
that defines sym. Now add in the symbol offset. */
value = (sym ? sym_map->l_addr + sym->st_value : 0);
+ if (sym != NULL
+ && __builtin_expect (ELFW(ST_TYPE) (sym->st_info)
+ == STT_GNU_IFUNC, 0))
+ value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
}
else
/* We already found the symbol. The module (and therefore its load
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] 2nd round of Mips ifunc support
2013-11-16 0:44 [PATCH] 2nd round of Mips ifunc support Jack Carter
2013-11-16 1:11 ` Jack Carter
@ 2013-11-18 12:29 ` Will Newton
2013-11-20 21:15 ` Jack Carter
1 sibling, 1 reply; 4+ messages in thread
From: Will Newton @ 2013-11-18 12:29 UTC (permalink / raw)
To: Jack Carter; +Cc: libc-ports, rdsandiford
On 16 November 2013 00:41, Jack Carter <Jack.Carter@imgtec.com> wrote:
> Attached is the second pass for Mips ifunc support in glibc.
>
> There is an abi attached as well.
Your patch would be easier to review if it was posted inline with e.g.
git-send-email.
I don't think the vxworks comment in the commit message (or indeed the
reference to micromips etc.) makes any sense in the context of glibc.
The use of __builtin_expect should be replaced with __glibc_unlikely.
The example in your ABI doc should probably take an argument of the
hwcap value rather than void.
--
Will Newton
Toolchain Working Group, Linaro
^ permalink raw reply [flat|nested] 4+ messages in thread
* RE: [PATCH] 2nd round of Mips ifunc support
2013-11-18 12:29 ` Will Newton
@ 2013-11-20 21:15 ` Jack Carter
0 siblings, 0 replies; 4+ messages in thread
From: Jack Carter @ 2013-11-20 21:15 UTC (permalink / raw)
To: Will Newton; +Cc: libc-ports, rdsandiford
Will, Thanks for looking at this. My responses are below.
Jack
________________________________________
> From: Will Newton [will.newton@linaro.org]
> Sent: Saturday, November 16, 2013 3:06 AM
> To: Jack Carter
> Cc: libc-ports@sourceware.org; rdsandiford@googlemail.com
> Subject: Re: [PATCH] 2nd round of Mips ifunc support
>
> On 16 November 2013 00:41, Jack Carter <Jack.Carter@imgtec.com> wrote:
> > Attached is the second pass for Mips ifunc support in glibc.
> >
> > There is an abi attached as well.
>
> Your patch would be easier to review if it was posted inline with e.g.
> git-send-email.
Will do. I got reamed for using it for LLVM, but am learning the differences between
the 2 cultures.
>
> I don't think the vxworks comment in the commit message (or indeed the
> reference to micromips etc.) makes any sense in the context of glibc.
I will take them out.
>
> The use of __builtin_expect should be replaced with __glibc_unlikely.
I will make the changes.
>
> The example in your ABI doc should probably take an argument of the
> hwcap value rather than void.
I will make the changes.
>
> --
> Will Newton
> Toolchain Working Group, Linaro
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2013-11-20 19:57 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-16 0:44 [PATCH] 2nd round of Mips ifunc support Jack Carter
2013-11-16 1:11 ` Jack Carter
2013-11-18 12:29 ` Will Newton
2013-11-20 21:15 ` Jack Carter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).