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