summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf/rtld.c
diff options
context:
space:
mode:
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