Changeset View
Changeset View
Standalone View
Standalone View
head/libexec/rtld-elf/aarch64/reloc.c
Show First 20 Lines • Show All 212 Lines • ▼ Show 20 Lines | for (rela = obj->pltrela; rela < relalim; rela++) { | ||||
switch(ELF_R_TYPE(rela->r_info)) { | switch(ELF_R_TYPE(rela->r_info)) { | ||||
case R_AARCH64_JUMP_SLOT: | case R_AARCH64_JUMP_SLOT: | ||||
*where += (Elf_Addr)obj->relocbase; | *where += (Elf_Addr)obj->relocbase; | ||||
break; | break; | ||||
case R_AARCH64_TLSDESC: | case R_AARCH64_TLSDESC: | ||||
reloc_tlsdesc(obj, rela, where); | reloc_tlsdesc(obj, rela, where); | ||||
break; | break; | ||||
case R_AARCH64_IRELATIVE: | |||||
obj->irelative = true; | |||||
break; | |||||
default: | default: | ||||
_rtld_error("Unknown relocation type %u in PLT", | _rtld_error("Unknown relocation type %u in PLT", | ||||
(unsigned int)ELF_R_TYPE(rela->r_info)); | (unsigned int)ELF_R_TYPE(rela->r_info)); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* LD_BIND_NOW was set - force relocation for all jump slots | * LD_BIND_NOW was set - force relocation for all jump slots | ||||
*/ | */ | ||||
int | int | ||||
reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) | reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) | ||||
{ | { | ||||
const Obj_Entry *defobj; | const Obj_Entry *defobj; | ||||
const Elf_Rela *relalim; | const Elf_Rela *relalim; | ||||
const Elf_Rela *rela; | const Elf_Rela *rela; | ||||
const Elf_Sym *def; | const Elf_Sym *def; | ||||
struct tls_data *tlsdesc; | struct tls_data *tlsdesc; | ||||
relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); | relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); | ||||
for (rela = obj->pltrela; rela < relalim; rela++) { | for (rela = obj->pltrela; rela < relalim; rela++) { | ||||
Elf_Addr *where; | Elf_Addr *where, target; | ||||
where = (Elf_Addr *)(obj->relocbase + rela->r_offset); | where = (Elf_Addr *)(obj->relocbase + rela->r_offset); | ||||
switch(ELF_R_TYPE(rela->r_info)) { | switch(ELF_R_TYPE(rela->r_info)) { | ||||
case R_AARCH64_JUMP_SLOT: | case R_AARCH64_JUMP_SLOT: | ||||
def = find_symdef(ELF_R_SYM(rela->r_info), obj, | def = find_symdef(ELF_R_SYM(rela->r_info), obj, | ||||
&defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); | &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); | ||||
if (def == NULL) { | if (def == NULL) | ||||
dbg("reloc_jmpslots: sym not found"); | |||||
return (-1); | 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); | |||||
*where = (Elf_Addr)(defobj->relocbase + def->st_value); | reloc_jmpslot(where, target, defobj, obj, | ||||
(const Elf_Rel *)rela); | |||||
break; | break; | ||||
case R_AARCH64_TLSDESC: | case R_AARCH64_TLSDESC: | ||||
if (ELF_R_SYM(rela->r_info) != 0) { | if (ELF_R_SYM(rela->r_info) != 0) { | ||||
tlsdesc = (struct tls_data *)where[1]; | tlsdesc = (struct tls_data *)where[1]; | ||||
if (tlsdesc->index == -1) | if (tlsdesc->index == -1) | ||||
rtld_tlsdesc_handle_locked(tlsdesc, | rtld_tlsdesc_handle_locked(tlsdesc, | ||||
SYMLOOK_IN_PLT | flags, lockstate); | SYMLOOK_IN_PLT | flags, lockstate); | ||||
} | } | ||||
break; | break; | ||||
default: | default: | ||||
_rtld_error("Unknown relocation type %x in jmpslot", | _rtld_error("Unknown relocation type %x in jmpslot", | ||||
(unsigned int)ELF_R_TYPE(rela->r_info)); | (unsigned int)ELF_R_TYPE(rela->r_info)); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) | reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) | ||||
{ | { | ||||
const Elf_Rela *relalim; | |||||
const Elf_Rela *rela; | |||||
Elf_Addr *where, target, *ptr; | |||||
/* XXX not implemented */ | if (!obj->irelative) | ||||
return (0); | 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_AARCH64_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 | int | ||||
reloc_gnu_ifunc(Obj_Entry *obj, int flags, | reloc_gnu_ifunc(Obj_Entry *obj, int flags, | ||||
struct Struct_RtldLockState *lockstate) | struct Struct_RtldLockState *lockstate) | ||||
{ | { | ||||
const Elf_Rela *relalim; | |||||
const Elf_Rela *rela; | |||||
Elf_Addr *where, target; | |||||
const Elf_Sym *def; | |||||
const Obj_Entry *defobj; | |||||
/* XXX not implemented */ | if (!obj->gnu_ifunc) | ||||
return (0); | 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_AARCH64_JUMP_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); | |||||
} | |||||
Elf_Addr | Elf_Addr | ||||
reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, | reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, | ||||
const Obj_Entry *obj, const Elf_Rel *rel) | const Obj_Entry *obj, const Elf_Rel *rel) | ||||
{ | { | ||||
assert(ELF_R_TYPE(rel->r_info) == R_AARCH64_JUMP_SLOT); | assert(ELF_R_TYPE(rel->r_info) == R_AARCH64_JUMP_SLOT || | ||||
ELF_R_TYPE(rel->r_info) == R_AARCH64_IRELATIVE); | |||||
if (*where != target && !ld_bind_not) | if (*where != target && !ld_bind_not) | ||||
*where = target; | *where = target; | ||||
return (target); | return (target); | ||||
} | } | ||||
void | void | ||||
ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) | ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) | ||||
Show All 14 Lines | |||||
reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, | reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, | ||||
RtldLockState *lockstate) | RtldLockState *lockstate) | ||||
{ | { | ||||
const Obj_Entry *defobj; | const Obj_Entry *defobj; | ||||
const Elf_Rela *relalim; | const Elf_Rela *relalim; | ||||
const Elf_Rela *rela; | const Elf_Rela *rela; | ||||
const Elf_Sym *def; | const Elf_Sym *def; | ||||
SymCache *cache; | SymCache *cache; | ||||
Elf_Addr *where; | Elf_Addr *where, symval; | ||||
unsigned long symnum; | |||||
if ((flags & SYMLOOK_IFUNC) != 0) | |||||
/* XXX not implemented */ | |||||
return (0); | |||||
/* | /* | ||||
* The dynamic loader may be called from a thread, we have | * The dynamic loader may be called from a thread, we have | ||||
* limited amounts of stack available so we cannot use alloca(). | * limited amounts of stack available so we cannot use alloca(). | ||||
*/ | */ | ||||
if (obj == obj_rtld) | if (obj == obj_rtld) | ||||
cache = NULL; | cache = NULL; | ||||
else | else | ||||
cache = calloc(obj->dynsymcount, sizeof(SymCache)); | cache = calloc(obj->dynsymcount, sizeof(SymCache)); | ||||
/* No need to check for NULL here */ | /* No need to check for NULL here */ | ||||
relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); | relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); | ||||
for (rela = obj->rela; rela < relalim; rela++) { | for (rela = obj->rela; rela < relalim; rela++) { | ||||
where = (Elf_Addr *)(obj->relocbase + rela->r_offset); | /* | ||||
symnum = ELF_R_SYM(rela->r_info); | * First, resolve symbol for relocations which | ||||
* reference symbols. | |||||
*/ | |||||
switch (ELF_R_TYPE(rela->r_info)) { | switch (ELF_R_TYPE(rela->r_info)) { | ||||
case R_AARCH64_ABS64: | case R_AARCH64_ABS64: | ||||
case R_AARCH64_GLOB_DAT: | case R_AARCH64_GLOB_DAT: | ||||
def = find_symdef(symnum, obj, &defobj, flags, cache, | case R_AARCH64_TLS_TPREL64: | ||||
lockstate); | def = find_symdef(ELF_R_SYM(rela->r_info), obj, | ||||
&defobj, flags, cache, lockstate); | |||||
if (def == NULL) | if (def == NULL) | ||||
return (-1); | return (-1); | ||||
/* | |||||
* If symbol is IFUNC, only perform relocation | |||||
* when caller allowed it by passing | |||||
* SYMLOOK_IFUNC flag. Skip the relocations | |||||
* otherwise. | |||||
* | |||||
* Also error out in case IFUNC relocations | |||||
* are specified for TLS, which cannot be | |||||
* usefully interpreted. | |||||
*/ | |||||
if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { | |||||
switch (ELF_R_TYPE(rela->r_info)) { | |||||
case R_AARCH64_ABS64: | |||||
case R_AARCH64_GLOB_DAT: | |||||
if ((flags & SYMLOOK_IFUNC) == 0) { | |||||
obj->non_plt_gnu_ifunc = true; | |||||
continue; | |||||
} | |||||
symval = (Elf_Addr)rtld_resolve_ifunc( | |||||
defobj, def); | |||||
break; | |||||
default: | |||||
_rtld_error("%s: IFUNC for TLS reloc", | |||||
obj->path); | |||||
return (-1); | |||||
} | |||||
} else { | |||||
if ((flags & SYMLOOK_IFUNC) != 0) | |||||
continue; | |||||
symval = (Elf_Addr)defobj->relocbase + | |||||
def->st_value; | |||||
} | |||||
break; | |||||
default: | |||||
if ((flags & SYMLOOK_IFUNC) != 0) | |||||
continue; | |||||
} | |||||
*where = (Elf_Addr)defobj->relocbase + def->st_value + | where = (Elf_Addr *)(obj->relocbase + rela->r_offset); | ||||
rela->r_addend; | |||||
switch (ELF_R_TYPE(rela->r_info)) { | |||||
case R_AARCH64_ABS64: | |||||
case R_AARCH64_GLOB_DAT: | |||||
*where = symval + rela->r_addend; | |||||
break; | break; | ||||
case R_AARCH64_COPY: | case R_AARCH64_COPY: | ||||
/* | /* | ||||
* These are deferred until all other relocations have | * These are deferred until all other relocations have | ||||
* been done. All we do here is make sure that the | * been done. All we do here is make sure that the | ||||
* COPY relocation is not in a shared library. They | * COPY relocation is not in a shared library. They | ||||
* are allowed only in executable files. | * are allowed only in executable files. | ||||
*/ | */ | ||||
if (!obj->mainprog) { | if (!obj->mainprog) { | ||||
_rtld_error("%s: Unexpected R_AARCH64_COPY " | _rtld_error("%s: Unexpected R_AARCH64_COPY " | ||||
"relocation in shared library", obj->path); | "relocation in shared library", obj->path); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
break; | break; | ||||
case R_AARCH64_TLSDESC: | case R_AARCH64_TLSDESC: | ||||
reloc_tlsdesc(obj, rela, where); | reloc_tlsdesc(obj, rela, where); | ||||
break; | break; | ||||
case R_AARCH64_TLS_TPREL64: | case R_AARCH64_TLS_TPREL64: | ||||
def = find_symdef(symnum, obj, &defobj, flags, cache, | |||||
lockstate); | |||||
if (def == NULL) | |||||
return (-1); | |||||
/* | /* | ||||
* We lazily allocate offsets for static TLS as we | * We lazily allocate offsets for static TLS as we | ||||
* see the first relocation that references the | * see the first relocation that references the | ||||
* TLS block. This allows us to support (small | * TLS block. This allows us to support (small | ||||
* amounts of) static TLS in dynamically loaded | * amounts of) static TLS in dynamically loaded | ||||
* modules. If we run out of space, we generate an | * modules. If we run out of space, we generate an | ||||
* error. | * error. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 42 Lines • Show Last 20 Lines |