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