ef.c revision 134450
1/* 2 * Copyright (c) 2000, Boris Popov 3 * 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 Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $FreeBSD: head/usr.sbin/kldxref/ef.c 134450 2004-08-28 19:31:10Z iedowse $ 33 */ 34 35#include <sys/param.h> 36#include <sys/linker.h> 37#include <string.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <unistd.h> 41#include <errno.h> 42#include <fcntl.h> 43#include <machine/elf.h> 44#define FREEBSD_ELF 45#include <link.h> 46 47#include <err.h> 48 49#include "ef.h" 50 51struct ef_file { 52 char* ef_name; 53 struct elf_file *ef_efile; 54 Elf_Phdr * ef_ph; 55 int ef_fd; 56 int ef_type; 57 Elf_Ehdr ef_hdr; 58 void* ef_fpage; /* First block of the file */ 59 int ef_fplen; /* length of first block */ 60 Elf_Dyn* ef_dyn; /* Symbol table etc. */ 61 Elf_Hashelt ef_nbuckets; 62 Elf_Hashelt ef_nchains; 63 Elf_Hashelt* ef_buckets; 64 Elf_Hashelt* ef_chains; 65 Elf_Hashelt* ef_hashtab; 66 Elf_Off ef_stroff; 67 caddr_t ef_strtab; 68 int ef_strsz; 69 Elf_Off ef_symoff; 70 Elf_Sym* ef_symtab; 71 int ef_nsegs; 72 Elf_Phdr * ef_segs[2]; 73 int ef_verbose; 74 Elf_Rel * ef_rel; /* relocation table */ 75 int ef_relsz; /* number of entries */ 76 Elf_Rela * ef_rela; /* relocation table */ 77 int ef_relasz; /* number of entries */ 78}; 79 80static void ef_print_phdr(Elf_Phdr *); 81static u_long ef_get_offset(elf_file_t, Elf_Off); 82static int ef_parse_dynamic(elf_file_t); 83 84static int ef_get_type(elf_file_t ef); 85static int ef_close(elf_file_t ef); 86static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest); 87static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr); 88static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest); 89static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, 90 void *dest); 91static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, 92 void **ptr); 93static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, 94 void **ptr); 95static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Word symidx); 96static int ef_lookup_set(elf_file_t ef, const char *name, long *startp, 97 long *stopp, long *countp); 98static int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym); 99 100static struct elf_file_ops ef_file_ops = { 101 ef_get_type, 102 ef_close, 103 ef_read, 104 ef_read_entry, 105 ef_seg_read, 106 ef_seg_read_rel, 107 ef_seg_read_entry, 108 ef_seg_read_entry_rel, 109 ef_symaddr, 110 ef_lookup_set, 111 ef_lookup_symbol 112}; 113 114static void 115ef_print_phdr(Elf_Phdr *phdr) 116{ 117 118 if ((phdr->p_flags & PF_W) == 0) { 119 printf("text=0x%lx ", (long)phdr->p_filesz); 120 } else { 121 printf("data=0x%lx", (long)phdr->p_filesz); 122 if (phdr->p_filesz < phdr->p_memsz) 123 printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz)); 124 printf(" "); 125 } 126} 127 128static u_long 129ef_get_offset(elf_file_t ef, Elf_Off off) 130{ 131 Elf_Phdr *ph; 132 int i; 133 134 for (i = 0; i < ef->ef_nsegs; i++) { 135 ph = ef->ef_segs[i]; 136 if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) { 137 return ph->p_offset + (off - ph->p_vaddr); 138 } 139 } 140 return 0; 141} 142 143static int 144ef_get_type(elf_file_t ef) 145{ 146 147 return (ef->ef_type); 148} 149 150/* 151 * next three functions copied from link_elf.c 152 */ 153static unsigned long 154elf_hash(const char *name) 155{ 156 const unsigned char *p = (const unsigned char *) name; 157 unsigned long h = 0; 158 unsigned long g; 159 160 while (*p != '\0') { 161 h = (h << 4) + *p++; 162 if ((g = h & 0xf0000000) != 0) 163 h ^= g >> 24; 164 h &= ~g; 165 } 166 return h; 167} 168 169static int 170ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) 171{ 172 unsigned long symnum; 173 Elf_Sym* symp; 174 char *strp; 175 unsigned long hash; 176 177 /* First, search hashed global symbols */ 178 hash = elf_hash(name); 179 symnum = ef->ef_buckets[hash % ef->ef_nbuckets]; 180 181 while (symnum != STN_UNDEF) { 182 if (symnum >= ef->ef_nchains) { 183 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 184 ef->ef_name); 185 return ENOENT; 186 } 187 188 symp = ef->ef_symtab + symnum; 189 if (symp->st_name == 0) { 190 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 191 ef->ef_name); 192 return ENOENT; 193 } 194 195 strp = ef->ef_strtab + symp->st_name; 196 197 if (strcmp(name, strp) == 0) { 198 if (symp->st_shndx != SHN_UNDEF || 199 (symp->st_value != 0 && 200 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 201 *sym = symp; 202 return 0; 203 } else 204 return ENOENT; 205 } 206 207 symnum = ef->ef_chains[symnum]; 208 } 209 210 return ENOENT; 211} 212 213static int 214ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, 215 long *countp) 216{ 217 Elf_Sym *sym; 218 char *setsym; 219 int error, len; 220 221 len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ 222 setsym = malloc(len); 223 if (setsym == NULL) 224 return (ENOMEM); 225 226 /* get address of first entry */ 227 snprintf(setsym, len, "%s%s", "__start_set_", name); 228 error = ef_lookup_symbol(ef, setsym, &sym); 229 if (error) 230 goto out; 231 *startp = sym->st_value; 232 233 /* get address of last entry */ 234 snprintf(setsym, len, "%s%s", "__stop_set_", name); 235 error = ef_lookup_symbol(ef, setsym, &sym); 236 if (error) 237 goto out; 238 *stopp = sym->st_value; 239 240 /* and the number of entries */ 241 *countp = (*stopp - *startp) / sizeof(void *); 242 243out: 244 free(setsym); 245 return (error); 246} 247 248static Elf_Addr 249ef_symaddr(elf_file_t ef, Elf_Word symidx) 250{ 251 const Elf_Sym *sym; 252 253 if (symidx >= ef->ef_nchains) 254 return (0); 255 sym = ef->ef_symtab + symidx; 256 257 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL && 258 sym->st_shndx != SHN_UNDEF && sym->st_value != 0) 259 return (sym->st_value); 260 return (0); 261} 262 263static int 264ef_parse_dynamic(elf_file_t ef) 265{ 266 Elf_Dyn *dp; 267 Elf_Hashelt hashhdr[2]; 268/* int plttype = DT_REL;*/ 269 int error; 270 Elf_Off rel_off; 271 Elf_Off rela_off; 272 int rel_sz; 273 int rela_sz; 274 int rel_entry; 275 int rela_entry; 276 277 rel_off = rela_off = 0; 278 rel_sz = rela_sz = 0; 279 rel_entry = rela_entry = 0; 280 for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) { 281 switch (dp->d_tag) { 282 case DT_HASH: 283 error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr), 284 sizeof(hashhdr), hashhdr); 285 if (error) { 286 warnx("can't read hash header (%lx)", 287 ef_get_offset(ef, dp->d_un.d_ptr)); 288 return error; 289 } 290 ef->ef_nbuckets = hashhdr[0]; 291 ef->ef_nchains = hashhdr[1]; 292 error = ef_read_entry(ef, -1, 293 (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt), 294 (void**)&ef->ef_hashtab); 295 if (error) { 296 warnx("can't read hash table"); 297 return error; 298 } 299 ef->ef_buckets = ef->ef_hashtab; 300 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; 301 break; 302 case DT_STRTAB: 303 ef->ef_stroff = dp->d_un.d_ptr; 304 break; 305 case DT_STRSZ: 306 ef->ef_strsz = dp->d_un.d_val; 307 break; 308 case DT_SYMTAB: 309 ef->ef_symoff = dp->d_un.d_ptr; 310 break; 311 case DT_SYMENT: 312 if (dp->d_un.d_val != sizeof(Elf_Sym)) 313 return EFTYPE; 314 break; 315 case DT_REL: 316 if (rel_off != 0) 317 warnx("second DT_REL entry ignored"); 318 rel_off = dp->d_un.d_ptr; 319 break; 320 case DT_RELSZ: 321 if (rel_sz != 0) 322 warnx("second DT_RELSZ entry ignored"); 323 rel_sz = dp->d_un.d_val; 324 break; 325 case DT_RELENT: 326 if (rel_entry != 0) 327 warnx("second DT_RELENT entry ignored"); 328 rel_entry = dp->d_un.d_val; 329 break; 330 case DT_RELA: 331 if (rela_off != 0) 332 warnx("second DT_RELA entry ignored"); 333 rela_off = dp->d_un.d_ptr; 334 break; 335 case DT_RELASZ: 336 if (rela_sz != 0) 337 warnx("second DT_RELASZ entry ignored"); 338 rela_sz = dp->d_un.d_val; 339 break; 340 case DT_RELAENT: 341 if (rela_entry != 0) 342 warnx("second DT_RELAENT entry ignored"); 343 rela_entry = dp->d_un.d_val; 344 break; 345 } 346 } 347 if (ef->ef_symoff == 0) { 348 warnx("%s: no .dynsym section found\n", ef->ef_name); 349 return EFTYPE; 350 } 351 if (ef->ef_stroff == 0) { 352 warnx("%s: no .dynstr section found\n", ef->ef_name); 353 return EFTYPE; 354 } 355 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff), 356 ef->ef_nchains * sizeof(Elf_Sym), 357 (void**)&ef->ef_symtab) != 0) { 358 if (ef->ef_verbose) 359 warnx("%s: can't load .dynsym section (0x%lx)", 360 ef->ef_name, (long)ef->ef_symoff); 361 return EIO; 362 } 363 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz, 364 (void**)&ef->ef_strtab) != 0) { 365 warnx("can't load .dynstr section"); 366 return EIO; 367 } 368 if (rel_off != 0) { 369 if (rel_entry == 0) { 370 warnx("%s: no DT_RELENT for DT_REL", ef->ef_name); 371 return (EFTYPE); 372 } 373 if (rel_entry != sizeof(Elf_Rel)) { 374 warnx("%s: inconsistent DT_RELENT value", 375 ef->ef_name); 376 return (EFTYPE); 377 } 378 if (rel_sz % rel_entry != 0) { 379 warnx("%s: inconsistent values for DT_RELSZ and " 380 "DT_RELENT", ef->ef_name); 381 return (EFTYPE); 382 } 383 if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz, 384 (void **)&ef->ef_rel) != 0) { 385 warnx("%s: cannot load DT_REL section", ef->ef_name); 386 return (EIO); 387 } 388 ef->ef_relsz = rel_sz / rel_entry; 389 if (ef->ef_verbose) 390 warnx("%s: %d REL entries", ef->ef_name, 391 ef->ef_relsz); 392 } 393 if (rela_off != 0) { 394 if (rela_entry == 0) { 395 warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name); 396 return (EFTYPE); 397 } 398 if (rela_entry != sizeof(Elf_Rela)) { 399 warnx("%s: inconsistent DT_RELAENT value", 400 ef->ef_name); 401 return (EFTYPE); 402 } 403 if (rela_sz % rela_entry != 0) { 404 warnx("%s: inconsistent values for DT_RELASZ and " 405 "DT_RELAENT", ef->ef_name); 406 return (EFTYPE); 407 } 408 if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz, 409 (void **)&ef->ef_rela) != 0) { 410 warnx("%s: cannot load DT_RELA section", ef->ef_name); 411 return (EIO); 412 } 413 ef->ef_relasz = rela_sz / rela_entry; 414 if (ef->ef_verbose) 415 warnx("%s: %d RELA entries", ef->ef_name, 416 ef->ef_relasz); 417 } 418 return 0; 419} 420 421static int 422ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 423{ 424 ssize_t r; 425 426 if (offset != (Elf_Off)-1) { 427 if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) 428 return EIO; 429 } 430 431 r = read(ef->ef_fd, dest, len); 432 if (r != -1 && (size_t)r == len) 433 return 0; 434 else 435 return EIO; 436} 437 438static int 439ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 440{ 441 int error; 442 443 *ptr = malloc(len); 444 if (*ptr == NULL) 445 return ENOMEM; 446 error = ef_read(ef, offset, len, *ptr); 447 if (error) 448 free(*ptr); 449 return error; 450} 451 452static int 453ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 454{ 455 u_long ofs = ef_get_offset(ef, offset); 456 457 if (ofs == 0) { 458 if (ef->ef_verbose) 459 warnx("ef_seg_read(%s): zero offset (%lx:%ld)", 460 ef->ef_name, (long)offset, ofs); 461 return EFAULT; 462 } 463 return ef_read(ef, ofs, len, dest); 464} 465 466static int 467ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void*dest) 468{ 469 u_long ofs = ef_get_offset(ef, offset); 470 const Elf_Rela *a; 471 const Elf_Rel *r; 472 int error; 473 474 if (ofs == 0) { 475 if (ef->ef_verbose) 476 warnx("ef_seg_read(%s): zero offset (%lx:%ld)", 477 ef->ef_name, (long)offset, ofs); 478 return EFAULT; 479 } 480 if ((error = ef_read(ef, ofs, len, dest)) != 0) 481 return (error); 482 483 for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) { 484 error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len, 485 dest); 486 if (error != 0) 487 return (error); 488 } 489 for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) { 490 error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len, 491 dest); 492 if (error != 0) 493 return (error); 494 } 495 return (0); 496} 497 498static int 499ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 500{ 501 int error; 502 503 *ptr = malloc(len); 504 if (*ptr == NULL) 505 return ENOMEM; 506 error = ef_seg_read(ef, offset, len, *ptr); 507 if (error) 508 free(*ptr); 509 return error; 510} 511 512static int 513ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr) 514{ 515 int error; 516 517 *ptr = malloc(len); 518 if (*ptr == NULL) 519 return ENOMEM; 520 error = ef_seg_read_rel(ef, offset, len, *ptr); 521 if (error) 522 free(*ptr); 523 return error; 524} 525 526int 527ef_open(const char *filename, struct elf_file *efile, int verbose) 528{ 529 elf_file_t ef; 530 Elf_Ehdr *hdr; 531 int fd; 532 int error; 533 int phlen, res; 534 int nsegs; 535 Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit; 536 537 if (filename == NULL) 538 return EFTYPE; 539 if ((fd = open(filename, O_RDONLY)) == -1) 540 return errno; 541 542 ef = malloc(sizeof(*ef)); 543 if (ef == NULL) { 544 close(fd); 545 return (ENOMEM); 546 } 547 548 efile->ef_ef = ef; 549 efile->ef_ops = &ef_file_ops; 550 551 bzero(ef, sizeof(*ef)); 552 ef->ef_verbose = verbose; 553 ef->ef_fd = fd; 554 ef->ef_name = strdup(filename); 555 ef->ef_efile = efile; 556 hdr = (Elf_Ehdr *)&ef->ef_hdr; 557 do { 558 res = read(fd, hdr, sizeof(*hdr)); 559 error = EFTYPE; 560 if (res != sizeof(*hdr)) 561 break; 562 if (!IS_ELF(*hdr)) 563 break; 564 if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || 565 hdr->e_ident[EI_DATA] != ELF_TARG_DATA || 566 hdr->e_ident[EI_VERSION] != EV_CURRENT || 567 hdr->e_version != EV_CURRENT || 568 hdr->e_machine != ELF_TARG_MACH || 569 hdr->e_phentsize != sizeof(Elf_Phdr)) 570 break; 571 phlen = hdr->e_phnum * sizeof(Elf_Phdr); 572 if (ef_read_entry(ef, hdr->e_phoff, phlen, 573 (void**)&ef->ef_ph) != 0) 574 break; 575 phdr = ef->ef_ph; 576 phlimit = phdr + hdr->e_phnum; 577 nsegs = 0; 578 phdyn = NULL; 579 phphdr = NULL; 580 while (phdr < phlimit) { 581 if (verbose > 1) 582 ef_print_phdr(phdr); 583 switch (phdr->p_type) { 584 case PT_LOAD: 585 if (nsegs == 2) { 586 warnx("%s: too many sections", 587 filename); 588 break; 589 } 590 ef->ef_segs[nsegs++] = phdr; 591 break; 592 case PT_PHDR: 593 phphdr = phdr; 594 break; 595 case PT_DYNAMIC: 596 phdyn = phdr; 597 break; 598 } 599 phdr++; 600 } 601 if (verbose > 1) 602 printf("\n"); 603 ef->ef_nsegs = nsegs; 604 if (phdyn == NULL) { 605 warnx("file isn't dynamically-linked"); 606 break; 607 } 608 if (ef_read_entry(ef, phdyn->p_offset, 609 phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) { 610 printf("ef_read_entry failed\n"); 611 break; 612 } 613 error = ef_parse_dynamic(ef); 614 if (error) 615 break; 616 if (hdr->e_type == ET_DYN) { 617 ef->ef_type = EFT_KLD; 618/* pad = (u_int)dest & PAGE_MASK; 619 if (pad) 620 dest += PAGE_SIZE - pad;*/ 621 error = 0; 622 } else if (hdr->e_type == ET_EXEC) { 623/* dest = hdr->e_entry; 624 if (dest == 0) 625 break;*/ 626 ef->ef_type = EFT_KERNEL; 627 error = 0; 628 } else 629 break; 630 } while(0); 631 if (error) 632 ef_close(ef); 633 return error; 634} 635 636static int 637ef_close(elf_file_t ef) 638{ 639 close(ef->ef_fd); 640/* if (ef->ef_fpage) 641 free(ef->ef_fpage);*/ 642 if (ef->ef_name) 643 free(ef->ef_name); 644 ef->ef_efile->ef_ops = NULL; 645 ef->ef_efile->ef_ef = NULL; 646 free(ef); 647 return 0; 648} 649