diff options
author | kib <kib@FreeBSD.org> | 2011-12-12 11:03:14 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2011-12-12 11:03:14 +0000 |
commit | a95b8e72c8b8615806e884acc5ccce614fae0370 (patch) | |
tree | d5f18ee2f1f28d34c0603cd801c3d94d87e79adf /libexec/rtld-elf/i386 | |
parent | e4d5e370653906483ad3fd23a8e15acb12b82ab5 (diff) | |
download | FreeBSD-src-a95b8e72c8b8615806e884acc5ccce614fae0370.zip FreeBSD-src-a95b8e72c8b8615806e884acc5ccce614fae0370.tar.gz |
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
Diffstat (limited to 'libexec/rtld-elf/i386')
-rw-r--r-- | libexec/rtld-elf/i386/reloc.c | 110 |
1 files changed, 95 insertions, 15 deletions
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) { |