Changeset View
Standalone View
libexec/rtld-elf/aarch64/reloc.c
Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
#define RELOC_ALIGNED_P(x) \ | #define RELOC_ALIGNED_P(x) \ | ||||
(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) | (((uintptr_t)(x) & (sizeof(void *) - 1)) == 0) | ||||
/* | /* | ||||
* This is not the correct prototype, but we only need it for | * This is not the correct prototype, but we only need it for | ||||
* a function pointer to a simple asm function. | * a function pointer to a simple asm function. | ||||
*/ | */ | ||||
void *_rtld_tlsdesc(void *); | void *_rtld_tlsdesc(void *); | ||||
void *_rtld_tlsdesc_dynamic(void *); | |||||
void _exit(int); | void _exit(int); | ||||
void | void | ||||
init_pltgot(Obj_Entry *obj) | init_pltgot(Obj_Entry *obj) | ||||
{ | { | ||||
if (obj->pltgot != NULL) { | if (obj->pltgot != NULL) { | ||||
obj->pltgot[1] = (Elf_Addr) obj; | obj->pltgot[1] = (Elf_Addr) obj; | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | "Undefined symbol \"%s\" referenced from COPY relocation in %s", | ||||
srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); | srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); | ||||
memcpy(dstaddr, srcaddr, size); | memcpy(dstaddr, srcaddr, size); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
struct tls_data { | |||||
int64_t index; | |||||
Obj_Entry *obj; | |||||
const Elf_Rela *rela; | |||||
}; | |||||
static struct tls_data * | |||||
reloc_tlsdesc_alloc(Obj_Entry *obj, const Elf_Rela *rela) | |||||
{ | |||||
struct tls_data *tlsdesc; | |||||
tlsdesc = malloc(sizeof(struct tls_data)); | |||||
kib: Use xmalloc() wrapper to avoid manuall testing the allocation failure and provide standard… | |||||
if (tlsdesc == NULL) { | |||||
abort(); | |||||
return (NULL); | |||||
} | |||||
tlsdesc->index = -1; | |||||
tlsdesc->obj = obj; | |||||
tlsdesc->rela = rela; | |||||
return (tlsdesc); | |||||
} | |||||
/* | /* | ||||
* Look up the symbol to find its tls index | |||||
*/ | |||||
int64_t | |||||
rtld_tlsdesc_handle(struct tls_data *tlsdesc, int flags) | |||||
{ | |||||
RtldLockState lockstate; | |||||
const Elf_Rela *rela; | |||||
const Elf_Sym *def; | |||||
const Obj_Entry *defobj; | |||||
Obj_Entry *obj; | |||||
/* We have already found the index, return it */ | |||||
if (tlsdesc->index >= 0) | |||||
return (tlsdesc->index); | |||||
rela = tlsdesc->rela; | |||||
obj = tlsdesc->obj; | |||||
rlock_acquire(rtld_bind_lock, &lockstate); | |||||
if (sigsetjmp(lockstate.env, 0) != 0) | |||||
lock_upgrade(rtld_bind_lock, &lockstate); | |||||
def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, NULL, | |||||
&lockstate); | |||||
if (def == NULL) | |||||
abort(); | |||||
kibUnsubmitted Not Done Inline ActionsPlease use _rtld_error(). kib: Please use _rtld_error(). | |||||
andrewAuthorUnsubmitted Not Done Inline ActionsI think we want to print the message set by _rtld_error in find_symdef and exit, something like die, but that's a static function in rtld.c. andrew: I think we want to print the message set by _rtld_error in find_symdef and exit, something like… | |||||
emasteUnsubmitted Not Done Inline ActionsI think I wanted something like die() in MIPS reloc.c too. emaste: I think I wanted something like die() in MIPS reloc.c too.
| |||||
kibUnsubmitted Not Done Inline ActionsAFAIR, in MIPS case the use of die() is bogus. Indeed, https://reviews.freebsd.org/D661 As usual, MIPS people do not care. kib: AFAIR, in MIPS case the use of die() is bogus.
Indeed, https://reviews.freebsd.org/D661
As… | |||||
emasteUnsubmitted Not Done Inline ActionsAh, indeed that is the change I was thinking about. emaste: Ah, indeed that is the change I was thinking about.
| |||||
andrewAuthorUnsubmitted Not Done Inline ActionsIs the above patch going in or do I need to do something similar here? andrew: Is the above patch going in or do I need to do something similar here? | |||||
kibUnsubmitted Not Done Inline ActionsI think you need to expose die(). Now, with the -fvisibility=hidden being used on x86, I am much less sensitive to making functions non-static. You could consider setting hidden for aarch64. kib: I think you need to expose die(). Now, with the -fvisibility=hidden being used on x86, I am… | |||||
emasteUnsubmitted Not Done Inline ActionsI still think there's some value in renaming it _rtld_die too, from a grepability perspective. NetBSD's rtld for example calls it _rtld_die. emaste: I still think there's some value in renaming it `_rtld_die` too, from a grepability perspective. | |||||
tlsdesc->index = def->st_value + rela->r_addend; | |||||
kibUnsubmitted Not Done Inline ActionsCould you, please, clarify the rules for sharing the tls_data items between threads ? Is it thread-private, or could it be that two threads try to perform rtld_tlsdesc_handle() simultaneously on the same tlsdesc ? I am mostly wondering about read-lock, is it enough or the case needs write-lock due to modifications to tlsdesc. Note that it is possible that symbol lookup returns different results for separate invocations. kib: Could you, please, clarify the rules for sharing the tls_data items between threads ? Is it… | |||||
andrewAuthorUnsubmitted Not Done Inline ActionsWe need to look up the symbol to find it's offset from the thread pointer. The details are in [1], and in this case we are interested in TPREL(S+A) on page 23. I will need to add the offset of the objects tls data. Currently it returns the offset within the objects data, but this will lead two objects pointing to the same data. Two threads could call this if they both access the same thread local data concurrently. I'll change it to use a write lock to handle this case. Is the reloc_jmpslots case safe if I use the lock provided, or will I need to upgrade it to a write lock? [1] http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056c/IHI0056C_beta_aaelf64.pdf andrew: We need to look up the symbol to find it's offset from the thread pointer. The details are in… | |||||
kibUnsubmitted Not Done Inline ActionsRe-using the provided lockstate should be fine. reloc_jmpslots() is called either by _rtld(), when the process is guaranteed to be single-threaded, or from the dlopen(). In the later case, rtld_bind_lock is write-locked. What worries me somewhat, is the use of rtld_tlsdesc_handle() from rtld_tlsdesc_dynamic(). I suppose you would need one more wrapper. kib: Re-using the provided lockstate should be fine. reloc_jmpslots() is called either by _rtld()… | |||||
andrewAuthorUnsubmitted Not Done Inline ActionsI have a fix for this, but an unable to push it as I need to decide how best to handle the case above where I need something like die(). andrew: I have a fix for this, but an unable to push it as I need to decide how best to handle the case… | |||||
lock_release(rtld_bind_lock, &lockstate); | |||||
return (tlsdesc->index); | |||||
} | |||||
/* | |||||
* Process the PLT relocations. | * Process the PLT relocations. | ||||
*/ | */ | ||||
int | int | ||||
reloc_plt(Obj_Entry *obj) | reloc_plt(Obj_Entry *obj) | ||||
{ | { | ||||
const Elf_Rela *relalim; | const Elf_Rela *relalim; | ||||
const Elf_Rela *rela; | const Elf_Rela *rela; | ||||
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; | ||||
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: | ||||
*where += (Elf_Addr)obj->relocbase; | *where += (Elf_Addr)obj->relocbase; | ||||
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) { | ||||
where[0] = (Elf_Addr)_rtld_tlsdesc; | where[0] = (Elf_Addr)_rtld_tlsdesc; | ||||
where[1] = rela->r_addend; | where[1] = rela->r_addend; | ||||
andrewAuthorUnsubmitted Not Done Inline ActionsI think this will need to change to: andrew: I think this will need to change to:
`where[1] = obj->tlsindex + rela->r_addend;` | |||||
} else { | } else { | ||||
_rtld_error("Unable to handle " | where[0] = (Elf_Addr)_rtld_tlsdesc_dynamic; | ||||
"R_AARCH64_TLSDESC with a symbol set"); | where[1] = (Elf_Addr)reloc_tlsdesc_alloc(obj, | ||||
return (-1); | rela); | ||||
} | } | ||||
break; | 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; | |||||
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; | ||||
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: | ||||
where = (Elf_Addr *)(obj->relocbase + rela->r_offset); | |||||
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"); | dbg("reloc_jmpslots: sym not found"); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
*where = (Elf_Addr)(defobj->relocbase + def->st_value); | *where = (Elf_Addr)(defobj->relocbase + def->st_value); | ||||
break; | break; | ||||
case R_AARCH64_TLSDESC: | case R_AARCH64_TLSDESC: | ||||
if (ELF_R_SYM(rela->r_info) != 0) { | |||||
tlsdesc = (struct tls_data *)where[1]; | |||||
rtld_tlsdesc_handle(tlsdesc, | |||||
SYMLOOK_IN_PLT | flags); | |||||
} | |||||
kibUnsubmitted Not Done Inline ActionsHmm, I think you should pass lockstate down to tld_tlsdesc_handle() and then for find_symdef(), instead of recursively locking the bind lock. In fact,what you do could deadlock. kib: Hmm, I think you should pass lockstate down to tld_tlsdesc_handle() and then for find_symdef()… | |||||
andrewAuthorUnsubmitted Not Done Inline ActionsOk andrew: Ok | |||||
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); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | case R_AARCH64_COPY: | ||||
* 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; | |||||
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 | |||||
* see the first relocation that references the | |||||
* TLS block. This allows us to support (small | |||||
* amounts of) static TLS in dynamically loaded | |||||
* modules. If we run out of space, we generate an | |||||
* error. | |||||
*/ | |||||
if (!defobj->tls_done) { | |||||
if (!allocate_tls_offset((Obj_Entry*) defobj)) { | |||||
_rtld_error( | |||||
"%s: No space available for static " | |||||
"Thread Local Storage", obj->path); | |||||
return (-1); | |||||
} | |||||
} | |||||
*where = def->st_value + rela->r_addend + | |||||
defobj->tlsoffset - TLS_TCB_SIZE; | |||||
break; | break; | ||||
case R_AARCH64_RELATIVE: | case R_AARCH64_RELATIVE: | ||||
*where = (Elf_Addr)(obj->relocbase + rela->r_addend); | *where = (Elf_Addr)(obj->relocbase + rela->r_addend); | ||||
break; | break; | ||||
default: | default: | ||||
rtld_printf("%s: Unhandled relocation %lu\n", | rtld_printf("%s: Unhandled relocation %lu\n", | ||||
obj->path, ELF_R_TYPE(rela->r_info)); | obj->path, ELF_R_TYPE(rela->r_info)); | ||||
return (-1); | return (-1); | ||||
Show All 23 Lines |
Use xmalloc() wrapper to avoid manuall testing the allocation failure and provide standard error message.