1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #if defined(LIBC_SCCS) && !defined(lint) 31 static char rcsid[] = "$OpenBSD: nlist.c,v 1.45 2003/06/25 21:16:47 deraadt Exp $"; 32 #endif /* LIBC_SCCS and not lint */ 33 34 #include <sys/types.h> 35 #include <sys/param.h> 36 #include <sys/mman.h> 37 #include <sys/stat.h> 38 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <a.out.h> /* pulls in nlist.h */ 46 47 #ifdef _NLIST_DO_ELF 48 #include <elf_abi.h> 49 #include <olf_abi.h> 50 #endif 51 52 #ifdef _NLIST_DO_ECOFF 53 #include <sys/exec_ecoff.h> 54 #endif 55 56 int __fdnlist(int, struct nlist *); 57 int __aout_fdnlist(int, struct nlist *); 58 int __ecoff_fdnlist(int, struct nlist *); 59 int __elf_fdnlist(int, struct nlist *); 60 #ifdef _NLIST_DO_ELF 61 int __elf_is_okay__(register Elf_Ehdr *ehdr); 62 #endif 63 64 #define ISLAST(p) (p->n_un.n_name == 0 || p->n_un.n_name[0] == 0) 65 66 #ifdef _NLIST_DO_AOUT 67 int 68 __aout_fdnlist(fd, list) 69 register int fd; 70 register struct nlist *list; 71 { 72 register struct nlist *p, *s; 73 register char *strtab; 74 register off_t symoff, stroff; 75 register u_long symsize; 76 register int nent, cc; 77 int strsize, usemalloc = 0; 78 struct nlist nbuf[1024]; 79 struct exec exec; 80 81 if (pread(fd, &exec, sizeof(exec), (off_t)0) != sizeof(exec) || 82 N_BADMAG(exec) || exec.a_syms == NULL) 83 return (-1); 84 85 stroff = N_STROFF(exec); 86 symoff = N_SYMOFF(exec); 87 symsize = exec.a_syms; 88 89 /* Read in the size of the string table. */ 90 if (pread(fd, (void *)&strsize, sizeof(strsize), stroff) != 91 sizeof(strsize)) 92 return (-1); 93 else 94 stroff += sizeof(strsize); 95 96 /* 97 * Read in the string table. We try mmap, but that will fail 98 * for /dev/ksyms so fall back on malloc. Since OpenBSD's malloc(3) 99 * returns memory to the system on free this does not cause bloat. 100 */ 101 strsize -= sizeof(strsize); 102 strtab = mmap(NULL, (size_t)strsize, PROT_READ, MAP_SHARED|MAP_FILE, 103 fd, stroff); 104 if (strtab == MAP_FAILED) { 105 usemalloc = 1; 106 if ((strtab = (char *)malloc(strsize)) == NULL) 107 return (-1); 108 errno = EIO; 109 if (pread(fd, strtab, strsize, stroff) != strsize) { 110 nent = -1; 111 goto aout_done; 112 } 113 } 114 115 /* 116 * clean out any left-over information for all valid entries. 117 * Type and value defined to be 0 if not found; historical 118 * versions cleared other and desc as well. Also figure out 119 * the largest string length so don't read any more of the 120 * string table than we have to. 121 * 122 * XXX clearing anything other than n_type and n_value violates 123 * the semantics given in the man page. 124 */ 125 nent = 0; 126 for (p = list; !ISLAST(p); ++p) { 127 p->n_type = 0; 128 p->n_other = 0; 129 p->n_desc = 0; 130 p->n_value = 0; 131 ++nent; 132 } 133 134 while (symsize > 0) { 135 cc = MIN(symsize, sizeof(nbuf)); 136 if (pread(fd, nbuf, cc, symoff) != cc) 137 break; 138 symsize -= cc; 139 symoff += cc; 140 for (s = nbuf; cc > 0; ++s, cc -= sizeof(*s)) { 141 char *sname = strtab + s->n_un.n_strx - sizeof(int); 142 143 if (s->n_un.n_strx == 0 || (s->n_type & N_STAB) != 0) 144 continue; 145 for (p = list; !ISLAST(p); p++) { 146 char *pname = p->n_un.n_name; 147 148 if (*sname != '_' && *pname == '_') 149 pname++; 150 if (!strcmp(sname, pname)) { 151 p->n_value = s->n_value; 152 p->n_type = s->n_type; 153 p->n_desc = s->n_desc; 154 p->n_other = s->n_other; 155 if (--nent <= 0) 156 break; 157 } 158 } 159 } 160 } 161 aout_done: 162 if (usemalloc) 163 free(strtab); 164 else 165 munmap(strtab, strsize); 166 return (nent); 167 } 168 #endif /* _NLIST_DO_AOUT */ 169 170 #ifdef _NLIST_DO_ECOFF 171 #define check(off, size) ((off < 0) || (off + size > mappedsize)) 172 #define BAD do { rv = -1; goto out; } while (0) 173 #define BADUNMAP do { rv = -1; goto unmap; } while (0) 174 175 int 176 __ecoff_fdnlist(fd, list) 177 register int fd; 178 register struct nlist *list; 179 { 180 struct nlist *p; 181 struct ecoff_exechdr *exechdrp; 182 struct ecoff_symhdr *symhdrp; 183 struct ecoff_extsym *esyms; 184 struct stat st; 185 char *mappedfile; 186 size_t mappedsize; 187 u_long symhdroff, extstroff; 188 u_int symhdrsize; 189 int rv, nent; 190 long i, nesyms; 191 192 rv = -3; 193 194 if (fstat(fd, &st) < 0) 195 BAD; 196 if (st.st_size > SIZE_T_MAX) { 197 errno = EFBIG; 198 BAD; 199 } 200 mappedsize = st.st_size; 201 mappedfile = mmap(NULL, mappedsize, PROT_READ, MAP_SHARED|MAP_FILE, 202 fd, 0); 203 if (mappedfile == MAP_FAILED) 204 BAD; 205 206 if (check(0, sizeof *exechdrp)) 207 BADUNMAP; 208 exechdrp = (struct ecoff_exechdr *)&mappedfile[0]; 209 210 if (ECOFF_BADMAG(exechdrp)) 211 BADUNMAP; 212 213 symhdroff = exechdrp->f.f_symptr; 214 symhdrsize = exechdrp->f.f_nsyms; 215 216 if (check(symhdroff, sizeof *symhdrp) || 217 sizeof *symhdrp != symhdrsize) 218 BADUNMAP; 219 symhdrp = (struct ecoff_symhdr *)&mappedfile[symhdroff]; 220 221 nesyms = symhdrp->esymMax; 222 if (check(symhdrp->cbExtOffset, nesyms * sizeof *esyms)) 223 BADUNMAP; 224 esyms = (struct ecoff_extsym *)&mappedfile[symhdrp->cbExtOffset]; 225 extstroff = symhdrp->cbSsExtOffset; 226 227 /* 228 * clean out any left-over information for all valid entries. 229 * Type and value defined to be 0 if not found; historical 230 * versions cleared other and desc as well. 231 * 232 * XXX clearing anything other than n_type and n_value violates 233 * the semantics given in the man page. 234 */ 235 nent = 0; 236 for (p = list; !ISLAST(p); ++p) { 237 p->n_type = 0; 238 p->n_other = 0; 239 p->n_desc = 0; 240 p->n_value = 0; 241 ++nent; 242 } 243 244 for (i = 0; i < nesyms; i++) { 245 for (p = list; !ISLAST(p); p++) { 246 char *nlistname; 247 char *symtabname; 248 249 nlistname = p->n_un.n_name; 250 if (*nlistname == '_') 251 nlistname++; 252 symtabname = 253 &mappedfile[extstroff + esyms[i].es_strindex]; 254 255 if (!strcmp(symtabname, nlistname)) { 256 p->n_value = esyms[i].es_value; 257 p->n_type = N_EXT; /* XXX */ 258 p->n_desc = 0; /* XXX */ 259 p->n_other = 0; /* XXX */ 260 if (--nent <= 0) 261 break; 262 } 263 } 264 } 265 rv = nent; 266 267 unmap: 268 munmap(mappedfile, mappedsize); 269 out: 270 return (rv); 271 } 272 #endif /* _NLIST_DO_ECOFF */ 273 274 #ifdef _NLIST_DO_ELF 275 /* 276 * __elf_is_okay__ - Determine if ehdr really 277 * is ELF and valid for the target platform. 278 * 279 * WARNING: This is NOT a ELF ABI function and 280 * as such it's use should be restricted. 281 */ 282 int 283 __elf_is_okay__(ehdr) 284 register Elf_Ehdr *ehdr; 285 { 286 register int retval = 0; 287 /* 288 * We need to check magic, class size, endianess, 289 * and version before we look at the rest of the 290 * Elf_Ehdr structure. These few elements are 291 * represented in a machine independent fashion. 292 */ 293 if ((IS_ELF(*ehdr) || IS_OLF(*ehdr)) && 294 ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS && 295 ehdr->e_ident[EI_DATA] == ELF_TARG_DATA && 296 ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) { 297 298 /* Now check the machine dependant header */ 299 if (ehdr->e_machine == ELF_TARG_MACH && 300 ehdr->e_version == ELF_TARG_VER) 301 retval = 1; 302 } 303 304 return retval; 305 } 306 307 int 308 __elf_fdnlist(fd, list) 309 register int fd; 310 register struct nlist *list; 311 { 312 register struct nlist *p; 313 register caddr_t strtab; 314 register Elf_Off symoff = 0, symstroff = 0; 315 register Elf_Word symsize = 0, symstrsize = 0; 316 register Elf_Sword nent, cc, i; 317 Elf_Sym sbuf[1024]; 318 Elf_Sym *s; 319 Elf_Ehdr ehdr; 320 Elf_Shdr *shdr = NULL; 321 Elf_Word shdr_size; 322 struct stat st; 323 int usemalloc = 0; 324 325 /* Make sure obj is OK */ 326 if (pread(fd, &ehdr, sizeof(Elf_Ehdr), (off_t)0) != sizeof(Elf_Ehdr) || 327 !__elf_is_okay__(&ehdr) || fstat(fd, &st) < 0) 328 return (-1); 329 330 /* calculate section header table size */ 331 shdr_size = ehdr.e_shentsize * ehdr.e_shnum; 332 333 /* Make sure it's not too big to mmap */ 334 if (shdr_size > SIZE_T_MAX) { 335 errno = EFBIG; 336 return (-1); 337 } 338 339 /* mmap section header table */ 340 shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ, 341 MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff); 342 if (shdr == MAP_FAILED) { 343 usemalloc = 1; 344 if ((shdr = malloc(shdr_size)) == NULL) 345 return (-1); 346 if (pread(fd, shdr, shdr_size, ehdr.e_shoff) != shdr_size) { 347 free(shdr); 348 return (-1); 349 } 350 } 351 352 /* 353 * Find the symbol table entry and it's corresponding 354 * string table entry. Version 1.1 of the ABI states 355 * that there is only one symbol table but that this 356 * could change in the future. 357 */ 358 for (i = 0; i < ehdr.e_shnum; i++) { 359 if (shdr[i].sh_type == SHT_SYMTAB) { 360 symoff = shdr[i].sh_offset; 361 symsize = shdr[i].sh_size; 362 symstroff = shdr[shdr[i].sh_link].sh_offset; 363 symstrsize = shdr[shdr[i].sh_link].sh_size; 364 break; 365 } 366 } 367 368 /* Flush the section header table */ 369 if (usemalloc) 370 free(shdr); 371 else 372 munmap((caddr_t)shdr, shdr_size); 373 374 /* Check for files too large to mmap. */ 375 /* XXX is this really possible? */ 376 if (symstrsize > SIZE_T_MAX) { 377 errno = EFBIG; 378 return (-1); 379 } 380 /* 381 * Map string table into our address space. This gives us 382 * an easy way to randomly access all the strings, without 383 * making the memory allocation permanent as with malloc/free 384 * (i.e., munmap will return it to the system). 385 */ 386 if (usemalloc) { 387 if ((strtab = malloc(symstrsize)) == NULL) 388 return (-1); 389 if (pread(fd, strtab, symstrsize, symstroff) != symstrsize) { 390 free(strtab); 391 return (-1); 392 } 393 } else { 394 strtab = mmap(NULL, (size_t)symstrsize, PROT_READ, 395 MAP_SHARED|MAP_FILE, fd, (off_t) symstroff); 396 if (strtab == MAP_FAILED) 397 return (-1); 398 } 399 /* 400 * clean out any left-over information for all valid entries. 401 * Type and value defined to be 0 if not found; historical 402 * versions cleared other and desc as well. Also figure out 403 * the largest string length so don't read any more of the 404 * string table than we have to. 405 * 406 * XXX clearing anything other than n_type and n_value violates 407 * the semantics given in the man page. 408 */ 409 nent = 0; 410 for (p = list; !ISLAST(p); ++p) { 411 p->n_type = 0; 412 p->n_other = 0; 413 p->n_desc = 0; 414 p->n_value = 0; 415 ++nent; 416 } 417 418 /* Don't process any further if object is stripped. */ 419 /* ELFism - dunno if stripped by looking at header */ 420 if (symoff == 0) 421 goto elf_done; 422 423 while (symsize > 0) { 424 cc = MIN(symsize, sizeof(sbuf)); 425 if (pread(fd, sbuf, cc, symoff) != cc) 426 break; 427 symsize -= cc; 428 symoff += cc; 429 for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) { 430 int soff = s->st_name; 431 432 if (soff == 0) 433 continue; 434 for (p = list; !ISLAST(p); p++) { 435 char *sym; 436 437 /* 438 * First we check for the symbol as it was 439 * provided by the user. If that fails, 440 * skip the first char if it's an '_' and 441 * try again. 442 * XXX - What do we do when the user really 443 * wants '_foo' and the are symbols 444 * for both 'foo' and '_foo' in the 445 * table and 'foo' is first? 446 */ 447 sym = p->n_un.n_name; 448 if (strcmp(&strtab[soff], sym) != 0 && 449 ((sym[0] == '_') && 450 strcmp(&strtab[soff], sym + 1) != 0)) 451 continue; 452 453 p->n_value = s->st_value; 454 455 /* XXX - type conversion */ 456 /* is pretty rude. */ 457 switch(ELF_ST_TYPE(s->st_info)) { 458 case STT_NOTYPE: 459 p->n_type = N_UNDF; 460 break; 461 case STT_OBJECT: 462 p->n_type = N_DATA; 463 break; 464 case STT_FUNC: 465 p->n_type = N_TEXT; 466 break; 467 case STT_FILE: 468 p->n_type = N_FN; 469 break; 470 } 471 if (ELF_ST_BIND(s->st_info) == 472 STB_LOCAL) 473 p->n_type = N_EXT; 474 p->n_desc = 0; 475 p->n_other = 0; 476 if (--nent <= 0) 477 break; 478 } 479 } 480 } 481 elf_done: 482 if (usemalloc) 483 free(strtab); 484 else 485 munmap(strtab, symstrsize); 486 return (nent); 487 } 488 #endif /* _NLIST_DO_ELF */ 489 490 491 static struct nlist_handlers { 492 int (*fn)(int fd, struct nlist *list); 493 } nlist_fn[] = { 494 #ifdef _NLIST_DO_AOUT 495 { __aout_fdnlist }, 496 #endif 497 #ifdef _NLIST_DO_ELF 498 { __elf_fdnlist }, 499 #endif 500 #ifdef _NLIST_DO_ECOFF 501 { __ecoff_fdnlist }, 502 #endif 503 }; 504 505 int 506 __fdnlist(fd, list) 507 register int fd; 508 register struct nlist *list; 509 { 510 int n = -1, i; 511 512 for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) { 513 n = (nlist_fn[i].fn)(fd, list); 514 if (n != -1) 515 break; 516 } 517 return (n); 518 } 519 520 521 int 522 nlist(name, list) 523 const char *name; 524 struct nlist *list; 525 { 526 int fd, n; 527 528 fd = open(name, O_RDONLY, 0); 529 if (fd < 0) 530 return (-1); 531 n = __fdnlist(fd, list); 532 (void)close(fd); 533 return (n); 534 } 535