diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c index 1a78f32312f..29d1fb7d61a 100644 --- a/libexec/rtld-elf/powerpc64/reloc.c +++ b/libexec/rtld-elf/powerpc64/reloc.c @@ -337,9 +337,8 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, * Initialise a PLT slot to the resolving trampoline */ static int -reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) +reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela, Elf_Addr *where) { - Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); long reloff; reloff = rela - obj->pltrela; @@ -373,9 +372,20 @@ reloc_plt(Obj_Entry *obj) relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { - assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); + Elf_Addr *where; - if (reloc_plt_object(obj, rela) < 0) { + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + + switch(ELF_R_TYPE(rela->r_info)) { + case R_PPC_JMP_SLOT: + reloc_plt_object(obj, rela, where); + break; + case R_PPC64_IRELATIVE: + obj->irelative = true; + break; + default: + _rtld_error("Unknown relocation type %u in PLT", + (unsigned int)ELF_R_TYPE(rela->r_info)); return (-1); } } @@ -395,32 +405,43 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; - Elf_Addr *where; - Elf_Addr target; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { - assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); - where = (Elf_Addr *)(obj->relocbase + rela->r_offset); - def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, - SYMLOOK_IN_PLT | flags, NULL, lockstate); - if (def == NULL) { - dbg("reloc_jmpslots: sym not found"); - return (-1); - } + Elf_Addr *where, target; - target = (Elf_Addr)(defobj->relocbase + def->st_value); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + switch(ELF_R_TYPE(rela->r_info)) { + case R_PPC_JMP_SLOT: + def = find_symdef(ELF_R_SYM(rela->r_info), obj, + &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) { + dbg("reloc_jmpslots: sym not found"); + 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); - if (def == &sym_zero) { - /* Zero undefined weak symbols */ + if (def == &sym_zero) { + /* Zero undefined weak symbols */ #if !defined(_CALL_ELF) || _CALL_ELF == 1 - bzero(where, sizeof(struct funcdesc)); + bzero(where, sizeof(struct funcdesc)); #else - *where = 0; + *where = 0; #endif - } else { - reloc_jmpslot(where, target, defobj, obj, - (const Elf_Rel *) rela); + } else { + reloc_jmpslot(where, target, defobj, obj, + (const Elf_Rel *) rela); + } + break; + /* XXX implement TLS relocs? */ + default: + _rtld_error("Unknown relocation type %x in jmpslot", + (unsigned int)ELF_R_TYPE(rela->r_info)); + return (-1); } } @@ -437,6 +458,9 @@ Elf_Addr reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, const Obj_Entry *obj, const Elf_Rel *rel) { + assert(ELF_R_TYPE(rel->r_info) == R_PPC_JMP_SLOT || + ELF_R_TYPE(rel->r_info) == R_PPC64_IRELATIVE); + /* * At the PLT entry pointed at by `wherep', construct @@ -496,18 +520,58 @@ reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { - - /* XXX not implemented */ - return (0); + const Elf_Rela *relalim; + const Elf_Rela *rela; + Elf_Addr *where, target, *ptr; + + if (!obj->irelative) + return (0); + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) == R_PPC64_IRELATIVE) { + ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + lock_release(rtld_bind_lock, lockstate); + target = call_ifunc_resolver(ptr); + wlock_acquire(rtld_bind_lock, lockstate); + *where = target; + } + } + obj->irelative = false; + return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, struct Struct_RtldLockState *lockstate) { - - /* XXX not implemented */ - return (0); + const Elf_Rela *relalim; + const Elf_Rela *rela; + Elf_Addr *where, target; + const Elf_Sym *def; + const Obj_Entry *defobj; + + if (!obj->gnu_ifunc) + return (0); + relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); + for (rela = obj->pltrela; rela < relalim; rela++) { + if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) { + where = (Elf_Addr *)(obj->relocbase + rela->r_offset); + def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, + SYMLOOK_IN_PLT | flags, NULL, lockstate); + if (def == NULL) + return (-1); + if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) + continue; + lock_release(rtld_bind_lock, lockstate); + target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); + wlock_acquire(rtld_bind_lock, lockstate); + reloc_jmpslot(where, target, defobj, obj, + (const Elf_Rel *)rela); + } + } + obj->gnu_ifunc = false; + return (0); } void