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