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