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