xref: /openbsd-src/libexec/ld.so/tib.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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 #include <sys/exec_elf.h>
26 
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 
38 static int	static_tls_size;
39 
40 int		_dl_tib_static_done;
41 
42 /*
43  * Allocate a TIB for passing to __tfork for a new thread.  'extra'
44  * is the amount of space to allocate on the side of the TIB opposite
45  * of the TLS data: before the TIB for variant 1 and after the TIB
46  * for variant 2.  If non-zero, tib_thread is set to point to that area.
47  */
48 void *
49 _dl_allocate_tib(size_t extra)
50 {
51 	char *base;
52 	struct tib *tib;
53 	char *thread = NULL;
54 	struct elf_object *obj;
55 
56 #if TLS_VARIANT == 1
57 	/* round up the extra size to align the tib after it */
58 	extra = ELF_ROUND(extra, sizeof(void *));
59 	base = _dl_malloc(extra + sizeof *tib + static_tls_size);
60 	if (base == NULL)
61 		return NULL;
62 	tib = (struct tib *)(base + extra);
63 	if (extra)
64 		thread = base;
65 #define TLS_ADDR(tibp, offset)	((char *)(tibp) + sizeof(struct tib) + (offset))
66 
67 #elif TLS_VARIANT == 2
68 	/* round up the tib size to align the extra area after it */
69 	base = _dl_malloc(ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN) +
70 	    extra + static_tls_size);
71 	if (base == NULL)
72 		return NULL;
73 	tib = (struct tib *)(base + static_tls_size);
74 	if (extra)
75 		thread = (char *)tib + ELF_ROUND(sizeof *tib, TIB_EXTRA_ALIGN);
76 #define TLS_ADDR(tibp, offset)	((char *)(tibp) - (offset))
77 
78 #endif
79 
80 	for (obj = _dl_objects; obj != NULL; obj = obj->next) {
81 		if (obj->tls_msize != 0) {
82 			char *addr = TLS_ADDR(tib, obj->tls_offset);
83 
84 			_dl_memset(addr + obj->tls_fsize, 0,
85 			    obj->tls_msize - obj->tls_fsize);
86 			if (obj->tls_static_data != NULL)
87 				_dl_bcopy(obj->tls_static_data, addr,
88 				    obj->tls_fsize);
89 			DL_DEB(("\t%s has index %u addr %p msize %u fsize %u\n",
90 				obj->load_name, obj->tls_offset,
91 				(void *)addr, obj->tls_msize, obj->tls_fsize));
92 		}
93 	}
94 
95 	TIB_INIT(tib, NULL, thread);
96 
97 	DL_DEB(("tib new=%p\n", (void *)tib));
98 
99 	return (tib);
100 }
101 
102 void
103 _dl_free_tib(void *tib, size_t extra)
104 {
105 	size_t tib_offset;
106 
107 #if TLS_VARIANT == 1
108 	tib_offset = ELF_ROUND(extra, sizeof(void *));
109 #elif TLS_VARIANT == 2
110 	tib_offset = static_tls_size;
111 #endif
112 
113 	DL_DEB(("free tib=%p\n", (void *)tib));
114 	_dl_free((char *)tib - tib_offset);
115 }
116 
117 
118 /*
119  * Record what's necessary for handling TLS for an object.
120  */
121 void
122 _dl_set_tls(elf_object_t *object, Elf_Phdr *ptls, Elf_Addr libaddr,
123     const char *libname)
124 {
125 	if (ptls->p_vaddr != 0 && ptls->p_filesz != 0)
126 		object->tls_static_data = (void *)(ptls->p_vaddr + libaddr);
127 	object->tls_fsize = ptls->p_filesz;
128 	object->tls_msize = ptls->p_memsz;
129 	object->tls_align = ptls->p_align;
130 
131 	DL_DEB(("tls %x %x %x %x\n",
132 	    object->tls_static_data, object->tls_fsize, object->tls_msize,
133 	    object->tls_align));
134 }
135 
136 static inline Elf_Addr
137 allocate_tls_offset(Elf_Addr msize, Elf_Addr align)
138 {
139 	Elf_Addr offset;
140 
141 #if TLS_VARIANT == 1
142 	/* round up to the required alignment, then allocate the space */
143 	offset = ELF_ROUND(static_tls_size, align);
144 	static_tls_size += msize;
145 #elif TLS_VARIANT == 2
146 	/*
147 	 * allocate the space, then round up to the alignment
148 	 * (these are negative offsets, so rounding up really rounds the
149 	 * address down)
150 	 */
151 	static_tls_size = ELF_ROUND(static_tls_size + msize, align);
152 	offset = static_tls_size;
153 #else
154 # error "unknown TLS_VARIANT"
155 #endif
156 	return offset;
157 }
158 
159 /*
160  * Calculate the TLS offset for each object with static TLS.
161  */
162 void
163 _dl_allocate_tls_offsets(void)
164 {
165 	struct elf_object *obj;
166 
167 	for (obj = _dl_objects; obj != NULL; obj = obj->next) {
168 		if (obj->tls_msize != 0) {
169 			obj->tls_offset = allocate_tls_offset(obj->tls_msize,
170 			    obj->tls_align);
171 		}
172 	}
173 
174 	/* no more static TLS allocations after this */
175 	_dl_tib_static_done = 1;
176 }
177 
178 /*
179  * Allocate the TIB + TLS for the initial thread.
180  */
181 void
182 _dl_allocate_first_tib(void)
183 {
184 	struct tib *tib;
185 
186 	tib = _dl_allocate_tib(0);
187 	tib->tib_tid = _dl_getthrid();
188 
189 	TCB_SET(TIB_TO_TCB(tib));
190 }
191