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