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