Make p_vaddr % p_align == p_offset % p_align for (some) TLS segments.
See https://sourceware.org/bugzilla/show_bug.cgi?id=24606 for the test case. See https://reviews.llvm.org/D64930 for the background and more discussion. Also this fixes another bug in malloc_aligned() where total size of the allocated memory might be not enough to fit the aligned requested block after the initial pointer is incremented by the pointer size. Reviewed by: bdragon Tested by: antoine (exp-run PR 244866), bdragon, emaste Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D21163
This commit is contained in:
parent
ff595bc3cd
commit
2f06c66ad5
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=359634
@ -313,6 +313,7 @@ map_object(int fd, const char *path, const struct stat *sb)
|
|||||||
obj->tlsindex = ++tls_max_index;
|
obj->tlsindex = ++tls_max_index;
|
||||||
obj->tlssize = phtls->p_memsz;
|
obj->tlssize = phtls->p_memsz;
|
||||||
obj->tlsalign = phtls->p_align;
|
obj->tlsalign = phtls->p_align;
|
||||||
|
obj->tlspoffset = phtls->p_offset;
|
||||||
obj->tlsinitsize = phtls->p_filesz;
|
obj->tlsinitsize = phtls->p_filesz;
|
||||||
obj->tlsinit = mapbase + phtls->p_vaddr;
|
obj->tlsinit = mapbase + phtls->p_vaddr;
|
||||||
}
|
}
|
||||||
|
@ -1501,6 +1501,7 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
|
|||||||
obj->tlsalign = ph->p_align;
|
obj->tlsalign = ph->p_align;
|
||||||
obj->tlsinitsize = ph->p_filesz;
|
obj->tlsinitsize = ph->p_filesz;
|
||||||
obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase);
|
obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase);
|
||||||
|
obj->tlspoffset = ph->p_offset;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PT_GNU_STACK:
|
case PT_GNU_STACK:
|
||||||
@ -4868,7 +4869,7 @@ allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
|
|||||||
Elf_Addr addr;
|
Elf_Addr addr;
|
||||||
Elf_Addr i;
|
Elf_Addr i;
|
||||||
size_t extra_size, maxalign, post_size, pre_size, tls_block_size;
|
size_t extra_size, maxalign, post_size, pre_size, tls_block_size;
|
||||||
size_t tls_init_align;
|
size_t tls_init_align, tls_init_offset;
|
||||||
|
|
||||||
if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
|
if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE)
|
||||||
return (oldtcb);
|
return (oldtcb);
|
||||||
@ -4885,7 +4886,7 @@ allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
|
|||||||
tls_block_size += pre_size + tls_static_space - TLS_TCB_SIZE - post_size;
|
tls_block_size += pre_size + tls_static_space - TLS_TCB_SIZE - post_size;
|
||||||
|
|
||||||
/* Allocate whole TLS block */
|
/* Allocate whole TLS block */
|
||||||
tls_block = malloc_aligned(tls_block_size, maxalign);
|
tls_block = malloc_aligned(tls_block_size, maxalign, 0);
|
||||||
tcb = (Elf_Addr **)(tls_block + pre_size + extra_size);
|
tcb = (Elf_Addr **)(tls_block + pre_size + extra_size);
|
||||||
|
|
||||||
if (oldtcb != NULL) {
|
if (oldtcb != NULL) {
|
||||||
@ -4909,15 +4910,21 @@ allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign)
|
|||||||
|
|
||||||
for (obj = globallist_curr(objs); obj != NULL;
|
for (obj = globallist_curr(objs); obj != NULL;
|
||||||
obj = globallist_next(obj)) {
|
obj = globallist_next(obj)) {
|
||||||
if (obj->tlsoffset > 0) {
|
if (obj->tlsoffset == 0)
|
||||||
addr = (Elf_Addr)tcb + obj->tlsoffset;
|
continue;
|
||||||
if (obj->tlsinitsize > 0)
|
tls_init_offset = obj->tlspoffset & (obj->tlsalign - 1);
|
||||||
memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize);
|
addr = (Elf_Addr)tcb + obj->tlsoffset;
|
||||||
if (obj->tlssize > obj->tlsinitsize)
|
if (tls_init_offset > 0)
|
||||||
memset((void*)(addr + obj->tlsinitsize), 0,
|
memset((void *)addr, 0, tls_init_offset);
|
||||||
obj->tlssize - obj->tlsinitsize);
|
if (obj->tlsinitsize > 0) {
|
||||||
dtv[obj->tlsindex + 1] = addr;
|
memcpy((void *)(addr + tls_init_offset), obj->tlsinit,
|
||||||
|
obj->tlsinitsize);
|
||||||
}
|
}
|
||||||
|
if (obj->tlssize > obj->tlsinitsize) {
|
||||||
|
memset((void *)(addr + tls_init_offset + obj->tlsinitsize),
|
||||||
|
0, obj->tlssize - obj->tlsinitsize - tls_init_offset);
|
||||||
|
}
|
||||||
|
dtv[obj->tlsindex + 1] = addr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4975,7 +4982,7 @@ allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign)
|
|||||||
size = round(tls_static_space, ralign) + round(tcbsize, ralign);
|
size = round(tls_static_space, ralign) + round(tcbsize, ralign);
|
||||||
|
|
||||||
assert(tcbsize >= 2*sizeof(Elf_Addr));
|
assert(tcbsize >= 2*sizeof(Elf_Addr));
|
||||||
tls = malloc_aligned(size, ralign);
|
tls = malloc_aligned(size, ralign, 0 /* XXX */);
|
||||||
dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr));
|
dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr));
|
||||||
|
|
||||||
segbase = (Elf_Addr)(tls + round(tls_static_space, ralign));
|
segbase = (Elf_Addr)(tls + round(tls_static_space, ralign));
|
||||||
@ -5068,25 +5075,24 @@ free_tls(void *tls, size_t tcbsize __unused, size_t tcbalign)
|
|||||||
void *
|
void *
|
||||||
allocate_module_tls(int index)
|
allocate_module_tls(int index)
|
||||||
{
|
{
|
||||||
Obj_Entry* obj;
|
Obj_Entry *obj;
|
||||||
char* p;
|
char *p;
|
||||||
|
|
||||||
TAILQ_FOREACH(obj, &obj_list, next) {
|
TAILQ_FOREACH(obj, &obj_list, next) {
|
||||||
if (obj->marker)
|
if (obj->marker)
|
||||||
continue;
|
continue;
|
||||||
if (obj->tlsindex == index)
|
if (obj->tlsindex == index)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!obj) {
|
if (obj == NULL) {
|
||||||
_rtld_error("Can't find module with TLS index %d", index);
|
_rtld_error("Can't find module with TLS index %d", index);
|
||||||
rtld_die();
|
rtld_die();
|
||||||
}
|
}
|
||||||
|
|
||||||
p = malloc_aligned(obj->tlssize, obj->tlsalign);
|
p = malloc_aligned(obj->tlssize, obj->tlsalign, obj->tlspoffset);
|
||||||
memcpy(p, obj->tlsinit, obj->tlsinitsize);
|
memcpy(p, obj->tlsinit, obj->tlsinitsize);
|
||||||
memset(p + obj->tlsinitsize, 0, obj->tlssize - obj->tlsinitsize);
|
memset(p + obj->tlsinitsize, 0, obj->tlssize - obj->tlsinitsize);
|
||||||
|
return (p);
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -163,6 +163,7 @@ typedef struct Struct_Obj_Entry {
|
|||||||
size_t tlssize; /* Size of TLS block for this module */
|
size_t tlssize; /* Size of TLS block for this module */
|
||||||
size_t tlsoffset; /* Offset of static TLS block for this module */
|
size_t tlsoffset; /* Offset of static TLS block for this module */
|
||||||
size_t tlsalign; /* Alignment of static TLS block */
|
size_t tlsalign; /* Alignment of static TLS block */
|
||||||
|
size_t tlspoffset; /* p_offset of the static TLS block */
|
||||||
|
|
||||||
caddr_t relro_page;
|
caddr_t relro_page;
|
||||||
size_t relro_size;
|
size_t relro_size;
|
||||||
@ -361,7 +362,7 @@ Obj_Entry *map_object(int, const char *, const struct stat *);
|
|||||||
void *xcalloc(size_t, size_t);
|
void *xcalloc(size_t, size_t);
|
||||||
void *xmalloc(size_t);
|
void *xmalloc(size_t);
|
||||||
char *xstrdup(const char *);
|
char *xstrdup(const char *);
|
||||||
void *malloc_aligned(size_t size, size_t align);
|
void *malloc_aligned(size_t size, size_t align, size_t offset);
|
||||||
void free_aligned(void *ptr);
|
void free_aligned(void *ptr);
|
||||||
extern Elf_Addr _GLOBAL_OFFSET_TABLE_[];
|
extern Elf_Addr _GLOBAL_OFFSET_TABLE_[];
|
||||||
extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */
|
extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
* $FreeBSD$
|
* $FreeBSD$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -76,16 +77,21 @@ xstrdup(const char *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
malloc_aligned(size_t size, size_t align)
|
malloc_aligned(size_t size, size_t align, size_t offset)
|
||||||
{
|
{
|
||||||
void *mem, *res;
|
char *mem, *res;
|
||||||
|
uintptr_t x;
|
||||||
|
|
||||||
|
offset &= align - 1;
|
||||||
if (align < sizeof(void *))
|
if (align < sizeof(void *))
|
||||||
align = sizeof(void *);
|
align = sizeof(void *);
|
||||||
|
|
||||||
mem = xmalloc(size + sizeof(void *) + align - 1);
|
mem = xmalloc(size + 3 * align + offset);
|
||||||
res = (void *)round((uintptr_t)mem + sizeof(void *), align);
|
x = roundup((uintptr_t)mem + sizeof(void *), align);
|
||||||
*(void **)((uintptr_t)res - sizeof(void *)) = mem;
|
x += offset;
|
||||||
|
res = (void *)x;
|
||||||
|
x -= sizeof(void *);
|
||||||
|
memcpy((void *)x, &mem, sizeof(mem));
|
||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +105,6 @@ free_aligned(void *ptr)
|
|||||||
return;
|
return;
|
||||||
x = (uintptr_t)ptr;
|
x = (uintptr_t)ptr;
|
||||||
x -= sizeof(void *);
|
x -= sizeof(void *);
|
||||||
mem = *(void **)x;
|
memcpy(&mem, (void *)x, sizeof(mem));
|
||||||
free(mem);
|
free(mem);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user