xref: /openbsd/lib/libc/dlfcn/init.c (revision 274d7c50)
1 /*	$OpenBSD: init.c,v 1.7 2019/06/02 01:03:01 guenther Exp $ */
2 /*
3  * Copyright (c) 2014,2015 Philip Guenther <guenther@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 
19 #define _DYN_LOADER
20 
21 #include <sys/types.h>
22 #include <sys/syscall.h>
23 
24 #ifndef PIC
25 #include <sys/mman.h>
26 #endif
27 
28 #include <tib.h>
29 #include <limits.h>		/* NAME_MAX */
30 #include <link.h>
31 #include <stdlib.h>		/* atexit */
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include "init.h"
36 
37 #define MAX(a,b)	(((a)>(b))?(a):(b))
38 
39 #ifdef TIB_EXTRA_ALIGN
40 # define TIB_ALIGN	MAX(__alignof__(struct tib), TIB_EXTRA_ALIGN)
41 #else
42 # define TIB_ALIGN	__alignof__(struct tib)
43 #endif
44 
45 /* XXX should be in an include file shared with csu */
46 char	***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void));
47 
48 /* provide definition for this */
49 int	_pagesize = 0;
50 
51 /*
52  * In dynamicly linked binaries environ and __progname are overriden by
53  * the definitions in ld.so.
54  */
55 char	**environ __attribute__((weak)) = NULL;
56 char	*__progname __attribute__((weak)) = NULL;
57 
58 
59 #ifndef PIC
60 struct dl_phdr_info	_static_phdr_info __relro = { .dlpi_name = "a.out" };
61 
62 static inline void early_static_init(char **_argv, char **_envp);
63 static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum);
64 
65 /* provided by the linker */
66 extern Elf_Ehdr __executable_start[] __attribute__((weak));
67 #endif /* PIC */
68 
69 /* provide definitions for these */
70 const dl_cb *_dl_cb __relro = NULL;
71 
72 void _libc_preinit(int, char **, char **, dl_cb_cb *) __dso_hidden;
73 void
74 _libc_preinit(int argc, char **argv, char **envp, dl_cb_cb *cb)
75 {
76 	AuxInfo	*aux;
77 #ifndef PIC
78 	Elf_Phdr *phdr = NULL;
79 	int phnum = 0;
80 
81 	/* static libc in a static link? */
82 	if (cb == NULL)
83 		early_static_init(argv, envp);
84 #endif /* !PIC */
85 
86 	if (cb != NULL)
87 		_dl_cb = cb(DL_CB_CUR);
88 
89 	/* Extract useful bits from the auxiliary vector */
90 	while (*envp++ != NULL)
91 		;
92 	for (aux = (void *)envp; aux->au_id != AUX_null; aux++) {
93 		switch (aux->au_id) {
94 		case AUX_pagesz:
95 			_pagesize = aux->au_v;
96 			break;
97 #ifndef PIC
98 		case AUX_base:
99 			_static_phdr_info.dlpi_addr = aux->au_v;
100 			break;
101 		case AUX_phdr:
102 			phdr = (void *)aux->au_v;
103 			break;
104 		case AUX_phnum:
105 			phnum = aux->au_v;
106 			break;
107 #endif /* !PIC */
108 		}
109 	}
110 
111 #ifndef PIC
112 	if (cb == NULL && phdr == NULL && __executable_start != NULL) {
113 		/*
114 		 * Static non-PIE processes don't get an AUX vector,
115 		 * so find the phdrs through the ELF header
116 		 */
117 		_static_phdr_info.dlpi_addr = (Elf_Addr)__executable_start;
118 		phdr = (void *)((char *)__executable_start +
119 		    __executable_start->e_phoff);
120 		phnum = __executable_start->e_phnum;
121 	}
122 	_static_phdr_info.dlpi_phdr = phdr;
123 	_static_phdr_info.dlpi_phnum = phnum;
124 
125 	/* static libc in a static link? */
126 	if (cb == NULL)
127 		setup_static_tib(phdr, phnum);
128 #endif /* !PIC */
129 }
130 
131 /* ARM just had to be different... */
132 #ifndef __arm__
133 # define TYPE	"@"
134 #else
135 # define TYPE	"%"
136 #endif
137 
138 #ifdef __LP64__
139 # define VALUE_ALIGN		".balign 8"
140 # define VALUE_DIRECTIVE	".quad"
141 #else
142 # define VALUE_ALIGN		".balign 4"
143 # ifdef __hppa__
144    /* hppa just had to be different: func pointers prefix with 'P%' */
145 #  define VALUE_DIRECTIVE	".int P%"
146 # else
147 #  define VALUE_DIRECTIVE	".int"
148 # endif
149 #endif
150 
151 #define ADD_TO_ARRAY(func, which) \
152 	__asm(	" .section ."#which",\"a\","TYPE#which"\n " \
153 	VALUE_ALIGN"\n "VALUE_DIRECTIVE" "#func"\n .previous")
154 
155 #ifdef PIC
156 ADD_TO_ARRAY(_libc_preinit, init_array);
157 #else
158 ADD_TO_ARRAY(_libc_preinit, preinit_array);
159 #endif
160 
161 /*
162  * In dynamic links, invoke ld.so's dl_clean_boot() callback, if any,
163  * and register its cleanup.
164  */
165 char ***
166 _csu_finish(char **argv, char **envp, void (*cleanup)(void))
167 {
168 	if (_dl_cb != NULL && _dl_cb->dl_clean_boot != NULL)
169 		_dl_cb->dl_clean_boot();
170 
171 	if (cleanup != NULL)
172 		atexit(cleanup);
173 
174 	return &environ;
175 }
176 
177 #ifndef PIC
178 /*
179  * static libc in a static link?  Then disable kbind and set up
180  * __progname and environ
181  */
182 static inline void
183 early_static_init(char **argv, char **envp)
184 {
185 	static char progname_storage[NAME_MAX+1];
186 
187 	/* disable kbind */
188 	syscall(SYS_kbind, (void *)NULL, (size_t)0, (long long)0);
189 
190 	environ = envp;
191 
192 	/* set up __progname */
193 	if (*argv != NULL) {		/* NULL ptr if argc = 0 */
194 		const char *p = strrchr(*argv, '/');
195 
196 		if (p == NULL)
197 			p = *argv;
198 		else
199 			p++;
200 		strlcpy(progname_storage, p, sizeof(progname_storage));
201 	}
202 	__progname = progname_storage;
203 }
204 
205 /*
206  * static TLS handling
207  */
208 #define ELF_ROUND(x,malign)	(((x) + (malign)-1) & ~((malign)-1))
209 
210 /* for static binaries, the location and size of the TLS image */
211 static void		*static_tls __relro;
212 static size_t		static_tls_fsize __relro;
213 
214 size_t			_static_tls_size __relro = 0;
215 int			_static_tls_align __relro;
216 int			_static_tls_align_offset __relro;
217 
218 static inline void
219 setup_static_tib(Elf_Phdr *phdr, int phnum)
220 {
221 	struct tib *tib;
222 	char *base;
223 	int i;
224 
225 	_static_tls_align = TIB_ALIGN;
226 	if (phdr != NULL) {
227 		for (i = 0; i < phnum; i++) {
228 			if (phdr[i].p_type != PT_TLS)
229 				continue;
230 			if (phdr[i].p_memsz == 0)
231 				break;
232 			if (phdr[i].p_memsz < phdr[i].p_filesz)
233 				break;		/* invalid */
234 			if (phdr[i].p_align > getpagesize())
235 				break;		/* nope */
236 			_static_tls_align = MAX(phdr[i].p_align, TIB_ALIGN);
237 #if TLS_VARIANT == 1
238 			/*
239 			 * Variant 1 places the data after the TIB.  If the
240 			 * TLS alignment is larger than the TIB alignment
241 			 * then we may need to pad in front of the TIB to
242 			 * place the TLS data on the proper alignment.
243 			 * Example: p_align=16 sizeof(TIB)=52 align(TIB)=4
244 			 * - need to offset the TIB 12 bytes from the start
245 			 * - to place ths TLS data at offset 64
246 			 */
247 			_static_tls_size = phdr[i].p_memsz;
248 			_static_tls_align_offset =
249 			    ELF_ROUND(sizeof(struct tib), _static_tls_align) -
250 			    sizeof(struct tib);
251 #elif TLS_VARIANT == 2
252 			/*
253 			 * Variant 2 places the data before the TIB
254 			 * so we need to round up the size to the
255 			 * TLS data alignment TIB's alignment.
256 			 * Example A: p_memsz=24 p_align=16 align(TIB)=8
257 			 * - need to allocate 32 bytes for TLS as compiler
258 			 * - will give the first TLS symbol an offset of -32
259 			 * Example B: p_memsz=4 p_align=4 align(TIB)=8
260 			 * - need to allocate 8 bytes so that the TIB is
261 			 * - properly aligned
262 			 */
263 			_static_tls_size = ELF_ROUND(phdr[i].p_memsz,
264 			    phdr[i].p_align);
265 			_static_tls_align_offset = ELF_ROUND(_static_tls_size,
266 			    _static_tls_align) - _static_tls_size;
267 #endif
268 			if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) {
269 				static_tls = (void *)phdr[i].p_vaddr +
270 				    _static_phdr_info.dlpi_addr;
271 				static_tls_fsize = phdr[i].p_filesz;
272 			}
273 			break;
274 		}
275 	}
276 
277 	base = mmap(NULL, _static_tls_size + _static_tls_align_offset
278 	    + sizeof *tib, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
279 
280 	tib = _static_tls_init(base, NULL);
281 	tib->tib_tid = getthrid();
282 	TCB_SET(TIB_TO_TCB(tib));
283 #if ! TCB_HAVE_MD_GET
284 	_libc_single_tcb = TIB_TO_TCB(tib);
285 #endif
286 }
287 
288 struct tib *
289 _static_tls_init(char *base, void *thread)
290 {
291 	struct tib *tib;
292 
293 	base += _static_tls_align_offset;
294 # if TLS_VARIANT == 1
295 	tib = (struct tib *)base;
296 	base += sizeof(struct tib);
297 # elif TLS_VARIANT == 2
298 	tib = (struct tib *)(base + _static_tls_size);
299 # endif
300 
301 	if (_static_tls_size) {
302 		if (static_tls != NULL)
303 			memcpy(base, static_tls, static_tls_fsize);
304 		memset(base + static_tls_fsize, 0,
305 		    _static_tls_size - static_tls_fsize);
306 	}
307 
308 	TIB_INIT(tib, NULL, thread);
309 	return tib;
310 }
311 #endif /* !PIC */
312