xref: /freebsd-13-stable/lib/libc/gen/auxv.c (revision 3d497e17ebd33fe0f58d773e35ab994d750258d6)
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 <sys/cdefs.h>
30 #include "namespace.h"
31 #include <elf.h>
32 #include <errno.h>
33 #include <link.h>
34 #include <pthread.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 static pthread_once_t aux_vector_once = PTHREAD_ONCE_INIT;
45 
46 static void
init_aux_vector_once(void)47 init_aux_vector_once(void)
48 {
49 	Elf_Addr *sp;
50 
51 	sp = (Elf_Addr *)environ;
52 	while (*sp++ != 0)
53 		;
54 	__elf_aux_vector = (Elf_Auxinfo *)sp;
55 }
56 
57 void
__init_elf_aux_vector(void)58 __init_elf_aux_vector(void)
59 {
60 
61 	if (&_DYNAMIC != NULL)
62 		return;
63 	_once(&aux_vector_once, init_aux_vector_once);
64 }
65 
66 static pthread_once_t aux_once = PTHREAD_ONCE_INIT;
67 static int pagesize, osreldate, canary_len, ncpus, pagesizes_len, bsdflags;
68 static int hwcap_present, hwcap2_present;
69 static char *canary, *pagesizes, *execpath;
70 static void *ps_strings, *timekeep;
71 static u_long hwcap, hwcap2;
72 static void *fxrng_seed_version;
73 static u_long usrstackbase, usrstacklim;
74 
75 #ifdef __powerpc__
76 static int powerpc_new_auxv_format = 0;
77 static void _init_aux_powerpc_fixup(void);
78 int _powerpc_elf_aux_info(int, void *, int);
79 #endif
80 
81 static void
init_aux(void)82 init_aux(void)
83 {
84 	Elf_Auxinfo *aux;
85 
86 	for (aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
87 		switch (aux->a_type) {
88 		case AT_BSDFLAGS:
89 			bsdflags = aux->a_un.a_val;
90 			break;
91 
92 		case AT_CANARY:
93 			canary = (char *)(aux->a_un.a_ptr);
94 			break;
95 
96 		case AT_CANARYLEN:
97 			canary_len = aux->a_un.a_val;
98 			break;
99 
100 		case AT_EXECPATH:
101 			execpath = (char *)(aux->a_un.a_ptr);
102 			break;
103 
104 		case AT_HWCAP:
105 			hwcap_present = 1;
106 			hwcap = (u_long)(aux->a_un.a_val);
107 			break;
108 
109 		case AT_HWCAP2:
110 			hwcap2_present = 1;
111 			hwcap2 = (u_long)(aux->a_un.a_val);
112 			break;
113 
114 		case AT_PAGESIZES:
115 			pagesizes = (char *)(aux->a_un.a_ptr);
116 			break;
117 
118 		case AT_PAGESIZESLEN:
119 			pagesizes_len = aux->a_un.a_val;
120 			break;
121 
122 		case AT_PAGESZ:
123 			pagesize = aux->a_un.a_val;
124 			break;
125 
126 		case AT_OSRELDATE:
127 			osreldate = aux->a_un.a_val;
128 			break;
129 
130 		case AT_NCPUS:
131 			ncpus = aux->a_un.a_val;
132 			break;
133 
134 		case AT_TIMEKEEP:
135 			timekeep = aux->a_un.a_ptr;
136 			break;
137 
138 		case AT_PS_STRINGS:
139 			ps_strings = aux->a_un.a_ptr;
140 			break;
141 
142 		case AT_FXRNG:
143 			fxrng_seed_version = aux->a_un.a_ptr;
144 			break;
145 
146 		case AT_USRSTACKBASE:
147 			usrstackbase = aux->a_un.a_val;
148 			break;
149 
150 		case AT_USRSTACKLIM:
151 			usrstacklim = aux->a_un.a_val;
152 			break;
153 #ifdef __powerpc__
154 		/*
155 		 * Since AT_STACKPROT is always set, and the common
156 		 * value 23 is mutually exclusive with the legacy powerpc
157 		 * value 21, the existence of AT_STACKPROT proves we are
158 		 * on the common format.
159 		 */
160 		case AT_STACKPROT:	/* 23 */
161 			powerpc_new_auxv_format = 1;
162 			break;
163 #endif
164 		}
165 	}
166 #ifdef __powerpc__
167 	if (!powerpc_new_auxv_format)
168 		_init_aux_powerpc_fixup();
169 #endif
170 }
171 
172 #ifdef __powerpc__
173 static void
_init_aux_powerpc_fixup(void)174 _init_aux_powerpc_fixup(void)
175 {
176 	Elf_Auxinfo *aux;
177 
178 	/*
179 	 * Before 1300070, PowerPC platforms had nonstandard numbering for
180 	 * the aux vector. When running old binaries, the kernel will pass
181 	 * the vector using the old numbering. Reload affected variables.
182 	 */
183 	for (aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
184 		switch (aux->a_type) {
185 		case AT_OLD_CANARY:
186 			canary = (char *)(aux->a_un.a_ptr);
187 			break;
188 		case AT_OLD_CANARYLEN:
189 			canary_len = aux->a_un.a_val;
190 			break;
191 		case AT_OLD_EXECPATH:
192 			execpath = (char *)(aux->a_un.a_ptr);
193 			break;
194 		case AT_OLD_PAGESIZES:
195 			pagesizes = (char *)(aux->a_un.a_ptr);
196 			break;
197 		case AT_OLD_PAGESIZESLEN:
198 			pagesizes_len = aux->a_un.a_val;
199 			break;
200 		case AT_OLD_OSRELDATE:
201 			osreldate = aux->a_un.a_val;
202 			break;
203 		case AT_OLD_NCPUS:
204 			ncpus = aux->a_un.a_val;
205 			break;
206 		}
207 	}
208 }
209 
210 int
_powerpc_elf_aux_info(int aux,void * buf,int buflen)211 _powerpc_elf_aux_info(int aux, void *buf, int buflen)
212 {
213 
214 	/*
215 	 * If we are in the old auxv format, we need to translate the aux
216 	 * parameter of elf_aux_info() calls into the common auxv format.
217 	 * Internal libc calls always use the common format, and they
218 	 * directly call _elf_aux_info instead of using the weak symbol.
219 	 */
220 	if (!powerpc_new_auxv_format) {
221 		switch (aux) {
222 		case AT_OLD_EXECPATH:
223 			aux = AT_EXECPATH;
224 			break;
225 		case AT_OLD_CANARY:
226 			aux = AT_CANARY;
227 			break;
228 		case AT_OLD_CANARYLEN:
229 			aux = AT_CANARYLEN;
230 			break;
231 		case AT_OLD_OSRELDATE:
232 			aux = AT_OSRELDATE;
233 			break;
234 		case AT_OLD_NCPUS:
235 			aux = AT_NCPUS;
236 			break;
237 		case AT_OLD_PAGESIZES:
238 			aux = AT_PAGESIZES;
239 			break;
240 		case AT_OLD_PAGESIZESLEN:
241 			aux = AT_PAGESIZESLEN;
242 			break;
243 		case AT_OLD_STACKPROT:
244 			aux = AT_STACKPROT;
245 			break;
246 		}
247 	}
248 	return _elf_aux_info(aux, buf, buflen);
249 }
250 __weak_reference(_powerpc_elf_aux_info, elf_aux_info);
251 #else
252 __weak_reference(_elf_aux_info, elf_aux_info);
253 #endif
254 
255 int
_elf_aux_info(int aux,void * buf,int buflen)256 _elf_aux_info(int aux, void *buf, int buflen)
257 {
258 	int res;
259 
260 	__init_elf_aux_vector();
261 	if (__elf_aux_vector == NULL)
262 		return (ENOSYS);
263 	_once(&aux_once, init_aux);
264 
265 	if (buflen < 0)
266 		return (EINVAL);
267 
268 	switch (aux) {
269 	case AT_CANARY:
270 		if (canary != NULL && canary_len >= buflen) {
271 			memcpy(buf, canary, buflen);
272 			memset(canary, 0, canary_len);
273 			canary = NULL;
274 			res = 0;
275 		} else
276 			res = ENOENT;
277 		break;
278 	case AT_EXECPATH:
279 		if (execpath == NULL)
280 			res = ENOENT;
281 		else if (buf == NULL)
282 			res = EINVAL;
283 		else {
284 			if (strlcpy(buf, execpath, buflen) >=
285 			    (unsigned int)buflen)
286 				res = EINVAL;
287 			else
288 				res = 0;
289 		}
290 		break;
291 	case AT_HWCAP:
292 		if (hwcap_present && buflen == sizeof(u_long)) {
293 			*(u_long *)buf = hwcap;
294 			res = 0;
295 		} else
296 			res = ENOENT;
297 		break;
298 	case AT_HWCAP2:
299 		if (hwcap2_present && buflen == sizeof(u_long)) {
300 			*(u_long *)buf = hwcap2;
301 			res = 0;
302 		} else
303 			res = ENOENT;
304 		break;
305 	case AT_PAGESIZES:
306 		if (pagesizes != NULL && pagesizes_len >= buflen) {
307 			memcpy(buf, pagesizes, buflen);
308 			res = 0;
309 		} else
310 			res = ENOENT;
311 		break;
312 	case AT_PAGESZ:
313 		if (buflen == sizeof(int)) {
314 			if (pagesize != 0) {
315 				*(int *)buf = pagesize;
316 				res = 0;
317 			} else
318 				res = ENOENT;
319 		} else
320 			res = EINVAL;
321 		break;
322 	case AT_OSRELDATE:
323 		if (buflen == sizeof(int)) {
324 			if (osreldate != 0) {
325 				*(int *)buf = osreldate;
326 				res = 0;
327 			} else
328 				res = ENOENT;
329 		} else
330 			res = EINVAL;
331 		break;
332 	case AT_NCPUS:
333 		if (buflen == sizeof(int)) {
334 			if (ncpus != 0) {
335 				*(int *)buf = ncpus;
336 				res = 0;
337 			} else
338 				res = ENOENT;
339 		} else
340 			res = EINVAL;
341 		break;
342 	case AT_TIMEKEEP:
343 		if (buflen == sizeof(void *)) {
344 			if (timekeep != NULL) {
345 				*(void **)buf = timekeep;
346 				res = 0;
347 			} else
348 				res = ENOENT;
349 		} else
350 			res = EINVAL;
351 		break;
352 	case AT_BSDFLAGS:
353 		if (buflen == sizeof(int)) {
354 			*(int *)buf = bsdflags;
355 			res = 0;
356 		} else
357 			res = EINVAL;
358 		break;
359 	case AT_PS_STRINGS:
360 		if (buflen == sizeof(void *)) {
361 			if (ps_strings != NULL) {
362 				*(void **)buf = ps_strings;
363 				res = 0;
364 			} else
365 				res = ENOENT;
366 		} else
367 			res = EINVAL;
368 		break;
369 	case AT_FXRNG:
370 		if (buflen == sizeof(void *)) {
371 			if (fxrng_seed_version != NULL) {
372 				*(void **)buf = fxrng_seed_version;
373 				res = 0;
374 			} else
375 				res = ENOENT;
376 		} else
377 			res = EINVAL;
378 		break;
379 	case AT_USRSTACKBASE:
380 		if (buflen == sizeof(u_long)) {
381 			if (usrstackbase != 0) {
382 				*(u_long *)buf = usrstackbase;
383 				res = 0;
384 			} else
385 				res = ENOENT;
386 		} else
387 			res = EINVAL;
388 		break;
389 	case AT_USRSTACKLIM:
390 		if (buflen == sizeof(u_long)) {
391 			if (usrstacklim != 0) {
392 				*(u_long *)buf = usrstacklim;
393 				res = 0;
394 			} else
395 				res = ENOENT;
396 		} else
397 			res = EINVAL;
398 		break;
399 	default:
400 		res = ENOENT;
401 		break;
402 	}
403 	return (res);
404 }
405