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