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