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