diff options
Diffstat (limited to 'libexec/rtld-elf/rtld.c')
-rw-r--r-- | libexec/rtld-elf/rtld.c | 52 |
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 */ |