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 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 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 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 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 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 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