summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/rtld-elf')
-rw-r--r--libexec/rtld-elf/amd64/reloc.c109
-rw-r--r--libexec/rtld-elf/arm/reloc.c16
-rw-r--r--libexec/rtld-elf/i386/reloc.c110
-rw-r--r--libexec/rtld-elf/ia64/reloc.c16
-rw-r--r--libexec/rtld-elf/mips/reloc.c16
-rw-r--r--libexec/rtld-elf/powerpc/reloc.c15
-rw-r--r--libexec/rtld-elf/powerpc64/reloc.c16
-rw-r--r--libexec/rtld-elf/rtld.c52
-rw-r--r--libexec/rtld-elf/rtld.h5
-rw-r--r--libexec/rtld-elf/sparc64/reloc.c16
10 files changed, 335 insertions, 36 deletions
diff --git a/libexec/rtld-elf/amd64/reloc.c b/libexec/rtld-elf/amd64/reloc.c
index 0bfc4fe..5ae8493 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);
-
- /* 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, 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 8f83a8e..c0afba9 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 c9a6e53..5f11106 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;
-
- 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;
+ Elf_Addr *where/*, val*/;
+
+ 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, 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 6ee0947..921e5d4 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 682adcd..0f53849 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 4aca86e..d0128f4 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 df14af2..7476a1e 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 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 */
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index 36b4bf6..4a752e4 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 4cb2bca..414523d 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)
OpenPOWER on IntegriCloud