summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf/rtld.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2011-12-12 11:03:14 +0000
committerkib <kib@FreeBSD.org>2011-12-12 11:03:14 +0000
commita95b8e72c8b8615806e884acc5ccce614fae0370 (patch)
treed5f18ee2f1f28d34c0603cd801c3d94d87e79adf /libexec/rtld-elf/rtld.c
parente4d5e370653906483ad3fd23a8e15acb12b82ab5 (diff)
downloadFreeBSD-src-a95b8e72c8b8615806e884acc5ccce614fae0370.zip
FreeBSD-src-a95b8e72c8b8615806e884acc5ccce614fae0370.tar.gz
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
Diffstat (limited to 'libexec/rtld-elf/rtld.c')
-rw-r--r--libexec/rtld-elf/rtld.c52
1 files changed, 44 insertions, 8 deletions
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 0d4e972..17cd67c 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -561,6 +561,17 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
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 reloff)
&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 bind_now, Obj_Entry *rtldobj,
}
}
+
+ /* 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 bind_now, Obj_Entry *rtldobj,
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 bind_now, Obj_Entry *rtldobj,
*/
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, void *retaddr, const Ver_Entry *ve,
* 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, RtldLockState *lockstate)
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_Entry *obj)
case STT_FUNC:
case STT_NOTYPE:
case STT_OBJECT:
+ case STT_GNU_IFUNC:
if (symp->st_value == 0)
continue;
/* fallthrough */
OpenPOWER on IntegriCloud