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