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