xref: /freebsd/lib/libsys/auxv.c (revision 1edb7116)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2010, 2012 Konstantin Belousov <kib@FreeBSD.ORG>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 #include "namespace.h"
30 #include <elf.h>
31 #include <errno.h>
32 #include <link.h>
33 #include <pthread.h>
34 #include <stdbool.h>
35 #include <string.h>
36 #include <sys/auxv.h>
37 #include "un-namespace.h"
38 #include "libc_private.h"
39 
40 extern int _DYNAMIC;
41 #pragma weak _DYNAMIC
42 
43 void *__elf_aux_vector;
44 
45 #ifndef PIC
46 static pthread_once_t aux_vector_once = PTHREAD_ONCE_INIT;
47 
48 static void
49 init_aux_vector_once(void)
50 {
51 	Elf_Addr *sp;
52 
53 	sp = (Elf_Addr *)environ;
54 	while (*sp++ != 0)
55 		;
56 	__elf_aux_vector = (Elf_Auxinfo *)sp;
57 }
58 
59 void
60 __init_elf_aux_vector(void)
61 {
62 
63 	if (&_DYNAMIC != NULL)
64 		return;
65 	_once(&aux_vector_once, init_aux_vector_once);
66 }
67 #endif
68 
69 static bool aux_once = false;
70 static int pagesize, osreldate, canary_len, ncpus, pagesizes_len, bsdflags;
71 static int hwcap_present, hwcap2_present;
72 static char *canary, *pagesizes, *execpath;
73 static void *ps_strings, *timekeep;
74 static u_long hwcap, hwcap2;
75 static void *fxrng_seed_version;
76 static u_long usrstackbase, usrstacklim;
77 
78 #ifdef __powerpc__
79 static int powerpc_new_auxv_format = 0;
80 static void _init_aux_powerpc_fixup(void);
81 int _powerpc_elf_aux_info(int, void *, int);
82 #endif
83 
84 /*
85  * This function might be called and actual body executed more than
86  * once in multithreading environment.  Due to this, it is and must
87  * continue to be idempotent.  All stores are atomic (no store
88  * tearing), because we only assign to int/long/ptr.
89  */
90 static void
91 init_aux(void)
92 {
93 	Elf_Auxinfo *aux;
94 
95 	if (aux_once)
96 		return;
97 	for (aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
98 		switch (aux->a_type) {
99 		case AT_BSDFLAGS:
100 			bsdflags = aux->a_un.a_val;
101 			break;
102 
103 		case AT_CANARY:
104 			canary = (char *)(aux->a_un.a_ptr);
105 			break;
106 
107 		case AT_CANARYLEN:
108 			canary_len = aux->a_un.a_val;
109 			break;
110 
111 		case AT_EXECPATH:
112 			execpath = (char *)(aux->a_un.a_ptr);
113 			break;
114 
115 		case AT_HWCAP:
116 			hwcap_present = 1;
117 			hwcap = (u_long)(aux->a_un.a_val);
118 			break;
119 
120 		case AT_HWCAP2:
121 			hwcap2_present = 1;
122 			hwcap2 = (u_long)(aux->a_un.a_val);
123 			break;
124 
125 		case AT_PAGESIZES:
126 			pagesizes = (char *)(aux->a_un.a_ptr);
127 			break;
128 
129 		case AT_PAGESIZESLEN:
130 			pagesizes_len = aux->a_un.a_val;
131 			break;
132 
133 		case AT_PAGESZ:
134 			pagesize = aux->a_un.a_val;
135 			break;
136 
137 		case AT_OSRELDATE:
138 			osreldate = aux->a_un.a_val;
139 			break;
140 
141 		case AT_NCPUS:
142 			ncpus = aux->a_un.a_val;
143 			break;
144 
145 		case AT_TIMEKEEP:
146 			timekeep = aux->a_un.a_ptr;
147 			break;
148 
149 		case AT_PS_STRINGS:
150 			ps_strings = aux->a_un.a_ptr;
151 			break;
152 
153 		case AT_FXRNG:
154 			fxrng_seed_version = aux->a_un.a_ptr;
155 			break;
156 
157 		case AT_USRSTACKBASE:
158 			usrstackbase = aux->a_un.a_val;
159 			break;
160 
161 		case AT_USRSTACKLIM:
162 			usrstacklim = aux->a_un.a_val;
163 			break;
164 #ifdef __powerpc__
165 		/*
166 		 * Since AT_STACKPROT is always set, and the common
167 		 * value 23 is mutually exclusive with the legacy powerpc
168 		 * value 21, the existence of AT_STACKPROT proves we are
169 		 * on the common format.
170 		 */
171 		case AT_STACKPROT:	/* 23 */
172 			powerpc_new_auxv_format = 1;
173 			break;
174 #endif
175 		}
176 	}
177 #ifdef __powerpc__
178 	if (!powerpc_new_auxv_format)
179 		_init_aux_powerpc_fixup();
180 #endif
181 
182 	aux_once = true;
183 }
184 
185 #ifdef __powerpc__
186 static void
187 _init_aux_powerpc_fixup(void)
188 {
189 	Elf_Auxinfo *aux;
190 
191 	/*
192 	 * Before 1300070, PowerPC platforms had nonstandard numbering for
193 	 * the aux vector. When running old binaries, the kernel will pass
194 	 * the vector using the old numbering. Reload affected variables.
195 	 */
196 	for (aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
197 		switch (aux->a_type) {
198 		case AT_OLD_CANARY:
199 			canary = (char *)(aux->a_un.a_ptr);
200 			break;
201 		case AT_OLD_CANARYLEN:
202 			canary_len = aux->a_un.a_val;
203 			break;
204 		case AT_OLD_EXECPATH:
205 			execpath = (char *)(aux->a_un.a_ptr);
206 			break;
207 		case AT_OLD_PAGESIZES:
208 			pagesizes = (char *)(aux->a_un.a_ptr);
209 			break;
210 		case AT_OLD_PAGESIZESLEN:
211 			pagesizes_len = aux->a_un.a_val;
212 			break;
213 		case AT_OLD_OSRELDATE:
214 			osreldate = aux->a_un.a_val;
215 			break;
216 		case AT_OLD_NCPUS:
217 			ncpus = aux->a_un.a_val;
218 			break;
219 		}
220 	}
221 }
222 
223 int
224 _powerpc_elf_aux_info(int aux, void *buf, int buflen)
225 {
226 
227 	/*
228 	 * If we are in the old auxv format, we need to translate the aux
229 	 * parameter of elf_aux_info() calls into the common auxv format.
230 	 * Internal libc calls always use the common format, and they
231 	 * directly call _elf_aux_info instead of using the weak symbol.
232 	 */
233 	if (!powerpc_new_auxv_format) {
234 		switch (aux) {
235 		case AT_OLD_EXECPATH:
236 			aux = AT_EXECPATH;
237 			break;
238 		case AT_OLD_CANARY:
239 			aux = AT_CANARY;
240 			break;
241 		case AT_OLD_CANARYLEN:
242 			aux = AT_CANARYLEN;
243 			break;
244 		case AT_OLD_OSRELDATE:
245 			aux = AT_OSRELDATE;
246 			break;
247 		case AT_OLD_NCPUS:
248 			aux = AT_NCPUS;
249 			break;
250 		case AT_OLD_PAGESIZES:
251 			aux = AT_PAGESIZES;
252 			break;
253 		case AT_OLD_PAGESIZESLEN:
254 			aux = AT_PAGESIZESLEN;
255 			break;
256 		case AT_OLD_STACKPROT:
257 			aux = AT_STACKPROT;
258 			break;
259 		}
260 	}
261 	return _elf_aux_info(aux, buf, buflen);
262 }
263 __weak_reference(_powerpc_elf_aux_info, elf_aux_info);
264 #else
265 __weak_reference(_elf_aux_info, elf_aux_info);
266 #endif
267 
268 int
269 _elf_aux_info(int aux, void *buf, int buflen)
270 {
271 	int res;
272 
273 #ifndef PIC
274 	__init_elf_aux_vector();
275 #endif
276 	if (__elf_aux_vector == NULL)
277 		return (ENOSYS);
278 	init_aux();	/* idempotent */
279 
280 	if (buflen < 0)
281 		return (EINVAL);
282 
283 	switch (aux) {
284 	case AT_CANARY:
285 		if (canary != NULL && canary_len >= buflen) {
286 			memcpy(buf, canary, buflen);
287 			memset(canary, 0, canary_len);
288 			canary = NULL;
289 			res = 0;
290 		} else
291 			res = ENOENT;
292 		break;
293 	case AT_EXECPATH:
294 		if (execpath == NULL)
295 			res = ENOENT;
296 		else if (buf == NULL)
297 			res = EINVAL;
298 		else {
299 			if (strlcpy(buf, execpath, buflen) >=
300 			    (unsigned int)buflen)
301 				res = EINVAL;
302 			else
303 				res = 0;
304 		}
305 		break;
306 	case AT_HWCAP:
307 		if (hwcap_present && buflen == sizeof(u_long)) {
308 			*(u_long *)buf = hwcap;
309 			res = 0;
310 		} else
311 			res = ENOENT;
312 		break;
313 	case AT_HWCAP2:
314 		if (hwcap2_present && buflen == sizeof(u_long)) {
315 			*(u_long *)buf = hwcap2;
316 			res = 0;
317 		} else
318 			res = ENOENT;
319 		break;
320 	case AT_PAGESIZES:
321 		if (pagesizes != NULL && pagesizes_len >= buflen) {
322 			memcpy(buf, pagesizes, buflen);
323 			res = 0;
324 		} else
325 			res = ENOENT;
326 		break;
327 	case AT_PAGESZ:
328 		if (buflen == sizeof(int)) {
329 			if (pagesize != 0) {
330 				*(int *)buf = pagesize;
331 				res = 0;
332 			} else
333 				res = ENOENT;
334 		} else
335 			res = EINVAL;
336 		break;
337 	case AT_OSRELDATE:
338 		if (buflen == sizeof(int)) {
339 			if (osreldate != 0) {
340 				*(int *)buf = osreldate;
341 				res = 0;
342 			} else
343 				res = ENOENT;
344 		} else
345 			res = EINVAL;
346 		break;
347 	case AT_NCPUS:
348 		if (buflen == sizeof(int)) {
349 			if (ncpus != 0) {
350 				*(int *)buf = ncpus;
351 				res = 0;
352 			} else
353 				res = ENOENT;
354 		} else
355 			res = EINVAL;
356 		break;
357 	case AT_TIMEKEEP:
358 		if (buflen == sizeof(void *)) {
359 			if (timekeep != NULL) {
360 				*(void **)buf = timekeep;
361 				res = 0;
362 			} else
363 				res = ENOENT;
364 		} else
365 			res = EINVAL;
366 		break;
367 	case AT_BSDFLAGS:
368 		if (buflen == sizeof(int)) {
369 			*(int *)buf = bsdflags;
370 			res = 0;
371 		} else
372 			res = EINVAL;
373 		break;
374 	case AT_PS_STRINGS:
375 		if (buflen == sizeof(void *)) {
376 			if (ps_strings != NULL) {
377 				*(void **)buf = ps_strings;
378 				res = 0;
379 			} else
380 				res = ENOENT;
381 		} else
382 			res = EINVAL;
383 		break;
384 	case AT_FXRNG:
385 		if (buflen == sizeof(void *)) {
386 			if (fxrng_seed_version != NULL) {
387 				*(void **)buf = fxrng_seed_version;
388 				res = 0;
389 			} else
390 				res = ENOENT;
391 		} else
392 			res = EINVAL;
393 		break;
394 	case AT_USRSTACKBASE:
395 		if (buflen == sizeof(u_long)) {
396 			if (usrstackbase != 0) {
397 				*(u_long *)buf = usrstackbase;
398 				res = 0;
399 			} else
400 				res = ENOENT;
401 		} else
402 			res = EINVAL;
403 		break;
404 	case AT_USRSTACKLIM:
405 		if (buflen == sizeof(u_long)) {
406 			if (usrstacklim != 0) {
407 				*(u_long *)buf = usrstacklim;
408 				res = 0;
409 			} else
410 				res = ENOENT;
411 		} else
412 			res = EINVAL;
413 		break;
414 	default:
415 		res = ENOENT;
416 		break;
417 	}
418 	return (res);
419 }
420