xref: /openbsd-src/libexec/ld.so/tib.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*
2  * Copyright (c) 2016 Philip Guenther <guenther@openbsd.org>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /*
18  * Thread Information Block (TIB) and Thread Local Storage (TLS) handling
19  * (the TCB, Thread Control Block, is part of the TIB)
20  */
21 
22 #define _DYN_LOADER
23 
24 #include <sys/types.h>
25 
26 #include <elf.h>
27 #include <tib.h>
28 
29 #include "archdep.h"
30 #include "resolve.h"
31 #include "util.h"
32 #include "syscall.h"
33 
34 /* If we need the syscall, use our local syscall definition */
35 #define	__set_tcb(tcb)	_dl___set_tcb(tcb)
36 
37 __dso_hidden void *allocate_tib(size_t);
38 
39 #define MAX(a,b)	(((a)>(b))?(a):(b))
40 
41 #ifdef TIB_EXTRA_ALIGN
42 # define TIB_ALIGN	MAX(__alignof__(struct tib), TIB_EXTRA_ALIGN)
43 #else
44 # define TIB_ALIGN	__alignof__(struct tib)
45 #endif
46 
47 
48 /* size of static TLS allocation */
49 static int	static_tls_size;
50 /* alignment of static TLS allocation */
51 static int	static_tls_align;
52 /* base-offset alignment of (first) static TLS allocation */
53 static int	static_tls_align_offset;
54 
55 int		_dl_tib_static_done;
56 
57 /*
58  * Allocate a TIB for passing to __tfork for a new thread.  'extra'
59  * is the amount of space to allocate on the side of the TIB opposite
60  * of the TLS data: before the TIB for variant 1 and after the TIB
61  * for variant 2.  If non-zero, tib_thread is set to point to that area.
62  */
63 void *
64 allocate_tib(size_t extra)
65 {
66 	char *base;
67 	struct tib *tib;
68 	char *thread = NULL;
69 	struct elf_object *obj;
70 
71 #if TLS_VARIANT == 1
72 	/* round up the extra size to align the TIB and TLS data after it */
73 	size_t unpad_extra = (extra <= static_tls_align_offset) ? 0 :
74 	    ELF_ROUND(extra - static_tls_align_offset, static_tls_align);
75 	base = _dl_aligned_alloc(static_tls_align, unpad_extra +
76 	    static_tls_align_offset + sizeof *tib + static_tls_size);
77 	if (base == NULL)
78 		return NULL;
79 	tib = (struct tib *)(base + unpad_extra + static_tls_align_offset);
80 	if (extra)
81 		thread = base;
82 #define TLS_ADDR(tibp, offset)	((char *)(tibp) + sizeof(struct tib) + (offset))
83 
84 #elif TLS_VARIANT == 2
85 	/* round up the TIB size to align the extra area after it */
86 	base = _dl_aligned_alloc(static_tls_align, static_tls_size +
87 	    static_tls_align_offset + ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN) +
88 	    extra);
89 	if (base == NULL)
90 		return NULL;
91 	base += static_tls_align_offset;
92 	tib = (struct tib *)(base + static_tls_size);
93 	if (extra)
94 		thread = (char *)tib + ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN);
95 #define TLS_ADDR(tibp, offset)	((char *)(tibp) - (offset))
96 
97 #endif
98 
99 	for (obj = _dl_objects; obj != NULL; obj = obj->next) {
100 		if (obj->tls_msize != 0) {
101 			char *addr = TLS_ADDR(tib, obj->tls_offset);
102 
103 			_dl_memset(addr + obj->tls_fsize, 0,
104 			    obj->tls_msize - obj->tls_fsize);
105 			if (obj->tls_static_data != NULL)
106 				_dl_bcopy(obj->tls_static_data, addr,
107 				    obj->tls_fsize);
108 			DL_DEB(("\t%s has index %u addr %p msize %u fsize %u\n",
109 				obj->load_name, obj->tls_offset,
110 				(void *)addr, obj->tls_msize, obj->tls_fsize));
111 		}
112 	}
113 
114 	TIB_INIT(tib, NULL, thread);
115 
116 	DL_DEB(("tib new=%p\n", (void *)tib));
117 
118 	return (tib);
119 }
120 __strong_alias(_dl_allocate_tib, allocate_tib);
121 
122 void
123 _dl_free_tib(void *tib, size_t extra)
124 {
125 	size_t tib_offset;
126 
127 #if TLS_VARIANT == 1
128 	tib_offset = (extra <= static_tls_align_offset) ? 0 :
129 	    ELF_ROUND(extra - static_tls_align_offset, static_tls_align);
130 #elif TLS_VARIANT == 2
131 	tib_offset = static_tls_size;
132 #endif
133 	tib_offset += static_tls_align_offset;
134 
135 	DL_DEB(("free tib=%p\n", (void *)tib));
136 	_dl_free((char *)tib - tib_offset);
137 }
138 
139 
140 /*
141  * Record what's necessary for handling TLS for an object.
142  */
143 void
144 _dl_set_tls(elf_object_t *object, Elf_Phdr *ptls, Elf_Addr libaddr,
145     const char *libname)
146 {
147 	if (ptls->p_vaddr != 0 && ptls->p_filesz != 0)
148 		object->tls_static_data = (void *)(ptls->p_vaddr + libaddr);
149 	object->tls_fsize = ptls->p_filesz;
150 	object->tls_msize = ptls->p_memsz;
151 	object->tls_align = ptls->p_align;
152 
153 	DL_DEB(("tls %x %x %x %x\n",
154 	    object->tls_static_data, object->tls_fsize, object->tls_msize,
155 	    object->tls_align));
156 }
157 
158 static inline Elf_Addr
159 allocate_tls_offset(Elf_Addr msize, Elf_Addr align, int for_exe)
160 {
161 	Elf_Addr offset;
162 
163 	if (for_exe && static_tls_size != 0)
164 		_dl_die("TLS allocation before executable!");
165 
166 #if TLS_VARIANT == 1
167 	if (for_exe) {
168 		/*
169 		 * Variant 1 places the data after the TIB.  If the
170 		 * TLS alignment is larger than the TIB alignment
171 		 * then we may need to pad in front of the TIB to
172 		 * place the TLS data on the proper alignment.
173 		 * Example: p_align=16 sizeof(TIB)=52 align(TIB)=4
174 		 * - need to offset the TIB 12 bytes from the start
175 		 * - to place ths TLS data at offset 64
176 		 */
177 		static_tls_align = MAX(align, TIB_ALIGN);
178 		static_tls_align_offset =
179 		    ELF_ROUND(sizeof(struct tib), static_tls_align) -
180 		    sizeof(struct tib);
181 		offset = 0;
182 		static_tls_size = msize;
183 	} else {
184 		/*
185 		 * If a later object increases the alignment, realign the
186 		 * existing sections.  We push as much padding as possible
187 		 * to the start there it can overlap the thread structure
188 		 */
189 		if (static_tls_align < align) {
190 			static_tls_align_offset += align - static_tls_align;
191 			static_tls_align = align;
192 		}
193 
194 		/*
195 		 * Round up to the required alignment, taking into account
196 		 * the leading padding and TIB, then allocate the space.
197 		 */
198 		offset = static_tls_align_offset + sizeof(struct tib) +
199 		    static_tls_size;
200 		offset = ELF_ROUND(offset, align) - static_tls_align_offset
201 		    - sizeof(struct tib);
202 		static_tls_size = offset + msize;
203 	}
204 #elif TLS_VARIANT == 2
205 	/* Realignment is automatic for variant II */
206 	if (static_tls_align < align)
207 		static_tls_align = align;
208 
209 	/*
210 	 * Variant 2 places the data before the TIB so we need to round up
211 	 * the size to the TLS data alignment TIB's alignment.
212 	 * Example A: p_memsz=24 p_align=16 align(TIB)=8
213 	 * - need to allocate 32 bytes for TLS as compiler
214 	 * - will give the first TLS symbol an offset of -32
215 	 * Example B: p_memsz=4 p_align=4 align(TIB)=8
216 	 * - need to allocate 8 bytes so that the TIB is
217 	 * - properly aligned
218 	 * So: allocate the space, then round up to the alignment
219 	 * (these are negative offsets, so rounding up really
220 	 * rounds the address down)
221 	 */
222 	static_tls_size = ELF_ROUND(static_tls_size + msize, align);
223 	offset = static_tls_size;
224 #else
225 # error "unknown TLS_VARIANT"
226 #endif
227 	return offset;
228 }
229 
230 /*
231  * Calculate the TLS offset for each object with static TLS.
232  */
233 void
234 _dl_allocate_tls_offsets(void)
235 {
236 	struct elf_object *obj;
237 
238 	static_tls_align = TIB_ALIGN;
239 	for (obj = _dl_objects; obj != NULL; obj = obj->next) {
240 		if (obj->tls_msize != 0) {
241 			obj->tls_offset = allocate_tls_offset(obj->tls_msize,
242 			    obj->tls_align, obj->obj_type == OBJTYPE_EXE);
243 		}
244 	}
245 
246 #if TLS_VARIANT == 2
247 	static_tls_align_offset = ELF_ROUND(static_tls_size, static_tls_align)
248 	    - static_tls_size;
249 #endif
250 
251 	/* no more static TLS allocations after this */
252 	_dl_tib_static_done = 1;
253 
254 	DL_DEB(("static tls size=%x align=%x offset=%x\n",
255 	    static_tls_size, static_tls_align, static_tls_align_offset));
256 }
257 
258 /*
259  * Allocate the TIB + TLS for the initial thread.
260  */
261 void
262 _dl_allocate_first_tib(void)
263 {
264 	struct tib *tib;
265 
266 	tib = allocate_tib(0);
267 	tib->tib_tid = _dl_getthrid();
268 
269 	TCB_SET(TIB_TO_TCB(tib));
270 }
271