source: mainline/uspace/lib/c/generic/tls.c@ 40abf56

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 40abf56 was 40abf56, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Make sure that a thread with uninitialized TLS does not need to call malloc()
to initialize it.

For threads and tasks created by loader, we create TLS beforehand and pass
it to the child. For tasks spawned directly by the kernel, we require it is
a static executable and allocate the initial TLS using as_area_create() instead
of the libc allocator.

  • Property mode set to 100644
File size: 6.6 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/** @file
33 *
34 * Support for thread-local storage, as described in:
35 * Drepper U.: ELF Handling For Thread-Local Storage, 2005
36 */
37
38#include <assert.h>
39#include <stddef.h>
40#include <align.h>
41#include <tls.h>
42#include <stdlib.h>
43#include <str.h>
44#include <macros.h>
45#include <elf/elf.h>
46#include <as.h>
47
48#include <libarch/config.h>
49
50#ifdef CONFIG_RTLD
51#include <rtld/rtld.h>
52#endif
53
54#include "private/libc.h"
55
56#if !defined(CONFIG_TLS_VARIANT_1) && !defined(CONFIG_TLS_VARIANT_2)
57#error Unknown TLS variant.
58#endif
59
60static ptrdiff_t _tcb_data_offset(void)
61{
62 const elf_segment_header_t *tls =
63 elf_get_phdr(__progsymbols.elfstart, PT_TLS);
64
65 size_t tls_align = tls ? tls->p_align : 1;
66
67#ifdef CONFIG_TLS_VARIANT_1
68 return ALIGN_UP((ptrdiff_t) sizeof(tcb_t), tls_align);
69#else
70 size_t tls_size = tls ? tls->p_memsz : 0;
71 return -ALIGN_UP((ptrdiff_t) tls_size, max(tls_align, _Alignof(tcb_t)));
72#endif
73}
74
75/** Get address of static TLS block */
76void *tls_get(void)
77{
78#ifdef CONFIG_RTLD
79 assert(runtime_env == NULL);
80#endif
81 return (uint8_t *)__tcb_get() + _tcb_data_offset();
82}
83
84static tcb_t *tls_make_generic(const void *elf, void *(*alloc)(size_t, size_t))
85{
86 assert(!elf_get_phdr(elf, PT_DYNAMIC));
87#ifdef CONFIG_RTLD
88 assert(runtime_env == NULL);
89#endif
90
91 const elf_segment_header_t *tls = elf_get_phdr(elf, PT_TLS);
92 size_t tls_size = tls ? tls->p_memsz : 0;
93 size_t tls_align = tls ? tls->p_align : 1;
94
95 /*
96 * We don't currently support alignment this big,
97 * and neither should we need to.
98 */
99 assert(tls_align <= PAGE_SIZE);
100
101#ifdef CONFIG_TLS_VARIANT_1
102 size_t alloc_size =
103 ALIGN_UP(sizeof(tcb_t), tls_align) + tls_size;
104#else
105 size_t alloc_size =
106 ALIGN_UP(tls_size, max(tls_align, _Alignof(tcb_t))) + sizeof(tcb_t);
107#endif
108
109 void *area = alloc(max(tls_align, _Alignof(tcb_t)), alloc_size);
110 if (!area)
111 return NULL;
112
113#ifdef CONFIG_TLS_VARIANT_1
114 tcb_t *tcb = area;
115 uint8_t *data = (uint8_t *)tcb + _tcb_data_offset();
116 memset(tcb, 0, sizeof(*tcb));
117#else
118 uint8_t *data = area;
119 tcb_t *tcb = (tcb_t *) (data - _tcb_data_offset());
120 memset(tcb, 0, sizeof(tcb_t));
121 tcb->self = tcb;
122#endif
123
124 if (!tls)
125 return tcb;
126
127 uintptr_t bias = elf_get_bias(elf);
128
129 /* Copy thread local data from the initialization image. */
130 memcpy(data, (void *)(tls->p_vaddr + bias), tls->p_filesz);
131 /* Zero out the thread local uninitialized data. */
132 memset(data + tls->p_filesz, 0, tls->p_memsz - tls->p_filesz);
133
134 return tcb;
135}
136
137static void *early_alloc(size_t align, size_t alloc_size)
138{
139 assert(align <= PAGE_SIZE);
140 alloc_size = ALIGN_UP(alloc_size, PAGE_SIZE);
141
142 void *area = as_area_create(AS_AREA_ANY, alloc_size,
143 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
144 if (area == AS_MAP_FAILED)
145 return NULL;
146 return area;
147}
148
149/** Same as tls_make(), but uses as_area_create() instead of memalign().
150 * Only used in __libc_main() if the program was created by the kernel.
151 */
152tcb_t *tls_make_initial(const void *elf)
153{
154 return tls_make_generic(elf, early_alloc);
155}
156
157/** Create TLS (Thread Local Storage) data structures.
158 *
159 * @return Pointer to TCB.
160 */
161tcb_t *tls_make(const void *elf)
162{
163 // TODO: Always use rtld.
164
165#ifdef CONFIG_RTLD
166 if (runtime_env != NULL)
167 return rtld_tls_make(runtime_env);
168#endif
169
170 return tls_make_generic(elf, memalign);
171}
172
173void tls_free(tcb_t *tcb)
174{
175#ifdef CONFIG_RTLD
176 free(tcb->dtv);
177
178 if (runtime_env != NULL) {
179 tls_free_arch(tcb, runtime_env->tls_size, runtime_env->tls_align);
180 return;
181 }
182#endif
183 const elf_segment_header_t *tls =
184 elf_get_phdr(__progsymbols.elfstart, PT_TLS);
185
186 assert(tls != NULL);
187 tls_free_arch(tcb,
188 ALIGN_UP(tls->p_memsz, tls->p_align) + sizeof(tcb_t),
189 max(tls->p_align, _Alignof(tcb_t)));
190}
191
192#ifdef CONFIG_TLS_VARIANT_1
193/** Allocate TLS variant 1 data structures.
194 *
195 * @param data Start of TLS section. This is an output argument.
196 * @param size Size of tdata + tbss section.
197 * @return Pointer to tcb_t structure.
198 */
199tcb_t *tls_alloc_variant_1(size_t size, size_t align)
200{
201 tcb_t *tcb = memalign(align, size);
202 if (!tcb)
203 return NULL;
204 memset(tcb, 0, sizeof(tcb_t));
205 return tcb;
206}
207
208/** Free TLS variant I data structures.
209 *
210 * @param tcb Pointer to TCB structure.
211 * @param size This argument is ignored.
212 */
213void tls_free_variant_1(tcb_t *tcb, size_t size, size_t align)
214{
215 free(tcb);
216}
217#endif
218
219#ifdef CONFIG_TLS_VARIANT_2
220/** Allocate TLS variant II data structures.
221 *
222 * @param data Pointer to pointer to thread local data. This is
223 * actually an output argument.
224 * @param size Size of thread local data.
225 * @param align Alignment of thread local data.
226 * @return Pointer to TCB structure.
227 */
228tcb_t *tls_alloc_variant_2(size_t size, size_t align)
229{
230 void *data = memalign(align, size);
231 if (data == NULL)
232 return NULL;
233
234 tcb_t *tcb = (tcb_t *) (data + size - sizeof(tcb_t));
235 memset(tcb, 0, sizeof(tcb_t));
236 tcb->self = tcb;
237 return tcb;
238}
239
240/** Free TLS variant II data structures.
241 *
242 * @param tcb Pointer to TCB structure.
243 * @param size Size of thread local data.
244 * @param align Alignment of thread local data.
245 */
246void tls_free_variant_2(tcb_t *tcb, size_t size, size_t align)
247{
248 if (tcb != NULL) {
249 void *start = ((void *) tcb) + sizeof(tcb_t) - size;
250 free(start);
251 }
252}
253#endif
254
255/** @}
256 */
Note: See TracBrowser for help on using the repository browser.