summaryrefslogtreecommitdiff
path: root/src/env/__init_tls.c
blob: 8ac0036bc487c87bf66f762a0d1480f324667798 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <elf.h>
#include <limits.h>
#include <sys/mman.h>
#include <string.h>
#include "pthread_impl.h"
#include "libc.h"
#include "atomic.h"
#include "syscall.h"

int __init_tp(void *p)
{
	pthread_t td = p;
	td->self = td;
	if (__set_thread_area(TP_ADJ(p)) < 0)
		return -1;
	td->tid = td->pid = __syscall(SYS_set_tid_address, &td->tid);
	td->errno_ptr = &td->errno_val;
	/* Currently, both of these predicates depend in the same thing:
	 * successful initialization of the thread pointer. However, in
	 * the future, we may support setups where setting the thread
	 * pointer is possible but threads other than the main thread
	 * cannot work, so it's best to keep the predicates separate. */
	libc.has_thread_pointer = 1;
	libc.can_do_threads = 1;
	return 0;
}

#ifndef SHARED

static long long builtin_tls[(sizeof(struct pthread) + 64)/sizeof(long long)];

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 (!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)) & (T.align-1);
	td = (pthread_t)mem;
	mem += sizeof(struct pthread);
#else
	mem += libc.tls_size - sizeof(struct pthread);
	mem -= (uintptr_t)mem & (T.align-1);
	td = (pthread_t)mem;
	mem -= T.size;
#endif
	td->dtv = dtv;
	dtv[1] = mem;
	memcpy(mem, T.image, T.len);
	return td;
}

void *__tls_get_addr(size_t *v)
{
	return (char *)__pthread_self()->dtv[1]+v[1];
}

static void *simple(void *p)
{
	*(void **)p = p;
	return __set_thread_area(TP_ADJ(p)) ? 0 : p;
}

weak_alias(simple, __install_initial_tls);

void *__mmap(void *, size_t, int, int, int, off_t);

#if ULONG_MAX == 0xffffffff
typedef Elf32_Phdr Phdr;
#else
typedef Elf64_Phdr Phdr;
#endif

void __init_tls(size_t *aux)
{
	unsigned char *p;
	size_t n;
	Phdr *phdr, *tls_phdr=0;
	size_t base = 0;
	void *mem;

	libc.tls_size = sizeof(struct pthread);

	for (p=(void *)aux[AT_PHDR],n=aux[AT_PHNUM]; n; n--,p+=aux[AT_PHENT]) {
		phdr = (void *)p;
		if (phdr->p_type == PT_PHDR)
			base = aux[AT_PHDR] - phdr->p_vaddr;
		if (phdr->p_type == PT_TLS)
			tls_phdr = phdr;
	}

	if (tls_phdr) {
		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;
	}

	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 *)+T.size+T.align+sizeof(struct pthread);

	if (libc.tls_size > sizeof builtin_tls) {
		mem = (void *)__syscall(
#ifdef SYS_mmap2
			SYS_mmap2,
#else
			SYS_mmap,
#endif
			0, libc.tls_size, PROT_READ|PROT_WRITE,
			MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
		/* -4095...-1 cast to void * will crash on dereference anyway,
		 * so don't bloat the init code checking for error codes and
		 * explicitly calling a_crash(). */
	} else {
		mem = builtin_tls;
	}

	/* Failure to initialize thread pointer is fatal if TLS is used. */
	if (__init_tp(__copy_tls(mem)) < 0 && tls_phdr)
		a_crash();
}
#else
void __init_tls(size_t *auxv) { }
#endif