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