ef_obj.c revision 134373
1/* 2 * Copyright (c) 2000, Boris Popov 3 * Copyright (c) 1998-2000 Doug Rabson 4 * Copyright (c) 2004 Peter Wemm 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: head/usr.sbin/kldxref/ef_obj.c 134373 2004-08-27 08:54:40Z iedowse $ 35 */ 36 37#include <sys/param.h> 38#include <sys/linker.h> 39#include <string.h> 40#include <stdio.h> 41#include <stdlib.h> 42#include <unistd.h> 43#include <errno.h> 44#include <fcntl.h> 45#include <machine/elf.h> 46#define FREEBSD_ELF 47#include <link.h> 48 49#include <err.h> 50 51#include "ef.h" 52 53typedef struct { 54 void *addr; 55 Elf_Off size; 56 int flags; 57 int sec; /* Original section */ 58 char *name; 59} Elf_progent; 60 61typedef struct { 62 Elf_Rel *rel; 63 int nrel; 64 int sec; 65} Elf_relent; 66 67typedef struct { 68 Elf_Rela *rela; 69 int nrela; 70 int sec; 71} Elf_relaent; 72 73struct ef_file { 74 char *ef_name; 75 int ef_fd; 76 Elf_Ehdr ef_hdr; 77 struct elf_file *ef_efile; 78 79 caddr_t address; 80 Elf_Off size; 81 Elf_Shdr *e_shdr; 82 83 Elf_progent *progtab; 84 int nprogtab; 85 86 Elf_relaent *relatab; 87 int nrela; 88 89 Elf_relent *reltab; 90 int nrel; 91 92 Elf_Sym *ddbsymtab; /* The symbol table we are using */ 93 long ddbsymcnt; /* Number of symbols */ 94 caddr_t ddbstrtab; /* String table */ 95 long ddbstrcnt; /* number of bytes in string table */ 96 97 caddr_t shstrtab; /* Section name string table */ 98 long shstrcnt; /* number of bytes in string table */ 99 100 int ef_verbose; 101}; 102 103static int ef_obj_get_type(elf_file_t ef); 104static int ef_obj_close(elf_file_t ef); 105static int ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest); 106static int ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, 107 void **ptr); 108static int ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, 109 void *dest); 110static int ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, 111 void *dest); 112static int ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, 113 void **ptr); 114static int ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, 115 void **ptr); 116static Elf_Addr ef_obj_symaddr(elf_file_t ef, Elf_Word symidx); 117static int ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, 118 long *stopp, long *countp); 119static int ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym); 120 121static struct elf_file_ops ef_obj_file_ops = { 122 ef_obj_get_type, 123 ef_obj_close, 124 ef_obj_read, 125 ef_obj_read_entry, 126 ef_obj_seg_read, 127 ef_obj_seg_read_rel, 128 ef_obj_seg_read_entry, 129 ef_obj_seg_read_entry_rel, 130 ef_obj_symaddr, 131 ef_obj_lookup_set, 132 ef_obj_lookup_symbol 133}; 134 135static int 136ef_obj_get_type(elf_file_t ef) 137{ 138 139 return (EFT_KLD); 140} 141 142static int 143ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym) 144{ 145 Elf_Sym *symp; 146 const char *strp; 147 int i; 148 149 for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { 150 strp = ef->ddbstrtab + symp->st_name; 151 if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) { 152 *sym = symp; 153 return 0; 154 } 155 } 156 return ENOENT; 157} 158 159static int 160ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp, 161 long *countp) 162{ 163 int i; 164 165 for (i = 0; i < ef->nprogtab; i++) { 166 if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) && 167 strcmp(ef->progtab[i].name + 4, name) == 0) { 168 *startp = (char *)ef->progtab[i].addr - ef->address; 169 *stopp = (char *)ef->progtab[i].addr + 170 ef->progtab[i].size - ef->address; 171 *countp = (*stopp - *startp) / sizeof(void *); 172 return (0); 173 } 174 } 175 return (ESRCH); 176} 177 178static Elf_Addr 179ef_obj_symaddr(elf_file_t ef, Elf_Word symidx) 180{ 181 const Elf_Sym *sym; 182 183 if (symidx >= ef->ddbsymcnt) 184 return (0); 185 sym = ef->ddbsymtab + symidx; 186 187 if (sym->st_shndx != SHN_UNDEF) 188 return (sym->st_value - (Elf_Addr)ef->address); 189 return (0); 190} 191 192static int 193ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) 194{ 195 ssize_t r; 196 197 if (offset != (Elf_Off)-1) { 198 if (lseek(ef->ef_fd, offset, SEEK_SET) == -1) 199 return EIO; 200 } 201 202 r = read(ef->ef_fd, dest, len); 203 if (r != -1 && (size_t)r == len) 204 return 0; 205 else 206 return EIO; 207} 208 209static int 210ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) 211{ 212 int error; 213 214 *ptr = malloc(len); 215 if (*ptr == NULL) 216 return ENOMEM; 217 error = ef_obj_read(ef, offset, len, *ptr); 218 if (error) 219 free(*ptr); 220 return error; 221} 222 223static int 224ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest) 225{ 226 227 if (offset + len > ef->size) { 228 if (ef->ef_verbose) 229 warnx("ef_seg_read_rel(%s): bad offset/len (%lx:%ld)", 230 ef->ef_name, (long)offset, (long)len); 231 return (EFAULT); 232 } 233 bcopy(ef->address + offset, dest, len); 234 return (0); 235} 236 237static int 238ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest) 239{ 240 char *memaddr; 241 Elf_Rel *r; 242 Elf_Rela *a; 243 Elf_Off secoff; 244 int error, i, sec; 245 246 if (offset + len > ef->size) { 247 if (ef->ef_verbose) 248 warnx("ef_seg_read_rel(%s): bad offset/len (%lx:%ld)", 249 ef->ef_name, (long)offset, (long)len); 250 return (EFAULT); 251 } 252 bcopy(ef->address + offset, dest, len); 253 254 /* Find out which section contains the data. */ 255 memaddr = ef->address + offset; 256 sec = -1; 257 for (i = 0; i < ef->nprogtab; i++) { 258 if (ef->progtab[i].addr == NULL) 259 continue; 260 if (memaddr < (char *)ef->progtab[i].addr || memaddr + len > 261 (char *)ef->progtab[i].addr + ef->progtab[i].size) 262 continue; 263 sec = ef->progtab[i].sec; 264 secoff = memaddr - (char *)ef->progtab[i].addr; 265 break; 266 } 267 268 if (sec == -1) 269 return (EFAULT); 270 271 /* Now do the relocations. */ 272 for (i = 0; i < ef->nrel; i++) { 273 if (ef->reltab[i].sec != sec) 274 continue; 275 for (r = ef->reltab[i].rel; 276 r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) { 277 error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, secoff, 278 len, dest); 279 if (error != 0) 280 return (error); 281 } 282 } 283 for (i = 0; i < ef->nrela; i++) { 284 if (ef->relatab[i].sec != sec) 285 continue; 286 for (a = ef->relatab[i].rela; 287 a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) { 288 error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, secoff, 289 len, dest); 290 if (error != 0) 291 return (error); 292 } 293 } 294 return (0); 295} 296 297static int 298ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr) 299{ 300 int error; 301 302 *ptr = malloc(len); 303 if (*ptr == NULL) 304 return ENOMEM; 305 error = ef_obj_seg_read(ef, offset, len, *ptr); 306 if (error) 307 free(*ptr); 308 return error; 309} 310 311static int 312ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, 313 void **ptr) 314{ 315 int error; 316 317 *ptr = malloc(len); 318 if (*ptr == NULL) 319 return ENOMEM; 320 error = ef_obj_seg_read_rel(ef, offset, len, *ptr); 321 if (error) 322 free(*ptr); 323 return error; 324} 325 326int 327ef_obj_open(const char *filename, struct elf_file *efile, int verbose) 328{ 329 elf_file_t ef; 330 Elf_Ehdr *hdr; 331 Elf_Shdr *shdr; 332 Elf_Sym *es; 333 char *mapbase; 334 void *vtmp; 335 size_t mapsize; 336 int alignmask, error, fd, pb, ra, res, rl; 337 int i, j, nbytes, nsym, shstrindex, symstrindex, symtabindex; 338 339 if (filename == NULL) 340 return EFTYPE; 341 if ((fd = open(filename, O_RDONLY)) == -1) 342 return errno; 343 344 ef = malloc(sizeof(*ef)); 345 if (ef == NULL) { 346 close(fd); 347 return (ENOMEM); 348 } 349 350 efile->ef_ef = ef; 351 efile->ef_ops = &ef_obj_file_ops; 352 353 bzero(ef, sizeof(*ef)); 354 ef->ef_verbose = verbose; 355 ef->ef_fd = fd; 356 ef->ef_name = strdup(filename); 357 ef->ef_efile = efile; 358 hdr = (Elf_Ehdr *)&ef->ef_hdr; 359 360 res = read(fd, hdr, sizeof(*hdr)); 361 error = EFTYPE; 362 if (res != sizeof(*hdr)) 363 goto out; 364 if (!IS_ELF(*hdr)) 365 goto out; 366 if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || 367 hdr->e_ident[EI_DATA] != ELF_TARG_DATA || 368 hdr->e_ident[EI_VERSION] != EV_CURRENT || 369 hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH || 370 hdr->e_type != ET_REL) 371 goto out; 372 373 nbytes = hdr->e_shnum * hdr->e_shentsize; 374 if (nbytes == 0 || hdr->e_shoff == 0 || 375 hdr->e_shentsize != sizeof(Elf_Shdr)) 376 goto out; 377 378 if (ef_obj_read_entry(ef, hdr->e_shoff, nbytes, &vtmp) != 0) { 379 printf("ef_read_entry failed\n"); 380 goto out; 381 } 382 ef->e_shdr = shdr = vtmp; 383 384 /* Scan the section header for information and table sizing. */ 385 nsym = 0; 386 symtabindex = -1; 387 symstrindex = -1; 388 for (i = 0; i < hdr->e_shnum; i++) { 389 switch (shdr[i].sh_type) { 390 case SHT_PROGBITS: 391 case SHT_NOBITS: 392 ef->nprogtab++; 393 break; 394 case SHT_SYMTAB: 395 nsym++; 396 symtabindex = i; 397 symstrindex = shdr[i].sh_link; 398 break; 399 case SHT_REL: 400 ef->nrel++; 401 break; 402 case SHT_RELA: 403 ef->nrela++; 404 break; 405 case SHT_STRTAB: 406 break; 407 } 408 } 409 410 if (ef->nprogtab == 0) { 411 warnx("%s: file has no contents", filename); 412 goto out; 413 } 414 if (nsym != 1) { 415 warnx("%s: file has no valid symbol table", filename); 416 goto out; 417 } 418 if (symstrindex < 0 || symstrindex > hdr->e_shnum || 419 shdr[symstrindex].sh_type != SHT_STRTAB) { 420 warnx("%s: file has invalid symbol strings", filename); 421 goto out; 422 } 423 424 /* Allocate space for tracking the load chunks */ 425 if (ef->nprogtab != 0) 426 ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab)); 427 if (ef->nrel != 0) 428 ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab)); 429 if (ef->nrela != 0) 430 ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab)); 431 if ((ef->nprogtab != 0 && ef->progtab == NULL) || 432 (ef->nrel != 0 && ef->reltab == NULL) || 433 (ef->nrela != 0 && ef->relatab == NULL)) { 434 printf("malloc failed\n"); 435 error = ENOMEM; 436 goto out; 437 } 438 439 ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym); 440 if (ef_obj_read_entry(ef, shdr[symtabindex].sh_offset, 441 shdr[symtabindex].sh_size, (void**)&ef->ddbsymtab) != 0) { 442 printf("ef_read_entry failed\n"); 443 goto out; 444 } 445 446 ef->ddbstrcnt = shdr[symstrindex].sh_size; 447 if (ef_obj_read_entry(ef, shdr[symstrindex].sh_offset, 448 shdr[symstrindex].sh_size, (void**)&ef->ddbstrtab) != 0) { 449 printf("ef_read_entry failed\n"); 450 goto out; 451 } 452 453 /* Do we have a string table for the section names? */ 454 shstrindex = -1; 455 if (hdr->e_shstrndx != 0 && 456 shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) { 457 shstrindex = hdr->e_shstrndx; 458 ef->shstrcnt = shdr[shstrindex].sh_size; 459 if (ef_obj_read_entry(ef, shdr[shstrindex].sh_offset, 460 shdr[shstrindex].sh_size, (void**)&ef->shstrtab) != 0) { 461 printf("ef_read_entry failed\n"); 462 goto out; 463 } 464 } 465 466 /* Size up code/data(progbits) and bss(nobits). */ 467 alignmask = 0; 468 mapsize = 0; 469 for (i = 0; i < hdr->e_shnum; i++) { 470 switch (shdr[i].sh_type) { 471 case SHT_PROGBITS: 472 case SHT_NOBITS: 473 alignmask = shdr[i].sh_addralign - 1; 474 mapsize += alignmask; 475 mapsize &= ~alignmask; 476 mapsize += shdr[i].sh_size; 477 break; 478 } 479 } 480 481 /* We know how much space we need for the text/data/bss/etc. */ 482 ef->address = malloc(mapsize); 483 ef->size = mapsize; 484 if (ef->address == NULL) { 485 printf("malloc failed\n"); 486 goto out; 487 } 488 mapbase = ef->address; 489 490 /* 491 * Now load code/data(progbits), zero bss(nobits), allocate 492 * space for and load relocs 493 */ 494 pb = 0; 495 rl = 0; 496 ra = 0; 497 alignmask = 0; 498 for (i = 0; i < hdr->e_shnum; i++) { 499 switch (shdr[i].sh_type) { 500 case SHT_PROGBITS: 501 case SHT_NOBITS: 502 alignmask = shdr[i].sh_addralign - 1; 503 mapbase += alignmask; 504 mapbase = (char *)((uintptr_t)mapbase & ~alignmask); 505 ef->progtab[pb].addr = (void *)(uintptr_t)mapbase; 506 if (shdr[i].sh_type == SHT_PROGBITS) { 507 ef->progtab[pb].name = "<<PROGBITS>>"; 508 if (ef_obj_read(ef, shdr[i].sh_offset, 509 shdr[i].sh_size, 510 ef->progtab[pb].addr) != 0) { 511 printf("failed to read progbits\n"); 512 goto out; 513 } 514 } else { 515 ef->progtab[pb].name = "<<NOBITS>>"; 516 bzero(ef->progtab[pb].addr, shdr[i].sh_size); 517 } 518 ef->progtab[pb].size = shdr[i].sh_size; 519 ef->progtab[pb].sec = i; 520 if (ef->shstrtab && shdr[i].sh_name != 0) 521 ef->progtab[pb].name = 522 ef->shstrtab + shdr[i].sh_name; 523 524 /* Update all symbol values with the offset. */ 525 for (j = 0; j < ef->ddbsymcnt; j++) { 526 es = &ef->ddbsymtab[j]; 527 if (es->st_shndx != i) 528 continue; 529 es->st_value += (Elf_Addr)ef->progtab[pb].addr; 530 } 531 mapbase += shdr[i].sh_size; 532 pb++; 533 break; 534 case SHT_REL: 535 ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel); 536 ef->reltab[rl].sec = shdr[i].sh_info; 537 if (ef_obj_read_entry(ef, shdr[i].sh_offset, 538 shdr[i].sh_size, (void**)&ef->reltab[rl].rel) != 539 0) { 540 printf("ef_read_entry failed\n"); 541 goto out; 542 } 543 rl++; 544 break; 545 case SHT_RELA: 546 ef->relatab[ra].nrela = 547 shdr[i].sh_size / sizeof(Elf_Rela); 548 ef->relatab[ra].sec = shdr[i].sh_info; 549 if (ef_obj_read_entry(ef, shdr[i].sh_offset, 550 shdr[i].sh_size, (void**)&ef->relatab[ra].rela) != 551 0) { 552 printf("ef_read_entry failed\n"); 553 goto out; 554 } 555 ra++; 556 break; 557 } 558 } 559 error = 0; 560out: 561 if (error) 562 ef_obj_close(ef); 563 return error; 564} 565 566static int 567ef_obj_close(elf_file_t ef) 568{ 569 int i; 570 571 close(ef->ef_fd); 572 if (ef->ef_name) 573 free(ef->ef_name); 574 if (ef->e_shdr != NULL) 575 free(ef->e_shdr); 576 if (ef->size != 0) 577 free(ef->address); 578 if (ef->nprogtab != 0) 579 free(ef->progtab); 580 if (ef->nrel != 0) { 581 for (i = 0; i < ef->nrel; i++) 582 if (ef->reltab[i].rel != NULL) 583 free(ef->reltab[i].rel); 584 free(ef->reltab); 585 } 586 if (ef->nrela != 0) { 587 for (i = 0; i < ef->nrela; i++) 588 if (ef->relatab[i].rela != NULL) 589 free(ef->relatab[i].rela); 590 free(ef->relatab); 591 } 592 if (ef->ddbsymtab != NULL) 593 free(ef->ddbsymtab); 594 if (ef->ddbstrtab != NULL) 595 free(ef->ddbstrtab); 596 if (ef->shstrtab != NULL) 597 free(ef->shstrtab); 598 ef->ef_efile->ef_ops = NULL; 599 ef->ef_efile->ef_ef = NULL; 600 free(ef); 601 602 return 0; 603} 604