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 35 #include <sys/param.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <gelf.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 44 #include "kldelf.h" 45 46 #define MAXSEGS 16 47 struct ef_file { 48 char *ef_name; 49 struct elf_file *ef_efile; 50 GElf_Phdr *ef_ph; 51 void *ef_fpage; /* First block of the file */ 52 int ef_fplen; /* length of first block */ 53 GElf_Hashelt ef_nbuckets; 54 GElf_Hashelt ef_nchains; 55 GElf_Hashelt *ef_buckets; 56 GElf_Hashelt *ef_chains; 57 GElf_Hashelt *ef_hashtab; 58 caddr_t ef_strtab; 59 long ef_strsz; 60 GElf_Sym *ef_symtab; 61 int ef_nsegs; 62 GElf_Phdr *ef_segs[MAXSEGS]; 63 int ef_verbose; 64 GElf_Rel *ef_rel; /* relocation table */ 65 long ef_relsz; /* number of entries */ 66 GElf_Rela *ef_rela; /* relocation table */ 67 long ef_relasz; /* number of entries */ 68 }; 69 70 static void ef_print_phdr(GElf_Phdr *); 71 static GElf_Off ef_get_offset(elf_file_t, GElf_Addr); 72 73 static void ef_close(elf_file_t ef); 74 75 static int ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, 76 void *dest); 77 static int ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, 78 char *dest); 79 80 static GElf_Addr ef_symaddr(elf_file_t ef, GElf_Size symidx); 81 static int ef_lookup_set(elf_file_t ef, const char *name, 82 GElf_Addr *startp, GElf_Addr *stopp, long *countp); 83 static int ef_lookup_symbol(elf_file_t ef, const char *name, 84 GElf_Sym **sym); 85 86 static struct elf_file_ops ef_file_ops = { 87 .close = ef_close, 88 .seg_read_rel = ef_seg_read_rel, 89 .seg_read_string = ef_seg_read_string, 90 .symaddr = ef_symaddr, 91 .lookup_set = ef_lookup_set, 92 .lookup_symbol = ef_lookup_symbol, 93 }; 94 95 static void 96 ef_print_phdr(GElf_Phdr *phdr) 97 { 98 99 if ((phdr->p_flags & PF_W) == 0) { 100 printf("text=0x%jx ", (uintmax_t)phdr->p_filesz); 101 } else { 102 printf("data=0x%jx", (uintmax_t)phdr->p_filesz); 103 if (phdr->p_filesz < phdr->p_memsz) 104 printf("+0x%jx", 105 (uintmax_t)(phdr->p_memsz - phdr->p_filesz)); 106 printf(" "); 107 } 108 } 109 110 static GElf_Off 111 ef_get_offset(elf_file_t ef, GElf_Addr addr) 112 { 113 GElf_Phdr *ph; 114 int i; 115 116 for (i = 0; i < ef->ef_nsegs; i++) { 117 ph = ef->ef_segs[i]; 118 if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) { 119 return (ph->p_offset + (addr - ph->p_vaddr)); 120 } 121 } 122 return (0); 123 } 124 125 /* 126 * next two functions copied from link_elf.c 127 */ 128 static int 129 ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym) 130 { 131 unsigned long hash, symnum; 132 GElf_Sym *symp; 133 char *strp; 134 135 /* First, search hashed global symbols */ 136 hash = elf_hash(name); 137 symnum = ef->ef_buckets[hash % ef->ef_nbuckets]; 138 139 while (symnum != STN_UNDEF) { 140 if (symnum >= ef->ef_nchains) { 141 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 142 ef->ef_name); 143 return (ENOENT); 144 } 145 146 symp = ef->ef_symtab + symnum; 147 if (symp->st_name == 0) { 148 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n", 149 ef->ef_name); 150 return (ENOENT); 151 } 152 153 strp = ef->ef_strtab + symp->st_name; 154 155 if (strcmp(name, strp) == 0) { 156 if (symp->st_shndx != SHN_UNDEF || 157 (symp->st_value != 0 && 158 GELF_ST_TYPE(symp->st_info) == STT_FUNC)) { 159 *sym = symp; 160 return (0); 161 } else 162 return (ENOENT); 163 } 164 165 symnum = ef->ef_chains[symnum]; 166 } 167 168 return (ENOENT); 169 } 170 171 static int 172 ef_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp, 173 GElf_Addr *stopp, long *countp) 174 { 175 GElf_Sym *sym; 176 char *setsym; 177 int error, len; 178 179 len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */ 180 setsym = malloc(len); 181 if (setsym == NULL) 182 return (errno); 183 184 /* get address of first entry */ 185 snprintf(setsym, len, "%s%s", "__start_set_", name); 186 error = ef_lookup_symbol(ef, setsym, &sym); 187 if (error != 0) 188 goto out; 189 *startp = sym->st_value; 190 191 /* get address of last entry */ 192 snprintf(setsym, len, "%s%s", "__stop_set_", name); 193 error = ef_lookup_symbol(ef, setsym, &sym); 194 if (error != 0) 195 goto out; 196 *stopp = sym->st_value; 197 198 /* and the number of entries */ 199 *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile); 200 201 out: 202 free(setsym); 203 return (error); 204 } 205 206 static GElf_Addr 207 ef_symaddr(elf_file_t ef, GElf_Size symidx) 208 { 209 const GElf_Sym *sym; 210 211 if (symidx >= ef->ef_nchains) 212 return (0); 213 sym = ef->ef_symtab + symidx; 214 215 if (GELF_ST_BIND(sym->st_info) == STB_LOCAL && 216 sym->st_shndx != SHN_UNDEF && sym->st_value != 0) 217 return (sym->st_value); 218 return (0); 219 } 220 221 static int 222 ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn) 223 { 224 GElf_Shdr *shdr; 225 GElf_Dyn *dyn, *dp; 226 size_t i, ndyn, nshdr, nsym; 227 int error; 228 GElf_Off hash_off, sym_off, str_off; 229 GElf_Off rel_off; 230 GElf_Off rela_off; 231 int rel_sz; 232 int rela_sz; 233 int dynamic_idx; 234 235 /* 236 * The kernel linker parses the PT_DYNAMIC segment to find 237 * various important tables. The gelf API of libelf is 238 * section-oriented and requires extracting data from sections 239 * instead of segments (program headers). As a result, 240 * iterate over section headers to read various tables after 241 * parsing values from PT_DYNAMIC. 242 */ 243 error = elf_read_shdrs(ef->ef_efile, &nshdr, &shdr); 244 if (error != 0) 245 return (EFTYPE); 246 dyn = NULL; 247 248 /* Find section for .dynamic. */ 249 dynamic_idx = -1; 250 for (i = 0; i < nshdr; i++) { 251 if (shdr[i].sh_type == SHT_DYNAMIC) { 252 /* 253 * PowerPC kernels contain additional sections 254 * beyond .dynamic in PT_DYNAMIC due to a linker 255 * script bug. Permit a section with a smaller 256 * size as a workaround. 257 */ 258 if (shdr[i].sh_offset != phdyn->p_offset || 259 ((elf_machine(ef->ef_efile) == EM_PPC || 260 elf_machine(ef->ef_efile) == EM_PPC64) ? 261 shdr[i].sh_size > phdyn->p_filesz : 262 shdr[i].sh_size != phdyn->p_filesz)) { 263 warnx(".dynamic section doesn't match phdr"); 264 error = EFTYPE; 265 goto out; 266 } 267 if (dynamic_idx != -1) { 268 warnx("multiple SHT_DYNAMIC sections"); 269 error = EFTYPE; 270 goto out; 271 } 272 dynamic_idx = i; 273 } 274 } 275 276 error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn); 277 if (error != 0) 278 goto out; 279 280 hash_off = rel_off = rela_off = sym_off = str_off = 0; 281 rel_sz = rela_sz = 0; 282 for (i = 0; i < ndyn; i++) { 283 dp = &dyn[i]; 284 if (dp->d_tag == DT_NULL) 285 break; 286 287 switch (dp->d_tag) { 288 case DT_HASH: 289 if (hash_off != 0) 290 warnx("second DT_HASH entry ignored"); 291 else 292 hash_off = ef_get_offset(ef, dp->d_un.d_ptr); 293 break; 294 case DT_STRTAB: 295 if (str_off != 0) 296 warnx("second DT_STRTAB entry ignored"); 297 else 298 str_off = ef_get_offset(ef, dp->d_un.d_ptr); 299 break; 300 case DT_SYMTAB: 301 if (sym_off != 0) 302 warnx("second DT_SYMTAB entry ignored"); 303 else 304 sym_off = ef_get_offset(ef, dp->d_un.d_ptr); 305 break; 306 case DT_SYMENT: 307 if (dp->d_un.d_val != elf_object_size(ef->ef_efile, 308 ELF_T_SYM)) { 309 error = EFTYPE; 310 goto out; 311 } 312 break; 313 case DT_REL: 314 if (rel_off != 0) 315 warnx("second DT_REL entry ignored"); 316 else 317 rel_off = ef_get_offset(ef, dp->d_un.d_ptr); 318 break; 319 case DT_RELSZ: 320 if (rel_sz != 0) 321 warnx("second DT_RELSZ entry ignored"); 322 else 323 rel_sz = dp->d_un.d_val; 324 break; 325 case DT_RELENT: 326 if (dp->d_un.d_val != elf_object_size(ef->ef_efile, 327 ELF_T_REL)) { 328 error = EFTYPE; 329 goto out; 330 } 331 break; 332 case DT_RELA: 333 if (rela_off != 0) 334 warnx("second DT_RELA entry ignored"); 335 else 336 rela_off = ef_get_offset(ef, dp->d_un.d_ptr); 337 break; 338 case DT_RELASZ: 339 if (rela_sz != 0) 340 warnx("second DT_RELSZ entry ignored"); 341 else 342 rela_sz = dp->d_un.d_val; 343 break; 344 case DT_RELAENT: 345 if (dp->d_un.d_val != elf_object_size(ef->ef_efile, 346 ELF_T_RELA)) { 347 error = EFTYPE; 348 goto out; 349 } 350 break; 351 } 352 } 353 if (hash_off == 0) { 354 warnx("%s: no .hash section found\n", ef->ef_name); 355 error = EFTYPE; 356 goto out; 357 } 358 if (sym_off == 0) { 359 warnx("%s: no .dynsym section found\n", ef->ef_name); 360 error = EFTYPE; 361 goto out; 362 } 363 if (str_off == 0) { 364 warnx("%s: no .dynstr section found\n", ef->ef_name); 365 error = EFTYPE; 366 goto out; 367 } 368 369 nsym = 0; 370 for (i = 0; i < nshdr; i++) { 371 switch (shdr[i].sh_type) { 372 case SHT_HASH: 373 if (shdr[i].sh_offset != hash_off) { 374 warnx("%s: ignoring SHT_HASH at different offset from DT_HASH", 375 ef->ef_name); 376 break; 377 } 378 379 /* 380 * libelf(3) mentions ELF_T_HASH, but it is 381 * not defined. 382 */ 383 if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) { 384 warnx("hash section too small"); 385 error = EFTYPE; 386 goto out; 387 } 388 error = elf_read_data(ef->ef_efile, ELF_T_WORD, 389 shdr[i].sh_offset, shdr[i].sh_size, 390 (void **)&ef->ef_hashtab); 391 if (error != 0) { 392 warnc(error, "can't read hash table"); 393 goto out; 394 } 395 ef->ef_nbuckets = ef->ef_hashtab[0]; 396 ef->ef_nchains = ef->ef_hashtab[1]; 397 if ((2 + ef->ef_nbuckets + ef->ef_nchains) * 398 sizeof(*ef->ef_hashtab) != shdr[i].sh_size) { 399 warnx("inconsistent hash section size"); 400 error = EFTYPE; 401 goto out; 402 } 403 404 ef->ef_buckets = ef->ef_hashtab + 2; 405 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets; 406 break; 407 case SHT_DYNSYM: 408 if (shdr[i].sh_offset != sym_off) { 409 warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB", 410 ef->ef_name); 411 break; 412 } 413 error = elf_read_symbols(ef->ef_efile, i, &nsym, 414 &ef->ef_symtab); 415 if (error != 0) { 416 if (ef->ef_verbose) 417 warnx("%s: can't load .dynsym section (0x%jx)", 418 ef->ef_name, (uintmax_t)sym_off); 419 goto out; 420 } 421 break; 422 case SHT_STRTAB: 423 if (shdr[i].sh_offset != str_off) 424 break; 425 error = elf_read_string_table(ef->ef_efile, 426 &shdr[i], &ef->ef_strsz, &ef->ef_strtab); 427 if (error != 0) { 428 warnx("can't load .dynstr section"); 429 error = EIO; 430 goto out; 431 } 432 break; 433 case SHT_REL: 434 if (shdr[i].sh_offset != rel_off) 435 break; 436 if (shdr[i].sh_size != rel_sz) { 437 warnx("%s: size mismatch for DT_REL section", 438 ef->ef_name); 439 error = EFTYPE; 440 goto out; 441 } 442 error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz, 443 &ef->ef_rel); 444 if (error != 0) { 445 warnx("%s: cannot load DT_REL section", 446 ef->ef_name); 447 goto out; 448 } 449 break; 450 case SHT_RELA: 451 if (shdr[i].sh_offset != rela_off) 452 break; 453 if (shdr[i].sh_size != rela_sz) { 454 warnx("%s: size mismatch for DT_RELA section", 455 ef->ef_name); 456 error = EFTYPE; 457 goto out; 458 } 459 error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz, 460 &ef->ef_rela); 461 if (error != 0) { 462 warnx("%s: cannot load DT_RELA section", 463 ef->ef_name); 464 goto out; 465 } 466 break; 467 } 468 } 469 470 if (ef->ef_hashtab == NULL) { 471 warnx("%s: did not find a symbol hash table", ef->ef_name); 472 error = EFTYPE; 473 goto out; 474 } 475 if (ef->ef_symtab == NULL) { 476 warnx("%s: did not find a dynamic symbol table", ef->ef_name); 477 error = EFTYPE; 478 goto out; 479 } 480 if (nsym != ef->ef_nchains) { 481 warnx("%s: symbol count mismatch", ef->ef_name); 482 error = EFTYPE; 483 goto out; 484 } 485 if (ef->ef_strtab == NULL) { 486 warnx("%s: did not find a dynamic string table", ef->ef_name); 487 error = EFTYPE; 488 goto out; 489 } 490 if (rel_off != 0 && ef->ef_rel == NULL) { 491 warnx("%s: did not find a DT_REL relocation table", 492 ef->ef_name); 493 error = EFTYPE; 494 goto out; 495 } 496 if (rela_off != 0 && ef->ef_rela == NULL) { 497 warnx("%s: did not find a DT_RELA relocation table", 498 ef->ef_name); 499 error = EFTYPE; 500 goto out; 501 } 502 503 error = 0; 504 out: 505 free(dyn); 506 free(shdr); 507 return (error); 508 } 509 510 static int 511 ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) 512 { 513 GElf_Off ofs; 514 const GElf_Rela *a; 515 const GElf_Rel *r; 516 int error; 517 518 ofs = ef_get_offset(ef, address); 519 if (ofs == 0) { 520 if (ef->ef_verbose) 521 warnx("ef_seg_read_rel(%s): bad address (%jx)", 522 ef->ef_name, (uintmax_t)address); 523 return (EFAULT); 524 } 525 error = elf_read_raw_data(ef->ef_efile, ofs, dest, len); 526 if (error != 0) 527 return (error); 528 529 for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) { 530 error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address, 531 len, dest); 532 if (error != 0) 533 return (error); 534 } 535 for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) { 536 error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address, 537 len, dest); 538 if (error != 0) 539 return (error); 540 } 541 return (0); 542 } 543 544 static int 545 ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest) 546 { 547 GElf_Off ofs; 548 549 ofs = ef_get_offset(ef, address); 550 if (ofs == 0) { 551 if (ef->ef_verbose) 552 warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)", 553 ef->ef_name, (uintmax_t)address, (uintmax_t)ofs); 554 return (EFAULT); 555 } 556 557 return (elf_read_raw_string(ef->ef_efile, ofs, dest, len)); 558 } 559 560 int 561 ef_open(struct elf_file *efile, int verbose) 562 { 563 elf_file_t ef; 564 GElf_Ehdr *hdr; 565 size_t i, nphdr, nsegs; 566 int error; 567 GElf_Phdr *phdr, *phdyn; 568 569 hdr = &efile->ef_hdr; 570 if (hdr->e_phnum == 0 || 571 hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) || 572 hdr->e_shnum == 0 || hdr->e_shoff == 0 || 573 hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR)) 574 return (EFTYPE); 575 576 ef = malloc(sizeof(*ef)); 577 if (ef == NULL) 578 return (errno); 579 580 efile->ef_ef = ef; 581 efile->ef_ops = &ef_file_ops; 582 583 bzero(ef, sizeof(*ef)); 584 ef->ef_verbose = verbose; 585 ef->ef_name = strdup(efile->ef_filename); 586 ef->ef_efile = efile; 587 588 error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph); 589 if (error != 0) { 590 phdr = NULL; 591 goto out; 592 } 593 594 error = EFTYPE; 595 nsegs = 0; 596 phdyn = NULL; 597 phdr = ef->ef_ph; 598 for (i = 0; i < nphdr; i++, phdr++) { 599 if (verbose > 1) 600 ef_print_phdr(phdr); 601 switch (phdr->p_type) { 602 case PT_LOAD: 603 if (nsegs < MAXSEGS) 604 ef->ef_segs[nsegs] = phdr; 605 nsegs++; 606 break; 607 case PT_PHDR: 608 break; 609 case PT_DYNAMIC: 610 phdyn = phdr; 611 break; 612 } 613 } 614 if (verbose > 1) 615 printf("\n"); 616 if (phdyn == NULL) { 617 warnx("Skipping %s: not dynamically-linked", 618 ef->ef_name); 619 goto out; 620 } 621 622 if (nsegs > MAXSEGS) { 623 warnx("%s: too many segments", ef->ef_name); 624 goto out; 625 } 626 ef->ef_nsegs = nsegs; 627 628 error = ef_parse_dynamic(ef, phdyn); 629 out: 630 if (error != 0) 631 ef_close(ef); 632 return (error); 633 } 634 635 static void 636 ef_close(elf_file_t ef) 637 { 638 free(ef->ef_rela); 639 free(ef->ef_rel); 640 free(ef->ef_strtab); 641 free(ef->ef_symtab); 642 free(ef->ef_hashtab); 643 free(ef->ef_ph); 644 if (ef->ef_name) 645 free(ef->ef_name); 646 ef->ef_efile->ef_ops = NULL; 647 ef->ef_efile->ef_ef = NULL; 648 free(ef); 649 } 650