summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf/amd64
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2011-12-12 11:03:14 +0000
committerkib <kib@FreeBSD.org>2011-12-12 11:03:14 +0000
commita95b8e72c8b8615806e884acc5ccce614fae0370 (patch)
treed5f18ee2f1f28d34c0603cd801c3d94d87e79adf /libexec/rtld-elf/amd64
parente4d5e370653906483ad3fd23a8e15acb12b82ab5 (diff)
downloadFreeBSD-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/amd64')
-rw-r--r--libexec/rtld-elf/amd64/reloc.c109
1 files changed, 96 insertions, 13 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)
{
OpenPOWER on IntegriCloud