svn commit: r228435 - in head/libexec/rtld-elf: . amd64 arm i386
ia64 mips powerpc powerpc64 sparc64
Konstantin Belousov
kib at FreeBSD.org
Mon Dec 12 11:03:15 UTC 2011
Author: kib
Date: Mon Dec 12 11:03:14 2011
New Revision: 228435
URL: http://svn.freebsd.org/changeset/base/228435
Log:
Add support for STT_GNU_IFUNC and R_MACHINE_IRELATIVE GNU extensions to
rtld on 386 and amd64. This adds runtime bits neccessary for the use
of the dispatch functions from the dynamically-linked executables and
shared libraries.
To allow use of external references from the dispatch function, resolution
of the R_MACHINE_IRESOLVE relocations in PLT is postponed until GOT entries
for PLT are prepared, and normal resolution of the GOT entries is finished.
Similar to how it is done by GNU, IRELATIVE relocations are resolved in
advance, instead of normal lazy handling for PLT.
Move the init_pltgot() call before the relocations for the object are
processed.
MFC after: 3 weeks
Modified:
head/libexec/rtld-elf/amd64/reloc.c
head/libexec/rtld-elf/arm/reloc.c
head/libexec/rtld-elf/i386/reloc.c
head/libexec/rtld-elf/ia64/reloc.c
head/libexec/rtld-elf/mips/reloc.c
head/libexec/rtld-elf/powerpc/reloc.c
head/libexec/rtld-elf/powerpc64/reloc.c
head/libexec/rtld-elf/rtld.c
head/libexec/rtld-elf/rtld.h
head/libexec/rtld-elf/sparc64/reloc.c
Modified: head/libexec/rtld-elf/amd64/reloc.c
==============================================================================
--- head/libexec/rtld-elf/amd64/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/amd64/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -344,11 +344,22 @@ reloc_plt(Obj_Entry *obj)
for (rela = obj->pltrela; rela < relalim; rela++) {
Elf_Addr *where;
- assert(ELF_R_TYPE(rela->r_info) == R_X86_64_JMP_SLOT);
-
- /* Relocate the GOT slot pointing into the PLT. */
- where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
- *where += (Elf_Addr)obj->relocbase;
+ switch(ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ /* Relocate the GOT slot pointing into the PLT. */
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+
+ case R_X86_64_IRELATIVE:
+ obj->irelative = true;
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
}
return 0;
}
@@ -368,19 +379,91 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
const Elf_Sym *def;
const Obj_Entry *defobj;
- assert(ELF_R_TYPE(rela->r_info) == R_X86_64_JMP_SLOT);
- where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
- def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL,
- lockstate);
- if (def == NULL)
- return -1;
- target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
- reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);
+ reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+ break;
+
+ case R_X86_64_IRELATIVE:
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ (unsigned int)ELF_R_TYPE(rela->r_info));
+ return (-1);
+ }
}
obj->jmpslots_done = true;
return 0;
}
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target, *ptr;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ break;
+
+ case R_X86_64_IRELATIVE:
+ ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ target = ((Elf_Addr (*)(void))ptr)();
+ *where = target;
+ break;
+ }
+ }
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rela *relalim;
+ const Elf_Rela *rela;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize);
+ for (rela = obj->pltrela; rela < relalim; rela++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rela->r_info)) {
+ case R_X86_64_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
+ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela);
+ break;
+ }
+ }
+ obj->gnu_ifunc = false;
+ return 0;
+}
+
void
allocate_initial_tls(Obj_Entry *objs)
{
Modified: head/libexec/rtld-elf/arm/reloc.c
==============================================================================
--- head/libexec/rtld-elf/arm/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/arm/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -337,6 +337,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
return (0);
}
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
Elf_Addr
reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
const Obj_Entry *obj, const Elf_Rel *rel)
Modified: head/libexec/rtld-elf/i386/reloc.c
==============================================================================
--- head/libexec/rtld-elf/i386/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/i386/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -298,13 +298,24 @@ reloc_plt(Obj_Entry *obj)
rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
for (rel = obj->pltrel; rel < rellim; rel++) {
- Elf_Addr *where;
+ Elf_Addr *where/*, val*/;
- assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
-
- /* Relocate the GOT slot pointing into the PLT. */
- where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
- *where += (Elf_Addr)obj->relocbase;
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ /* Relocate the GOT slot pointing into the PLT. */
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ *where += (Elf_Addr)obj->relocbase;
+ break;
+
+ case R_386_IRELATIVE:
+ obj->irelative = true;
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
}
return 0;
}
@@ -324,19 +335,88 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
const Elf_Sym *def;
const Obj_Entry *defobj;
- assert(ELF_R_TYPE(rel->r_info) == R_386_JMP_SLOT);
- where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
- def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
- lockstate);
- if (def == NULL)
- return -1;
- target = (Elf_Addr)(defobj->relocbase + def->st_value);
- reloc_jmpslot(where, target, defobj, obj, rel);
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {
+ obj->gnu_ifunc = true;
+ continue;
+ }
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ reloc_jmpslot(where, target, defobj, obj, rel);
+ break;
+
+ case R_386_IRELATIVE:
+ break;
+
+ default:
+ _rtld_error("Unknown relocation type %x in PLT",
+ ELF_R_TYPE(rel->r_info));
+ return (-1);
+ }
}
+
obj->jmpslots_done = true;
return 0;
}
+int
+reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+ Elf_Addr *where, target;
+
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_IRELATIVE:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ target = ((Elf_Addr (*)(void))(*where))();
+ *where = target;
+ break;
+ }
+ }
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, RtldLockState *lockstate)
+{
+ const Elf_Rel *rellim;
+ const Elf_Rel *rel;
+
+ if (!obj->gnu_ifunc)
+ return (0);
+ rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
+ for (rel = obj->pltrel; rel < rellim; rel++) {
+ Elf_Addr *where, target;
+ const Elf_Sym *def;
+ const Obj_Entry *defobj;
+
+ switch (ELF_R_TYPE(rel->r_info)) {
+ case R_386_JMP_SLOT:
+ where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
+ def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
+ lockstate);
+ if (def == NULL)
+ return (-1);
+ if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)
+ continue;
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ reloc_jmpslot(where, target, defobj, obj, rel);
+ break;
+ }
+ }
+
+ obj->gnu_ifunc = false;
+ return (0);
+}
+
void
allocate_initial_tls(Obj_Entry *objs)
{
Modified: head/libexec/rtld-elf/ia64/reloc.c
==============================================================================
--- head/libexec/rtld-elf/ia64/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/ia64/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -435,6 +435,22 @@ reloc_plt(Obj_Entry *obj)
return 0;
}
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
/* Relocate the jump slots in an object. */
int
reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate)
Modified: head/libexec/rtld-elf/mips/reloc.c
==============================================================================
--- head/libexec/rtld-elf/mips/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/mips/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -498,6 +498,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
return (0);
}
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
Elf_Addr
reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj,
const Obj_Entry *obj, const Elf_Rel *rel)
Modified: head/libexec/rtld-elf/powerpc/reloc.c
==============================================================================
--- head/libexec/rtld-elf/powerpc/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/powerpc/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -504,6 +504,21 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr
return (target);
}
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
/*
* Setup the plt glue routines.
Modified: head/libexec/rtld-elf/powerpc64/reloc.c
==============================================================================
--- head/libexec/rtld-elf/powerpc64/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/powerpc64/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -456,6 +456,22 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr
return (target);
}
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
void
init_pltgot(Obj_Entry *obj)
{
Modified: head/libexec/rtld-elf/rtld.c
==============================================================================
--- head/libexec/rtld-elf/rtld.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/rtld.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -561,6 +561,17 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_
return (func_ptr_type) obj_main->entry;
}
+void *
+rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def)
+{
+ void *ptr;
+ Elf_Addr target;
+
+ ptr = (void *)make_function_pointer(def, obj);
+ target = ((Elf_Addr (*)(void))ptr)();
+ return ((void *)target);
+}
+
Elf_Addr
_rtld_bind(Obj_Entry *obj, Elf_Size reloff)
{
@@ -584,8 +595,10 @@ _rtld_bind(Obj_Entry *obj, Elf_Size relo
&lockstate);
if (def == NULL)
die();
-
- target = (Elf_Addr)(defobj->relocbase + def->st_value);
+ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)
+ target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);
+ else
+ target = (Elf_Addr)(defobj->relocbase + def->st_value);
dbg("\"%s\" in \"%s\" ==> %p in \"%s\"",
defobj->strtab + def->st_name, basename(obj->path),
@@ -1944,6 +1957,10 @@ relocate_objects(Obj_Entry *first, bool
}
}
+
+ /* Set the special PLT or GOT entries. */
+ init_pltgot(obj);
+
/* Process the PLT relocations. */
if (reloc_plt(obj) == -1)
return -1;
@@ -1952,7 +1969,6 @@ relocate_objects(Obj_Entry *first, bool
if (reloc_jmpslots(obj, lockstate) == -1)
return -1;
-
/*
* Set up the magic number and version in the Obj_Entry. These
* were checked in the crt1.o from the original ElfKit, so we
@@ -1960,11 +1976,26 @@ relocate_objects(Obj_Entry *first, bool
*/
obj->magic = RTLD_MAGIC;
obj->version = RTLD_VERSION;
-
- /* Set the special PLT or GOT entries. */
- init_pltgot(obj);
}
+ /*
+ * The handling of R_MACHINE_IRELATIVE relocations and jumpslots
+ * referencing STT_GNU_IFUNC symbols is postponed till the other
+ * relocations are done. The indirect functions specified as
+ * ifunc are allowed to call other symbols, so we need to have
+ * objects relocated before asking for resolution from indirects.
+ *
+ * The R_MACHINE_IRELATIVE slots are resolved in greedy fashion,
+ * instead of the usual lazy handling of PLT slots. It is
+ * consistent with how GNU does it.
+ */
+ for (obj = first; obj != NULL; obj = obj->next) {
+ if (obj->irelative && reloc_iresolve(obj, lockstate) == -1)
+ return (-1);
+ if ((obj->bind_now || bind_now) && obj->gnu_ifunc &&
+ reloc_gnu_ifunc(obj, lockstate) == -1)
+ return (-1);
+ }
return 0;
}
@@ -2376,9 +2407,11 @@ do_dlsym(void *handle, const char *name,
* the relocated value of the symbol.
*/
if (ELF_ST_TYPE(def->st_info) == STT_FUNC)
- return make_function_pointer(def, defobj);
+ return (make_function_pointer(def, defobj));
+ else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC)
+ return (rtld_resolve_ifunc(defobj, def));
else
- return defobj->relocbase + def->st_value;
+ return (defobj->relocbase + def->st_value);
}
_rtld_error("Undefined symbol \"%s\"", name);
@@ -2822,6 +2855,8 @@ get_program_var_addr(const char *name, R
if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC)
return ((const void **)make_function_pointer(req.sym_out,
req.defobj_out));
+ else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC)
+ return ((const void **)rtld_resolve_ifunc(req.defobj_out, req.sym_out));
else
return ((const void **)(req.defobj_out->relocbase +
req.sym_out->st_value));
@@ -3088,6 +3123,7 @@ symlook_obj1(SymLook *req, const Obj_Ent
case STT_FUNC:
case STT_NOTYPE:
case STT_OBJECT:
+ case STT_GNU_IFUNC:
if (symp->st_value == 0)
continue;
/* fallthrough */
Modified: head/libexec/rtld-elf/rtld.h
==============================================================================
--- head/libexec/rtld-elf/rtld.h Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/rtld.h Mon Dec 12 11:03:14 2011 (r228435)
@@ -230,6 +230,8 @@ typedef struct Struct_Obj_Entry {
bool on_fini_list: 1; /* Object is already on fini list. */
bool dag_inited : 1; /* Object has its DAG initialized. */
bool filtees_loaded : 1; /* Filtees loaded */
+ bool irelative : 1; /* Object has R_MACHDEP_IRELATIVE relocs */
+ bool gnu_ifunc : 1; /* Object has references to STT_GNU_IFUNC */
struct link_map linkmap; /* For GDB and dlinfo() */
Objlist dldags; /* Object belongs to these dlopened DAGs (%) */
@@ -317,6 +319,7 @@ void lockdflt_init(void);
void obj_free(Obj_Entry *);
Obj_Entry *obj_new(void);
void _rtld_bind_start(void);
+void *rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def);
void symlook_init(SymLook *, const char *);
int symlook_obj(SymLook *, const Obj_Entry *);
void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset);
@@ -334,6 +337,8 @@ int do_copy_relocations(Obj_Entry *);
int reloc_non_plt(Obj_Entry *, Obj_Entry *, struct Struct_RtldLockState *);
int reloc_plt(Obj_Entry *);
int reloc_jmpslots(Obj_Entry *, struct Struct_RtldLockState *);
+int reloc_iresolve(Obj_Entry *, struct Struct_RtldLockState *);
+int reloc_gnu_ifunc(Obj_Entry *, struct Struct_RtldLockState *);
void allocate_initial_tls(Obj_Entry *);
#endif /* } */
Modified: head/libexec/rtld-elf/sparc64/reloc.c
==============================================================================
--- head/libexec/rtld-elf/sparc64/reloc.c Mon Dec 12 10:10:49 2011 (r228434)
+++ head/libexec/rtld-elf/sparc64/reloc.c Mon Dec 12 11:03:14 2011 (r228435)
@@ -550,6 +550,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockS
return (0);
}
+int
+reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
+int
+reloc_gnu_ifunc(Obj_Entry *obj, struct Struct_RtldLockState *lockstate)
+{
+
+ /* XXX not implemented */
+ return (0);
+}
+
Elf_Addr
reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *obj,
const Obj_Entry *refobj, const Elf_Rel *rel)
More information about the svn-src-head
mailing list