diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c index 0bfc4fe8f070..5ae8493facd4 100644 --- a/libexec/rtld-elf/amd64/reloc.c +++ b/libexec/rtld-elf/amd64/reloc.c @@ -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); + 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; - /* Relocate the GOT slot pointing into the PLT. */ - where = (Elf_Addr *)(obj->relocbase + rela->r_offset); - *where += (Elf_Addr)obj->relocbase; + 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, RtldLockState *lockstate) 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) { diff --git a/libexec/rtld-elf/arm/reloc.c b/libexec/rtld-elf/arm/reloc.c index 8f83a8e08942..c0afba937de7 100644 --- a/libexec/rtld-elf/arm/reloc.c +++ b/libexec/rtld-elf/arm/reloc.c @@ -337,6 +337,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate) 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) diff --git a/libexec/rtld-elf/i386/reloc.c b/libexec/rtld-elf/i386/reloc.c index c9a6e53ee941..5f11106ab14c 100644 --- a/libexec/rtld-elf/i386/reloc.c +++ b/libexec/rtld-elf/i386/reloc.c @@ -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); + 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; - /* Relocate the GOT slot pointing into the PLT. */ - where = (Elf_Addr *)(obj->relocbase + rel->r_offset); - *where += (Elf_Addr)obj->relocbase; + 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, RtldLockState *lockstate) 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) { diff --git a/libexec/rtld-elf/ia64/reloc.c b/libexec/rtld-elf/ia64/reloc.c index 6ee094769be4..921e5d401bf5 100644 --- a/libexec/rtld-elf/ia64/reloc.c +++ b/libexec/rtld-elf/ia64/reloc.c @@ -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) diff --git a/libexec/rtld-elf/mips/reloc.c b/libexec/rtld-elf/mips/reloc.c index 682adcd1f265..0f538498a621 100644 --- a/libexec/rtld-elf/mips/reloc.c +++ b/libexec/rtld-elf/mips/reloc.c @@ -498,6 +498,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate) 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) diff --git a/libexec/rtld-elf/powerpc/reloc.c b/libexec/rtld-elf/powerpc/reloc.c index 4aca86edb1a9..d0128f415e84 100644 --- a/libexec/rtld-elf/powerpc/reloc.c +++ b/libexec/rtld-elf/powerpc/reloc.c @@ -504,6 +504,21 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, 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. diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c index df14af2c416a..7476a1e05730 100644 --- a/libexec/rtld-elf/powerpc64/reloc.c +++ b/libexec/rtld-elf/powerpc64/reloc.c @@ -456,6 +456,22 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, 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) { diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 0d4e97229b21..17cd67c9b0ff 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 */ diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 36b4bf67e6a6..4a752e42a285 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -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 /* } */ diff --git a/libexec/rtld-elf/sparc64/reloc.c b/libexec/rtld-elf/sparc64/reloc.c index 4cb2bca8a9d2..414523daf3b8 100644 --- a/libexec/rtld-elf/sparc64/reloc.c +++ b/libexec/rtld-elf/sparc64/reloc.c @@ -550,6 +550,22 @@ reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate) 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)