load_elf.c revision 64187
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * Copyright (c) 1998 Peter Wemm <peter@freebsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/boot/common/load_elf.c 64187 2000-08-03 09:14:02Z jhb $ 28 */ 29 30#include <sys/param.h> 31#include <sys/exec.h> 32#include <sys/reboot.h> 33#include <sys/linker.h> 34#include <sys/module.h> 35#include <string.h> 36#include <machine/bootinfo.h> 37#include <machine/elf.h> 38#include <stand.h> 39#define FREEBSD_ELF 40#include <link.h> 41 42#include "bootstrap.h" 43 44#define COPYOUT(s,d,l) archsw.arch_copyout((vm_offset_t)(s), d, l) 45 46 47typedef struct elf_file { 48 Elf_Phdr *ph; 49 Elf_Ehdr *ehdr; 50 Elf_Sym *symtab; 51 Elf_Off *hashtab; 52 Elf_Off nbuckets; 53 Elf_Off nchains; 54 Elf_Off* buckets; 55 Elf_Off* chains; 56 char *strtab; 57 size_t strsz; 58 int fd; 59 caddr_t firstpage; 60 size_t firstlen; 61 int kernel; 62 vm_offset_t off; 63} *elf_file_t; 64 65static int elf_loadimage(struct preloaded_file *mp, elf_file_t ef, vm_offset_t loadaddr); 66static int elf_lookup_symbol(struct preloaded_file *mp, elf_file_t ef, const char* name, Elf_Sym* sym); 67static int elf_parse_modmetadata(struct preloaded_file *mp, elf_file_t ef); 68static char *fake_modname(const char *name); 69 70const char *elf_kerneltype = "elf kernel"; 71const char *elf_moduletype = "elf module"; 72 73/* 74 * Attempt to load the file (file) as an ELF module. It will be stored at 75 * (dest), and a pointer to a module structure describing the loaded object 76 * will be saved in (result). 77 */ 78int 79elf_loadfile(char *filename, vm_offset_t dest, struct preloaded_file **result) 80{ 81 struct preloaded_file *fp, *kfp; 82 struct elf_file ef; 83 Elf_Ehdr *ehdr; 84 int err; 85 u_int pad; 86 char *s; 87 ssize_t bytes_read; 88 89 fp = NULL; 90 bzero(&ef, sizeof(struct elf_file)); 91 92 /* 93 * Open the image, read and validate the ELF header 94 */ 95 if (filename == NULL) /* can't handle nameless */ 96 return(EFTYPE); 97 if ((ef.fd = open(filename, O_RDONLY)) == -1) 98 return(errno); 99 ef.firstpage = malloc(PAGE_SIZE); 100 if (ef.firstpage == NULL) { 101 close(ef.fd); 102 return(ENOMEM); 103 } 104 bytes_read = read(ef.fd, ef.firstpage, PAGE_SIZE); 105 ef.firstlen = (size_t)bytes_read; 106 if (bytes_read < 0 || ef.firstlen <= sizeof(Elf_Ehdr)) { 107 err = EFTYPE; /* could be EIO, but may be small file */ 108 goto oerr; 109 } 110 ehdr = ef.ehdr = (Elf_Ehdr *)ef.firstpage; 111 112 /* Is it ELF? */ 113 if (!IS_ELF(*ehdr)) { 114 err = EFTYPE; 115 goto oerr; 116 } 117 if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ 118 ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || 119 ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ 120 ehdr->e_version != EV_CURRENT || 121 ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ 122 err = EFTYPE; 123 goto oerr; 124 } 125 126 127 /* 128 * Check to see what sort of module we are. 129 */ 130 kfp = file_findfile(NULL, NULL); 131 if (ehdr->e_type == ET_DYN) { 132 /* Looks like a kld module */ 133 if (kfp == NULL) { 134 printf("elf_loadfile: can't load module before kernel\n"); 135 err = EPERM; 136 goto oerr; 137 } 138 if (strcmp(elf_kerneltype, kfp->f_type)) { 139 printf("elf_loadfile: can't load module with kernel type '%s'\n", kfp->f_type); 140 err = EPERM; 141 goto oerr; 142 } 143 /* Looks OK, got ahead */ 144 ef.kernel = 0; 145 146 /* Page-align the load address */ 147 pad = (u_int)dest & PAGE_MASK; 148 if (pad != 0) { 149 pad = PAGE_SIZE - pad; 150 dest += pad; 151 } 152 } else if (ehdr->e_type == ET_EXEC) { 153 /* Looks like a kernel */ 154 if (kfp != NULL) { 155 printf("elf_loadfile: kernel already loaded\n"); 156 err = EPERM; 157 goto oerr; 158 } 159 /* 160 * Calculate destination address based on kernel entrypoint 161 */ 162 dest = (vm_offset_t) ehdr->e_entry; 163 if (dest == 0) { 164 printf("elf_loadfile: not a kernel (maybe static binary?)\n"); 165 err = EPERM; 166 goto oerr; 167 } 168 ef.kernel = 1; 169 170 } else { 171 err = EFTYPE; 172 goto oerr; 173 } 174 175 /* 176 * Ok, we think we should handle this. 177 */ 178 fp = file_alloc(); 179 if (fp == NULL) { 180 printf("elf_loadfile: cannot allocate module info\n"); 181 err = EPERM; 182 goto out; 183 } 184 if (ef.kernel) 185 setenv("kernelname", filename, 1); 186 s = strrchr(filename, '/'); 187 if (s) 188 fp->f_name = strdup(s + 1); 189 else 190 fp->f_name = strdup(filename); 191 fp->f_type = strdup(ef.kernel ? elf_kerneltype : elf_moduletype); 192 193#ifdef ELF_VERBOSE 194 if (ef.kernel) 195 printf("%s entry at %p\n", filename, (void *) dest); 196#else 197 printf("%s ", filename); 198#endif 199 200 fp->f_size = elf_loadimage(fp, &ef, dest); 201 if (fp->f_size == 0 || fp->f_addr == 0) 202 goto ioerr; 203 204 /* save exec header as metadata */ 205 file_addmetadata(fp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); 206 207 /* Load OK, return module pointer */ 208 *result = (struct preloaded_file *)fp; 209 err = 0; 210 goto out; 211 212 ioerr: 213 err = EIO; 214 oerr: 215 file_discard(fp); 216 out: 217 if (ef.firstpage) 218 free(ef.firstpage); 219 close(ef.fd); 220 return(err); 221} 222 223/* 224 * With the file (fd) open on the image, and (ehdr) containing 225 * the Elf header, load the image at (off) 226 */ 227static int 228elf_loadimage(struct preloaded_file *fp, elf_file_t ef, vm_offset_t off) 229{ 230 int i; 231 u_int j; 232 Elf_Ehdr *ehdr; 233 Elf_Phdr *phdr, *php; 234 Elf_Shdr *shdr; 235 int ret; 236 vm_offset_t firstaddr; 237 vm_offset_t lastaddr; 238 void *buf; 239 size_t resid, chunk; 240 ssize_t result; 241 vm_offset_t dest; 242 vm_offset_t ssym, esym; 243 Elf_Dyn *dp; 244 int ndp; 245 char *s; 246 int symstrindex; 247 int symtabindex; 248 long size; 249 u_int fpcopy; 250 251 dp = NULL; 252 shdr = NULL; 253 ret = 0; 254 firstaddr = lastaddr = 0; 255 ehdr = ef->ehdr; 256 if (ef->kernel) { 257#ifdef __i386__ 258 off = - (off & 0xff000000u); /* i386 relocates after locore */ 259#else 260 off = 0; /* alpha is direct mapped for kernels */ 261#endif 262 } 263 ef->off = off; 264 265 if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > ef->firstlen) { 266 printf("elf_loadimage: program header not within first page\n"); 267 goto out; 268 } 269 phdr = (Elf_Phdr *)(ef->firstpage + ehdr->e_phoff); 270 271 for (i = 0; i < ehdr->e_phnum; i++) { 272 /* We want to load PT_LOAD segments only.. */ 273 if (phdr[i].p_type != PT_LOAD) 274 continue; 275 276#ifdef ELF_VERBOSE 277 printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", 278 (long)phdr[i].p_filesz, (long)phdr[i].p_offset, 279 (long)(phdr[i].p_vaddr + off), 280 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 281#else 282 if ((phdr[i].p_flags & PF_W) == 0) { 283 printf("text=0x%lx ", (long)phdr[i].p_filesz); 284 } else { 285 printf("data=0x%lx", (long)phdr[i].p_filesz); 286 if (phdr[i].p_filesz < phdr[i].p_memsz) 287 printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz)); 288 printf(" "); 289 } 290#endif 291 fpcopy = 0; 292 if (ef->firstlen > phdr[i].p_offset) { 293 fpcopy = ef->firstlen - phdr[i].p_offset; 294 archsw.arch_copyin(ef->firstpage + phdr[i].p_offset, 295 phdr[i].p_vaddr + off, fpcopy); 296 } 297 if (phdr[i].p_filesz > fpcopy) { 298 if (lseek(ef->fd, (off_t)(phdr[i].p_offset + fpcopy), 299 SEEK_SET) == -1) { 300 printf("\nelf_loadexec: cannot seek\n"); 301 goto out; 302 } 303 if (archsw.arch_readin(ef->fd, phdr[i].p_vaddr + off + fpcopy, 304 phdr[i].p_filesz - fpcopy) != (ssize_t)(phdr[i].p_filesz - fpcopy)) { 305 printf("\nelf_loadexec: archsw.readin failed\n"); 306 goto out; 307 } 308 } 309 /* clear space from oversized segments; eg: bss */ 310 if (phdr[i].p_filesz < phdr[i].p_memsz) { 311#ifdef ELF_VERBOSE 312 printf(" (bss: 0x%lx-0x%lx)", 313 (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), 314 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 315#endif 316 317 /* no archsw.arch_bzero */ 318 buf = malloc(PAGE_SIZE); 319 if (buf == NULL) { 320 printf("\nelf_loadimage: malloc() failed\n"); 321 goto out; 322 } 323 bzero(buf, PAGE_SIZE); 324 resid = phdr[i].p_memsz - phdr[i].p_filesz; 325 dest = phdr[i].p_vaddr + off + phdr[i].p_filesz; 326 while (resid > 0) { 327 chunk = min(PAGE_SIZE, resid); 328 archsw.arch_copyin(buf, dest, chunk); 329 resid -= chunk; 330 dest += chunk; 331 } 332 free(buf); 333 } 334#ifdef ELF_VERBOSE 335 printf("\n"); 336#endif 337 338 if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) 339 firstaddr = phdr[i].p_vaddr + off; 340 if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) 341 lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; 342 } 343 lastaddr = roundup(lastaddr, sizeof(long)); 344 345 /* 346 * Now grab the symbol tables. This isn't easy if we're reading a 347 * .gz file. I think the rule is going to have to be that you must 348 * strip a file to remove symbols before gzipping it so that we do not 349 * try to lseek() on it. 350 */ 351 chunk = ehdr->e_shnum * ehdr->e_shentsize; 352 if (chunk == 0 || ehdr->e_shoff == 0) 353 goto nosyms; 354 shdr = malloc(chunk); 355 if (shdr == NULL) 356 goto nosyms; 357 if (lseek(ef->fd, (off_t)ehdr->e_shoff, SEEK_SET) == -1) { 358 printf("\nelf_loadimage: cannot lseek() to section headers"); 359 goto nosyms; 360 } 361 result = read(ef->fd, shdr, chunk); 362 if (result < 0 || (size_t)result != chunk) { 363 printf("\nelf_loadimage: read section headers failed"); 364 goto nosyms; 365 } 366 symtabindex = -1; 367 symstrindex = -1; 368 for (i = 0; i < ehdr->e_shnum; i++) { 369 if (shdr[i].sh_type != SHT_SYMTAB) 370 continue; 371 for (j = 0; j < ehdr->e_phnum; j++) { 372 if (phdr[j].p_type != PT_LOAD) 373 continue; 374 if (shdr[i].sh_offset >= phdr[j].p_offset && 375 (shdr[i].sh_offset + shdr[i].sh_size <= 376 phdr[j].p_offset + phdr[j].p_filesz)) { 377 shdr[i].sh_offset = 0; 378 shdr[i].sh_size = 0; 379 break; 380 } 381 } 382 if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) 383 continue; /* alread loaded in a PT_LOAD above */ 384 /* Save it for loading below */ 385 symtabindex = i; 386 symstrindex = shdr[i].sh_link; 387 } 388 if (symtabindex < 0 || symstrindex < 0) 389 goto nosyms; 390 391 /* Ok, committed to a load. */ 392#ifndef ELF_VERBOSE 393 printf("syms=["); 394#endif 395 ssym = lastaddr; 396 for (i = symtabindex; i >= 0; i = symstrindex) { 397#ifdef ELF_VERBOSE 398 char *secname; 399 400 switch(shdr[i].sh_type) { 401 case SHT_SYMTAB: /* Symbol table */ 402 secname = "symtab"; 403 break; 404 case SHT_STRTAB: /* String table */ 405 secname = "strtab"; 406 break; 407 default: 408 secname = "WHOA!!"; 409 break; 410 } 411#endif 412 413 size = shdr[i].sh_size; 414 archsw.arch_copyin(&size, lastaddr, sizeof(size)); 415 lastaddr += sizeof(long); 416 417#ifdef ELF_VERBOSE 418 printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname, 419 shdr[i].sh_size, shdr[i].sh_offset, 420 lastaddr, lastaddr + shdr[i].sh_size); 421#else 422 if (i == symstrindex) 423 printf("+"); 424 printf("0x%lx+0x%lx", (long)sizeof(size), size); 425#endif 426 427 if (lseek(ef->fd, (off_t)shdr[i].sh_offset, SEEK_SET) == -1) { 428 printf("\nelf_loadimage: could not seek for symbols - skipped!"); 429 lastaddr = ssym; 430 ssym = 0; 431 goto nosyms; 432 } 433 result = archsw.arch_readin(ef->fd, lastaddr, shdr[i].sh_size); 434 if (result < 0 || (size_t)result != shdr[i].sh_size) { 435 printf("\nelf_loadimage: could not read symbols - skipped!"); 436 lastaddr = ssym; 437 ssym = 0; 438 goto nosyms; 439 } 440 /* Reset offsets relative to ssym */ 441 lastaddr += shdr[i].sh_size; 442 lastaddr = roundup(lastaddr, sizeof(long)); 443 if (i == symtabindex) 444 symtabindex = -1; 445 else if (i == symstrindex) 446 symstrindex = -1; 447 } 448 esym = lastaddr; 449#ifndef ELF_VERBOSE 450 printf("]"); 451#endif 452 453 file_addmetadata(fp, MODINFOMD_SSYM, sizeof(ssym), &ssym); 454 file_addmetadata(fp, MODINFOMD_ESYM, sizeof(esym), &esym); 455 456nosyms: 457 printf("\n"); 458 459 ret = lastaddr - firstaddr; 460 fp->f_addr = firstaddr; 461 462 php = NULL; 463 for (i = 0; i < ehdr->e_phnum; i++) { 464 if (phdr[i].p_type == PT_DYNAMIC) { 465 php = phdr + i; 466 dp = (Elf_Dyn *)(php->p_vaddr); 467 file_addmetadata(fp, MODINFOMD_DYNAMIC, sizeof(dp), &dp); 468 dp = NULL; 469 break; 470 } 471 } 472 473 if (php == NULL) /* this is bad, we cannot get to symbols or _DYNAMIC */ 474 goto out; 475 476 ndp = php->p_filesz / sizeof(Elf_Dyn); 477 if (ndp == 0) 478 goto out; 479 dp = malloc(php->p_filesz); 480 if (dp == NULL) 481 goto out; 482 archsw.arch_copyout(php->p_vaddr + off, dp, php->p_filesz); 483 484 ef->strsz = 0; 485 for (i = 0; i < ndp; i++) { 486 if (dp[i].d_tag == NULL) 487 break; 488 switch (dp[i].d_tag) { 489 case DT_HASH: 490 ef->hashtab = (Elf_Off*)(dp[i].d_un.d_ptr + off); 491 break; 492 case DT_STRTAB: 493 ef->strtab = (char *)(dp[i].d_un.d_ptr + off); 494 break; 495 case DT_STRSZ: 496 ef->strsz = dp[i].d_un.d_val; 497 break; 498 case DT_SYMTAB: 499 ef->symtab = (Elf_Sym*)(dp[i].d_un.d_ptr + off); 500 break; 501 default: 502 break; 503 } 504 } 505 if (ef->hashtab == NULL || ef->symtab == NULL || 506 ef->strtab == NULL || ef->strsz == 0) 507 goto out; 508 COPYOUT(ef->hashtab, &ef->nbuckets, sizeof(ef->nbuckets)); 509 COPYOUT(ef->hashtab + 1, &ef->nchains, sizeof(ef->nchains)); 510 ef->buckets = ef->hashtab + 2; 511 ef->chains = ef->buckets + ef->nbuckets; 512 if (elf_parse_modmetadata(fp, ef) == 0) 513 goto out; 514 515 if (ef->kernel) /* kernel must not depend on anything */ 516 goto out; 517 518 for (i = 0; i < ndp; i++) { 519 if (dp[i].d_tag == NULL) 520 break; 521 if (dp[i].d_tag != DT_NEEDED) 522 continue; 523 j = dp[i].d_un.d_ptr; 524 if (j < 1 || j > ef->strsz - 2) 525 continue; 526 s = strdupout((vm_offset_t)&ef->strtab[j]); 527 file_addmetadata(fp, MODINFOMD_DEPLIST, strlen(s) + 1, s); 528 free(s); 529 } 530 531out: 532 if (dp) 533 free(dp); 534 if (shdr) 535 free(shdr); 536 return ret; 537} 538 539static char invalid_name[] = "bad"; 540char * 541fake_modname(const char *name) { 542 char *sp, *ep; 543 size_t len; 544 545 sp = strrchr(name, '/'); 546 if (sp) 547 sp++; 548 else 549 sp = name; 550 ep = strrchr(name, '.'); 551 if (ep) { 552 if (ep == name) { 553 sp = invalid_name; 554 ep = invalid_name + sizeof(invalid_name) - 1; 555 } 556 } else 557 ep = name + strlen(name); 558 len = ep - sp; 559 ep = malloc(len + 1); 560 if (ep == NULL) 561 return NULL; 562 memcpy(ep, sp, len); 563 ep[len] = '\0'; 564 return ep; 565} 566 567int 568elf_parse_modmetadata(struct preloaded_file *fp, elf_file_t ef) { 569 struct mod_metadata md; 570 Elf_Sym sym; 571 char *s, *v, **p; 572 long entries; 573 int modcnt; 574 575 if (elf_lookup_symbol(fp, ef, "modmetadata_set", &sym) != 0) 576 return ENOENT; 577 COPYOUT(sym.st_value + ef->off, &entries, sizeof(entries)); 578 579 modcnt = 0; 580 p = (char **)(sym.st_value + ef->off + sizeof(entries)); 581 while (entries--) { 582 COPYOUT(p++, &v, sizeof(v)); 583 COPYOUT(v + ef->off, &md, sizeof(md)); 584 switch(md.md_type) { 585 case MDT_DEPEND: 586 if (ef->kernel) /* kernel must not depend on anything */ 587 break; 588 s = strdupout((vm_offset_t)(md.md_cval + ef->off)); 589 file_addmetadata(fp, MODINFOMD_DEPLIST, strlen(s) + 1, s); 590 free(s); 591 break; 592 case MDT_VERSION: 593 s = strdupout((vm_offset_t)(md.md_cval + ef->off)); 594 file_addmodule(fp, s, NULL); 595 free(s); 596 modcnt++; 597 break; 598 } 599 } 600 if (modcnt == 0) { 601 s = fake_modname(fp->f_name); 602 file_addmodule(fp, s, NULL); 603 free(s); 604 } 605 return 0; 606} 607 608static unsigned long 609elf_hash(const char *name) 610{ 611 const unsigned char *p = (const unsigned char *) name; 612 unsigned long h = 0; 613 unsigned long g; 614 615 while (*p != '\0') { 616 h = (h << 4) + *p++; 617 if ((g = h & 0xf0000000) != 0) 618 h ^= g >> 24; 619 h &= ~g; 620 } 621 return h; 622} 623 624static const char elf_bad_symtable[] = "elf_lookup_symbol: corrupt symbol table\n"; 625int 626elf_lookup_symbol(struct preloaded_file *fp, elf_file_t ef, const char* name, 627 Elf_Sym *symp) 628{ 629 unsigned long symnum; 630 Elf_Sym sym; 631 char *strp; 632 unsigned long hash; 633 634 hash = elf_hash(name); 635 COPYOUT(&ef->buckets[hash % ef->nbuckets], &symnum, sizeof(symnum)); 636 637 while (symnum != STN_UNDEF) { 638 if (symnum >= ef->nchains) { 639 printf(elf_bad_symtable); 640 return ENOENT; 641 } 642 643 COPYOUT(ef->symtab + symnum, &sym, sizeof(sym)); 644 if (sym.st_name == 0) { 645 printf(elf_bad_symtable); 646 return ENOENT; 647 } 648 649 strp = strdupout((vm_offset_t)(ef->strtab + sym.st_name)); 650 if (strcmp(name, strp) == 0) { 651 free(strp); 652 if (sym.st_shndx != SHN_UNDEF || 653 (sym.st_value != 0 && 654 ELF_ST_TYPE(sym.st_info) == STT_FUNC)) { 655 *symp = sym; 656 return 0; 657 } 658 return ENOENT; 659 } 660 free(strp); 661 COPYOUT(&ef->chains[symnum], &symnum, sizeof(symnum)); 662 } 663 return ENOENT; 664} 665