xref: /openbsd-src/lib/libc/dlfcn/init.c (revision 17423a06aac5f7ca8e40b9b2f3341b9b9f9255c6)
1*17423a06Skettenis /*	$OpenBSD: init.c,v 1.24 2024/07/22 22:06:27 kettenis Exp $ */
25af055cdSguenther /*
35af055cdSguenther  * Copyright (c) 2014,2015 Philip Guenther <guenther@openbsd.org>
45af055cdSguenther  *
55af055cdSguenther  * Permission to use, copy, modify, and distribute this software for any
65af055cdSguenther  * purpose with or without fee is hereby granted, provided that the above
75af055cdSguenther  * copyright notice and this permission notice appear in all copies.
85af055cdSguenther  *
95af055cdSguenther  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
105af055cdSguenther  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
115af055cdSguenther  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
125af055cdSguenther  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
135af055cdSguenther  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
145af055cdSguenther  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
155af055cdSguenther  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
165af055cdSguenther  */
175af055cdSguenther 
185af055cdSguenther 
195af055cdSguenther #define _DYN_LOADER
205af055cdSguenther 
215af055cdSguenther #include <sys/types.h>
22d97c60eaSguenther #include <sys/syscall.h>
23d82e6535Spirofti #include <sys/timetc.h>		/* timekeep */
245af055cdSguenther 
25fe38b55cSguenther #ifndef PIC
26fe38b55cSguenther #include <sys/mman.h>
27fe38b55cSguenther #endif
28fe38b55cSguenther 
29fe38b55cSguenther #include <tib.h>
305af055cdSguenther #include <limits.h>		/* NAME_MAX */
31767451c3Sguenther #include <link.h>
325af055cdSguenther #include <stdlib.h>		/* atexit */
335af055cdSguenther #include <string.h>
34fe38b55cSguenther #include <unistd.h>
355af055cdSguenther 
36767451c3Sguenther #include "init.h"
37767451c3Sguenther 
38f54aa464Sguenther #define MAX(a,b)	(((a)>(b))?(a):(b))
39f54aa464Sguenther 
40f54aa464Sguenther #ifdef TIB_EXTRA_ALIGN
41f54aa464Sguenther # define TIB_ALIGN	MAX(__alignof__(struct tib), TIB_EXTRA_ALIGN)
42f54aa464Sguenther #else
43f54aa464Sguenther # define TIB_ALIGN	__alignof__(struct tib)
44f54aa464Sguenther #endif
45f54aa464Sguenther 
465af055cdSguenther /* XXX should be in an include file shared with csu */
475af055cdSguenther char	***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void));
485af055cdSguenther 
49d82e6535Spirofti /* provide definitions for these */
505af055cdSguenther int	_pagesize = 0;
51d82e6535Spirofti struct timekeep	*_timekeep;
52ef873df0Sjca unsigned long	_hwcap, _hwcap2;
53ef873df0Sjca int	_hwcap_avail, _hwcap2_avail;
545af055cdSguenther 
555af055cdSguenther /*
562c53affbSjmc  * In dynamically linked binaries environ and __progname are overridden by
575af055cdSguenther  * the definitions in ld.so.
585af055cdSguenther  */
595af055cdSguenther char	**environ __attribute__((weak)) = NULL;
605af055cdSguenther char	*__progname __attribute__((weak)) = NULL;
615af055cdSguenther 
625af055cdSguenther 
635af055cdSguenther #ifndef PIC
645c7d06e5Sguenther struct dl_phdr_info	_static_phdr_info __relro = { .dlpi_name = "a.out" };
65767451c3Sguenther 
665af055cdSguenther static inline void early_static_init(char **_argv, char **_envp);
67fe38b55cSguenther static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum);
685af055cdSguenther 
69f54aa464Sguenther /* provided by the linker */
70f54aa464Sguenther extern Elf_Ehdr __executable_start[] __attribute__((weak));
71f54aa464Sguenther #endif /* PIC */
725af055cdSguenther 
735c7d06e5Sguenther /* provide definitions for these */
745c7d06e5Sguenther const dl_cb *_dl_cb __relro = NULL;
755c7d06e5Sguenther 
761c92691bSderaadt int	HIDDEN(execve)(const char *, char *const *, char *const *)
771c92691bSderaadt 	__attribute__((weak));
781c92691bSderaadt 
795c7d06e5Sguenther void _libc_preinit(int, char **, char **, dl_cb_cb *) __dso_hidden;
805c7d06e5Sguenther void
815c7d06e5Sguenther _libc_preinit(int argc, char **argv, char **envp, dl_cb_cb *cb)
825af055cdSguenther {
835af055cdSguenther 	AuxInfo	*aux;
845af055cdSguenther #ifndef PIC
85fe38b55cSguenther 	Elf_Phdr *phdr = NULL;
86fe38b55cSguenther 	int phnum = 0;
87fe38b55cSguenther 
885af055cdSguenther 	/* static libc in a static link? */
895c7d06e5Sguenther 	if (cb == NULL)
905af055cdSguenther 		early_static_init(argv, envp);
915af055cdSguenther #endif /* !PIC */
925af055cdSguenther 
935c7d06e5Sguenther 	if (cb != NULL)
945c7d06e5Sguenther 		_dl_cb = cb(DL_CB_CUR);
955c7d06e5Sguenther 
965af055cdSguenther 	/* Extract useful bits from the auxiliary vector */
975af055cdSguenther 	while (*envp++ != NULL)
985af055cdSguenther 		;
995af055cdSguenther 	for (aux = (void *)envp; aux->au_id != AUX_null; aux++) {
1005af055cdSguenther 		switch (aux->au_id) {
101ef873df0Sjca 		case AUX_hwcap:
102ef873df0Sjca 			_hwcap = aux->au_v;
103ef873df0Sjca 			_hwcap_avail = 1;
104ef873df0Sjca 			break;
105ef873df0Sjca 		case AUX_hwcap2:
106ef873df0Sjca 			_hwcap2 = aux->au_v;
107ef873df0Sjca 			_hwcap2_avail = 1;
108ef873df0Sjca 			break;
1095af055cdSguenther 		case AUX_pagesz:
1105af055cdSguenther 			_pagesize = aux->au_v;
1115af055cdSguenther 			break;
112fe38b55cSguenther #ifndef PIC
113767451c3Sguenther 		case AUX_base:
114767451c3Sguenther 			_static_phdr_info.dlpi_addr = aux->au_v;
115767451c3Sguenther 			break;
116fe38b55cSguenther 		case AUX_phdr:
117fe38b55cSguenther 			phdr = (void *)aux->au_v;
118fe38b55cSguenther 			break;
119fe38b55cSguenther 		case AUX_phnum:
120fe38b55cSguenther 			phnum = aux->au_v;
121fe38b55cSguenther 			break;
122fe38b55cSguenther #endif /* !PIC */
123d82e6535Spirofti 		case AUX_openbsd_timekeep:
124d82e6535Spirofti 			if (_tc_get_timecount) {
125d82e6535Spirofti 				_timekeep = (void *)aux->au_v;
126d82e6535Spirofti 				if (_timekeep &&
127d82e6535Spirofti 				    _timekeep->tk_version != TK_VERSION)
128d82e6535Spirofti 					_timekeep = NULL;
129d82e6535Spirofti 			}
13022966de3Stedu 			if (issetugid() == 0 && getenv("LIBC_NOUSERTC"))
13122966de3Stedu 				_timekeep = NULL;
132d82e6535Spirofti 			break;
1335af055cdSguenther 		}
1345af055cdSguenther 	}
1355af055cdSguenther 
136fe38b55cSguenther #ifndef PIC
1375c7d06e5Sguenther 	if (cb == NULL && phdr == NULL && __executable_start != NULL) {
138f54aa464Sguenther 		/*
139f54aa464Sguenther 		 * Static non-PIE processes don't get an AUX vector,
140f54aa464Sguenther 		 * so find the phdrs through the ELF header
141f54aa464Sguenther 		 */
142f54aa464Sguenther 		phdr = (void *)((char *)__executable_start +
143f54aa464Sguenther 		    __executable_start->e_phoff);
144f54aa464Sguenther 		phnum = __executable_start->e_phnum;
145f54aa464Sguenther 	}
1465c7d06e5Sguenther 	_static_phdr_info.dlpi_phdr = phdr;
1475c7d06e5Sguenther 	_static_phdr_info.dlpi_phnum = phnum;
1485c7d06e5Sguenther 
149fe38b55cSguenther 	/* static libc in a static link? */
150c5e6b56aSderaadt 	if (cb == NULL)
151fe38b55cSguenther 		setup_static_tib(phdr, phnum);
152493e9f46Sderaadt 
153493e9f46Sderaadt 	/*
154493e9f46Sderaadt 	 * If a static binary has text relocations (DT_TEXT), then un-writeable
155493e9f46Sderaadt 	 * segments were not made immutable by the kernel.  Textrel and RELRO
156493e9f46Sderaadt 	 * changes have now been completed and permissions corrected, so these
157493e9f46Sderaadt 	 * regions can become immutable.
158493e9f46Sderaadt 	 */
159493e9f46Sderaadt 	if (phdr) {
160493e9f46Sderaadt 		int i;
161493e9f46Sderaadt 
162493e9f46Sderaadt 		for (i = 0; i < phnum; i++) {
163493e9f46Sderaadt 			if (phdr[i].p_type == PT_LOAD &&
164493e9f46Sderaadt 			    (phdr[i].p_flags & PF_W) == 0)
165493e9f46Sderaadt 				mimmutable((void *)(_static_phdr_info.dlpi_addr +
166493e9f46Sderaadt 				    phdr[i].p_vaddr), phdr[i].p_memsz);
167493e9f46Sderaadt 		}
168493e9f46Sderaadt 	}
169fe38b55cSguenther #endif /* !PIC */
1705c7d06e5Sguenther }
1715c7d06e5Sguenther 
1725c7d06e5Sguenther /* ARM just had to be different... */
1735c7d06e5Sguenther #ifndef __arm__
1745c7d06e5Sguenther # define TYPE	"@"
1755c7d06e5Sguenther #else
1765c7d06e5Sguenther # define TYPE	"%"
1775c7d06e5Sguenther #endif
1785c7d06e5Sguenther 
1795c7d06e5Sguenther #ifdef __LP64__
1805c7d06e5Sguenther # define VALUE_ALIGN		".balign 8"
1815c7d06e5Sguenther # define VALUE_DIRECTIVE	".quad"
1825c7d06e5Sguenther #else
1835c7d06e5Sguenther # define VALUE_ALIGN		".balign 4"
1845c7d06e5Sguenther # ifdef __hppa__
1855c7d06e5Sguenther    /* hppa just had to be different: func pointers prefix with 'P%' */
1865c7d06e5Sguenther #  define VALUE_DIRECTIVE	".int P%"
1875c7d06e5Sguenther # else
1885c7d06e5Sguenther #  define VALUE_DIRECTIVE	".int"
1895c7d06e5Sguenther # endif
1905c7d06e5Sguenther #endif
1915c7d06e5Sguenther 
1925c7d06e5Sguenther #ifdef PIC
193*17423a06Skettenis /*
194*17423a06Skettenis  * Set a priority so _libc_preinit gets called before the constructor
195*17423a06Skettenis  * on libcompiler_rt that may use elf_aux_info(3).
196*17423a06Skettenis  */
197*17423a06Skettenis __asm(" .section .init_array.50,\"a\","TYPE"init_array\n " \
198*17423a06Skettenis 	VALUE_ALIGN"\n "VALUE_DIRECTIVE" _libc_preinit\n .previous");
1995c7d06e5Sguenther #else
200*17423a06Skettenis __asm(" .section .preinit_array,\"a\","TYPE"preinit_array\n " \
201*17423a06Skettenis 	VALUE_ALIGN"\n "VALUE_DIRECTIVE" _libc_preinit\n .previous");
2025c7d06e5Sguenther #endif
2035c7d06e5Sguenther 
2045c7d06e5Sguenther /*
2055c7d06e5Sguenther  * In dynamic links, invoke ld.so's dl_clean_boot() callback, if any,
2065c7d06e5Sguenther  * and register its cleanup.
2075c7d06e5Sguenther  */
2085c7d06e5Sguenther char ***
2095c7d06e5Sguenther _csu_finish(char **argv, char **envp, void (*cleanup)(void))
2105c7d06e5Sguenther {
2115c7d06e5Sguenther 	if (_dl_cb != NULL && _dl_cb->dl_clean_boot != NULL)
2125c7d06e5Sguenther 		_dl_cb->dl_clean_boot();
213fe38b55cSguenther 
2145af055cdSguenther 	if (cleanup != NULL)
2155af055cdSguenther 		atexit(cleanup);
2165af055cdSguenther 
2175af055cdSguenther 	return &environ;
2185af055cdSguenther }
2195af055cdSguenther 
2205af055cdSguenther #ifndef PIC
2215af055cdSguenther /*
222b18b10d8Sguenther  * static libc in a static link?  Then set up __progname and environ
2235af055cdSguenther  */
2245af055cdSguenther static inline void
2255af055cdSguenther early_static_init(char **argv, char **envp)
2265af055cdSguenther {
227d97c60eaSguenther 	static char progname_storage[NAME_MAX+1];
2285af055cdSguenther 
2295af055cdSguenther 	environ = envp;
2305af055cdSguenther 
2315af055cdSguenther 	/* set up __progname */
2325af055cdSguenther 	if (*argv != NULL) {		/* NULL ptr if argc = 0 */
2335af055cdSguenther 		const char *p = strrchr(*argv, '/');
2345af055cdSguenther 
2355af055cdSguenther 		if (p == NULL)
2365af055cdSguenther 			p = *argv;
2375af055cdSguenther 		else
2385af055cdSguenther 			p++;
2395af055cdSguenther 		strlcpy(progname_storage, p, sizeof(progname_storage));
2405af055cdSguenther 	}
2415af055cdSguenther 	__progname = progname_storage;
2425af055cdSguenther }
243fe38b55cSguenther 
244fe38b55cSguenther /*
245fe38b55cSguenther  * static TLS handling
246fe38b55cSguenther  */
247fe38b55cSguenther #define ELF_ROUND(x,malign)	(((x) + (malign)-1) & ~((malign)-1))
248fe38b55cSguenther 
249fe38b55cSguenther /* for static binaries, the location and size of the TLS image */
2505c7d06e5Sguenther static void		*static_tls __relro;
2515c7d06e5Sguenther static size_t		static_tls_fsize __relro;
252fe38b55cSguenther 
2535c7d06e5Sguenther size_t			_static_tls_size __relro = 0;
2545c7d06e5Sguenther int			_static_tls_align __relro;
2555c7d06e5Sguenther int			_static_tls_align_offset __relro;
256fe38b55cSguenther 
257fe38b55cSguenther static inline void
258fe38b55cSguenther setup_static_tib(Elf_Phdr *phdr, int phnum)
259fe38b55cSguenther {
260fe38b55cSguenther 	struct tib *tib;
261fe38b55cSguenther 	char *base;
262fe38b55cSguenther 	int i;
263fe38b55cSguenther 
264f54aa464Sguenther 	_static_tls_align = TIB_ALIGN;
265fe38b55cSguenther 	if (phdr != NULL) {
266fe38b55cSguenther 		for (i = 0; i < phnum; i++) {
267fe38b55cSguenther 			if (phdr[i].p_type != PT_TLS)
268fe38b55cSguenther 				continue;
269fe38b55cSguenther 			if (phdr[i].p_memsz == 0)
270fe38b55cSguenther 				break;
271fe38b55cSguenther 			if (phdr[i].p_memsz < phdr[i].p_filesz)
272fe38b55cSguenther 				break;		/* invalid */
273f54aa464Sguenther 			if (phdr[i].p_align > getpagesize())
274f54aa464Sguenther 				break;		/* nope */
275f54aa464Sguenther 			_static_tls_align = MAX(phdr[i].p_align, TIB_ALIGN);
276fe38b55cSguenther #if TLS_VARIANT == 1
277f54aa464Sguenther 			/*
278f54aa464Sguenther 			 * Variant 1 places the data after the TIB.  If the
279f54aa464Sguenther 			 * TLS alignment is larger than the TIB alignment
280f54aa464Sguenther 			 * then we may need to pad in front of the TIB to
281f54aa464Sguenther 			 * place the TLS data on the proper alignment.
282f54aa464Sguenther 			 * Example: p_align=16 sizeof(TIB)=52 align(TIB)=4
283f54aa464Sguenther 			 * - need to offset the TIB 12 bytes from the start
284f54aa464Sguenther 			 * - to place ths TLS data at offset 64
285f54aa464Sguenther 			 */
286fe38b55cSguenther 			_static_tls_size = phdr[i].p_memsz;
287f54aa464Sguenther 			_static_tls_align_offset =
288f54aa464Sguenther 			    ELF_ROUND(sizeof(struct tib), _static_tls_align) -
289f54aa464Sguenther 			    sizeof(struct tib);
290fe38b55cSguenther #elif TLS_VARIANT == 2
291fe38b55cSguenther 			/*
292f54aa464Sguenther 			 * Variant 2 places the data before the TIB
293f54aa464Sguenther 			 * so we need to round up the size to the
294f54aa464Sguenther 			 * TLS data alignment TIB's alignment.
295f54aa464Sguenther 			 * Example A: p_memsz=24 p_align=16 align(TIB)=8
296f54aa464Sguenther 			 * - need to allocate 32 bytes for TLS as compiler
297f54aa464Sguenther 			 * - will give the first TLS symbol an offset of -32
298f54aa464Sguenther 			 * Example B: p_memsz=4 p_align=4 align(TIB)=8
299f54aa464Sguenther 			 * - need to allocate 8 bytes so that the TIB is
300f54aa464Sguenther 			 * - properly aligned
301fe38b55cSguenther 			 */
302fe38b55cSguenther 			_static_tls_size = ELF_ROUND(phdr[i].p_memsz,
303fe38b55cSguenther 			    phdr[i].p_align);
304f54aa464Sguenther 			_static_tls_align_offset = ELF_ROUND(_static_tls_size,
305f54aa464Sguenther 			    _static_tls_align) - _static_tls_size;
306fe38b55cSguenther #endif
307fe38b55cSguenther 			if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) {
308f54aa464Sguenther 				static_tls = (void *)phdr[i].p_vaddr +
309f54aa464Sguenther 				    _static_phdr_info.dlpi_addr;
310fe38b55cSguenther 				static_tls_fsize = phdr[i].p_filesz;
311fe38b55cSguenther 			}
312fe38b55cSguenther 			break;
313fe38b55cSguenther 		}
314fe38b55cSguenther 	}
315fe38b55cSguenther 
316f54aa464Sguenther 	base = mmap(NULL, _static_tls_size + _static_tls_align_offset
317f54aa464Sguenther 	    + sizeof *tib, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
318fe38b55cSguenther 
319f54aa464Sguenther 	tib = _static_tls_init(base, NULL);
320fe38b55cSguenther 	tib->tib_tid = getthrid();
321fe38b55cSguenther 	TCB_SET(TIB_TO_TCB(tib));
322fe38b55cSguenther #if ! TCB_HAVE_MD_GET
323fe38b55cSguenther 	_libc_single_tcb = TIB_TO_TCB(tib);
324fe38b55cSguenther #endif
325fe38b55cSguenther }
326fe38b55cSguenther 
327f54aa464Sguenther struct tib *
328f54aa464Sguenther _static_tls_init(char *base, void *thread)
329fe38b55cSguenther {
330f54aa464Sguenther 	struct tib *tib;
331f54aa464Sguenther 
332f54aa464Sguenther 	base += _static_tls_align_offset;
333fe38b55cSguenther # if TLS_VARIANT == 1
334f54aa464Sguenther 	tib = (struct tib *)base;
335fe38b55cSguenther 	base += sizeof(struct tib);
336f54aa464Sguenther # elif TLS_VARIANT == 2
337f54aa464Sguenther 	tib = (struct tib *)(base + _static_tls_size);
338fe38b55cSguenther # endif
339f54aa464Sguenther 
340f54aa464Sguenther 	if (_static_tls_size) {
341fe38b55cSguenther 		if (static_tls != NULL)
342fe38b55cSguenther 			memcpy(base, static_tls, static_tls_fsize);
343fe38b55cSguenther 		memset(base + static_tls_fsize, 0,
344fe38b55cSguenther 		    _static_tls_size - static_tls_fsize);
345fe38b55cSguenther 	}
346f54aa464Sguenther 
347f54aa464Sguenther 	TIB_INIT(tib, NULL, thread);
348f54aa464Sguenther 	return tib;
349fe38b55cSguenther }
3505af055cdSguenther #endif /* !PIC */
351