add system for resetting TLS to initial values

this is needed for reused threads in the SIGEV_THREAD timer
notification system, and could be reused elsewhere in the future if
needed, though it should be refactored for such use.

for static linking, __init_tls.c is simply modified to export the TLS
info in a structure with external linkage, rather than using statics.
this perhaps makes the code more clear, since the statics were poorly
named for statics. the new __reset_tls.c is only linked if it is used.

for dynamic linking, the code is in dynlink.c. sharing code with
__copy_tls is not practical since __reset_tls must also re-zero
thread-local bss.
This commit is contained in:
Rich Felker 2013-08-03 16:27:30 -04:00
parent 7356c2554e
commit 7c6c290695
4 changed files with 56 additions and 14 deletions

32
src/env/__init_tls.c vendored
View File

@ -8,29 +8,33 @@
#ifndef SHARED
static void *image;
static size_t len, size, align;
struct tls_image {
void *image;
size_t len, size, align;
} __static_tls ATTR_LIBC_VISIBILITY;
#define T __static_tls
void *__copy_tls(unsigned char *mem)
{
pthread_t td;
if (!image) return mem;
if (!T.image) return mem;
void **dtv = (void *)mem;
dtv[0] = (void *)1;
#ifdef TLS_ABOVE_TP
mem += sizeof(void *) * 2;
mem += -((uintptr_t)mem + sizeof(struct pthread)) & (align-1);
mem += -((uintptr_t)mem + sizeof(struct pthread)) & (T.align-1);
td = (pthread_t)mem;
mem += sizeof(struct pthread);
#else
mem += libc.tls_size - sizeof(struct pthread);
mem -= (uintptr_t)mem & (align-1);
mem -= (uintptr_t)mem & (T.align-1);
td = (pthread_t)mem;
mem -= size;
mem -= T.size;
#endif
td->dtv = dtv;
dtv[1] = mem;
memcpy(mem, image, len);
memcpy(mem, T.image, T.len);
return td;
}
@ -73,15 +77,15 @@ void __init_tls(size_t *aux)
}
if (!tls_phdr) return;
image = (void *)(base + tls_phdr->p_vaddr);
len = tls_phdr->p_filesz;
size = tls_phdr->p_memsz;
align = tls_phdr->p_align;
T.image = (void *)(base + tls_phdr->p_vaddr);
T.len = tls_phdr->p_filesz;
T.size = tls_phdr->p_memsz;
T.align = tls_phdr->p_align;
size += (-size - (uintptr_t)image) & (align-1);
if (align < 4*sizeof(size_t)) align = 4*sizeof(size_t);
T.size += (-T.size - (uintptr_t)T.image) & (T.align-1);
if (T.align < 4*sizeof(size_t)) T.align = 4*sizeof(size_t);
libc.tls_size = 2*sizeof(void *)+size+align+sizeof(struct pthread);
libc.tls_size = 2*sizeof(void *)+T.size+T.align+sizeof(struct pthread);
mem = __mmap(0, libc.tls_size, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);

22
src/env/__reset_tls.c vendored Normal file
View File

@ -0,0 +1,22 @@
#ifndef SHARED
#include <string.h>
#include "pthread_impl.h"
#include "libc.h"
extern struct tls_image {
void *image;
size_t len, size, align;
} __static_tls ATTR_LIBC_VISIBILITY;
#define T __static_tls
void __reset_tls()
{
if (!T.size) return;
pthread_t self = __pthread_self();
memcpy(self->dtv[1], T.image, T.len);
memset((char *)self->dtv[1]+T.len, 0, T.size-T.len);
}
#endif

View File

@ -783,6 +783,19 @@ void _dl_debug_state(void)
{
}
void __reset_tls()
{
pthread_t self = __pthread_self();
struct dso *p;
for (p=head; p; p=p->next) {
if (!p->tls_id || !self->dtv[p->tls_id]) continue;
memcpy(self->dtv[p->tls_id], p->tls_image, p->tls_len);
memset((char *)self->dtv[p->tls_id]+p->tls_len, 0,
p->tls_size - p->tls_len);
if (p->tls_id == (size_t)self->dtv[0]) break;
}
}
void *__copy_tls(unsigned char *mem)
{
pthread_t td;

View File

@ -19,6 +19,8 @@ static void dummy_1(pthread_t self)
}
weak_alias(dummy_1, __pthread_tsd_run_dtors);
void __reset_tls();
static void cleanup_fromsig(void *p)
{
pthread_t self = __pthread_self();
@ -28,6 +30,7 @@ static void cleanup_fromsig(void *p)
self->canceldisable = 0;
self->cancelasync = 0;
self->unblock_cancel = 0;
__reset_tls();
longjmp(p, 1);
}