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
This commit is contained in:
Konstantin Belousov 2011-12-12 11:03:14 +00:00
parent 4038b79831
commit 6be4b69715
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=228435
10 changed files with 332 additions and 33 deletions

View file

@ -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)
{

View file

@ -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)

View file

@ -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)
{

View file

@ -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)

View file

@ -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)

View file

@ -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.

View file

@ -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)
{

View file

@ -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 */

View file

@ -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 /* } */

View file

@ -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)