load_elf.c revision 57468
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 57468 2000-02-25 05:10:44Z bp $ 28 */ 29 30#include <sys/param.h> 31#include <sys/exec.h> 32#include <sys/reboot.h> 33#include <sys/linker.h> 34#include <string.h> 35#include <machine/bootinfo.h> 36#include <machine/elf.h> 37#include <stand.h> 38#define FREEBSD_ELF 39#include <link.h> 40 41#include "bootstrap.h" 42 43static int elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t loadaddr, Elf_Ehdr *ehdr, int kernel, caddr_t firstpage, int firstlen); 44 45char *elf_kerneltype = "elf kernel"; 46char *elf_moduletype = "elf module"; 47 48/* 49 * Attempt to load the file (file) as an ELF module. It will be stored at 50 * (dest), and a pointer to a module structure describing the loaded object 51 * will be saved in (result). 52 */ 53int 54elf_loadmodule(char *filename, vm_offset_t dest, struct loaded_module **result) 55{ 56 struct loaded_module *mp, *kmp; 57 Elf_Ehdr *ehdr; 58 int fd; 59 int err, kernel; 60 u_int pad; 61 char *s; 62 caddr_t firstpage; 63 int firstlen; 64 65 mp = NULL; 66 67 /* 68 * Open the image, read and validate the ELF header 69 */ 70 if (filename == NULL) /* can't handle nameless */ 71 return(EFTYPE); 72 if ((fd = open(filename, O_RDONLY)) == -1) 73 return(errno); 74 firstpage = malloc(PAGE_SIZE); 75 if (firstpage == NULL) 76 return(ENOMEM); 77 firstlen = read(fd, firstpage, PAGE_SIZE); 78 if (firstlen <= sizeof(ehdr)) { 79 err = EFTYPE; /* could be EIO, but may be small file */ 80 goto oerr; 81 } 82 ehdr = (Elf_Ehdr *)firstpage; 83 84 /* Is it ELF? */ 85 if (!IS_ELF(*ehdr)) { 86 err = EFTYPE; 87 goto oerr; 88 } 89 if (ehdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ 90 ehdr->e_ident[EI_DATA] != ELF_TARG_DATA || 91 ehdr->e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ 92 ehdr->e_version != EV_CURRENT || 93 ehdr->e_machine != ELF_TARG_MACH) { /* Machine ? */ 94 err = EFTYPE; 95 goto oerr; 96 } 97 98 99 /* 100 * Check to see what sort of module we are. 101 */ 102 kmp = mod_findmodule(NULL, NULL); 103 if (ehdr->e_type == ET_DYN) { 104 /* Looks like a kld module */ 105 if (kmp == NULL) { 106 printf("elf_loadmodule: can't load module before kernel\n"); 107 err = EPERM; 108 goto oerr; 109 } 110 if (strcmp(elf_kerneltype, kmp->m_type)) { 111 printf("elf_loadmodule: can't load module with kernel type '%s'\n", kmp->m_type); 112 err = EPERM; 113 goto oerr; 114 } 115 /* Looks OK, got ahead */ 116 kernel = 0; 117 118 /* Page-align the load address */ 119 pad = (u_int)dest & PAGE_MASK; 120 if (pad != 0) { 121 pad = PAGE_SIZE - pad; 122 dest += pad; 123 } 124 } else if (ehdr->e_type == ET_EXEC) { 125 /* Looks like a kernel */ 126 if (kmp != NULL) { 127 printf("elf_loadmodule: kernel already loaded\n"); 128 err = EPERM; 129 goto oerr; 130 } 131 /* 132 * Calculate destination address based on kernel entrypoint 133 */ 134 dest = (vm_offset_t) ehdr->e_entry; 135 if (dest == 0) { 136 printf("elf_loadmodule: not a kernel (maybe static binary?)\n"); 137 err = EPERM; 138 goto oerr; 139 } 140 kernel = 1; 141 142 } else { 143 err = EFTYPE; 144 goto oerr; 145 } 146 147 /* 148 * Ok, we think we should handle this. 149 */ 150 mp = mod_allocmodule(); 151 if (mp == NULL) { 152 printf("elf_loadmodule: cannot allocate module info\n"); 153 err = EPERM; 154 goto out; 155 } 156 if (kernel) 157 setenv("kernelname", filename, 1); 158 s = strrchr(filename, '/'); 159 if (s) 160 mp->m_name = strdup(s + 1); 161 else 162 mp->m_name = strdup(filename); 163 mp->m_type = strdup(kernel ? elf_kerneltype : elf_moduletype); 164 165#ifdef ELF_VERBOSE 166 if (kernel) 167 printf("%s entry at %p\n", filename, (void *) dest); 168#else 169 printf("%s ", filename); 170#endif 171 172 mp->m_size = elf_loadimage(mp, fd, dest, ehdr, kernel, firstpage, firstlen); 173 if (mp->m_size == 0 || mp->m_addr == 0) 174 goto ioerr; 175 176 /* save exec header as metadata */ 177 mod_addmetadata(mp, MODINFOMD_ELFHDR, sizeof(*ehdr), ehdr); 178 179 /* Load OK, return module pointer */ 180 *result = (struct loaded_module *)mp; 181 err = 0; 182 goto out; 183 184 ioerr: 185 err = EIO; 186 oerr: 187 mod_discard(mp); 188 out: 189 if (firstpage) 190 free(firstpage); 191 close(fd); 192 return(err); 193} 194 195/* 196 * With the file (fd) open on the image, and (ehdr) containing 197 * the Elf header, load the image at (off) 198 */ 199static int 200elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t off, 201 Elf_Ehdr *ehdr, int kernel, caddr_t firstpage, int firstlen) 202{ 203 int i, j; 204 Elf_Phdr *phdr; 205 Elf_Shdr *shdr; 206 int ret; 207 vm_offset_t firstaddr; 208 vm_offset_t lastaddr; 209 void *buf; 210 size_t resid, chunk; 211 vm_offset_t dest; 212 vm_offset_t ssym, esym; 213 Elf_Dyn *dp; 214 int ndp; 215 char *s; 216 char *strtab; 217 size_t strsz; 218 int symstrindex; 219 int symtabindex; 220 long size; 221 int fpcopy; 222 223 dp = NULL; 224 shdr = NULL; 225 ret = 0; 226 firstaddr = lastaddr = 0; 227 if (kernel) { 228#ifdef __i386__ 229 off = - (off & 0xff000000u); /* i386 relocates after locore */ 230#else 231 off = 0; /* alpha is direct mapped for kernels */ 232#endif 233 } 234 235 if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(*phdr)) > firstlen) { 236 printf("elf_loadimage: program header not within first page\n"); 237 goto out; 238 } 239 phdr = (Elf_Phdr *)(firstpage + ehdr->e_phoff); 240 241 for (i = 0; i < ehdr->e_phnum; i++) { 242 /* We want to load PT_LOAD segments only.. */ 243 if (phdr[i].p_type != PT_LOAD) 244 continue; 245 246#ifdef ELF_VERBOSE 247 printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", 248 (long)phdr[i].p_filesz, (long)phdr[i].p_offset, 249 (long)(phdr[i].p_vaddr + off), 250 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 251#else 252 if ((phdr[i].p_flags & PF_W) == 0) { 253 printf("text=0x%lx ", (long)phdr[i].p_filesz); 254 } else { 255 printf("data=0x%lx", (long)phdr[i].p_filesz); 256 if (phdr[i].p_filesz < phdr[i].p_memsz) 257 printf("+0x%lx", (long)(phdr[i].p_memsz -phdr[i].p_filesz)); 258 printf(" "); 259 } 260#endif 261 fpcopy = 0; 262 if (firstlen > phdr[i].p_offset) { 263 fpcopy = firstlen - phdr[i].p_offset; 264 archsw.arch_copyin(firstpage + phdr[i].p_offset, 265 phdr[i].p_vaddr + off, fpcopy); 266 } 267 if (phdr[i].p_filesz > fpcopy) { 268 if (lseek(fd, phdr[i].p_offset + fpcopy, SEEK_SET) == -1) { 269 printf("\nelf_loadexec: cannot seek\n"); 270 goto out; 271 } 272 if (archsw.arch_readin(fd, phdr[i].p_vaddr + off + fpcopy, 273 phdr[i].p_filesz - fpcopy) != phdr[i].p_filesz - fpcopy) { 274 printf("\nelf_loadexec: archsw.readin failed\n"); 275 goto out; 276 } 277 } 278 /* clear space from oversized segments; eg: bss */ 279 if (phdr[i].p_filesz < phdr[i].p_memsz) { 280#ifdef ELF_VERBOSE 281 printf(" (bss: 0x%lx-0x%lx)", 282 (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), 283 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 284#endif 285 286 /* no archsw.arch_bzero */ 287 buf = malloc(PAGE_SIZE); 288 bzero(buf, PAGE_SIZE); 289 resid = phdr[i].p_memsz - phdr[i].p_filesz; 290 dest = phdr[i].p_vaddr + off + phdr[i].p_filesz; 291 while (resid > 0) { 292 chunk = min(PAGE_SIZE, resid); 293 archsw.arch_copyin(buf, dest, chunk); 294 resid -= chunk; 295 dest += chunk; 296 } 297 free(buf); 298 } 299#ifdef ELF_VERBOSE 300 printf("\n"); 301#endif 302 303 if (firstaddr == 0 || firstaddr > (phdr[i].p_vaddr + off)) 304 firstaddr = phdr[i].p_vaddr + off; 305 if (lastaddr == 0 || lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) 306 lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; 307 } 308 lastaddr = roundup(lastaddr, sizeof(long)); 309 310 /* 311 * Now grab the symbol tables. This isn't easy if we're reading a 312 * .gz file. I think the rule is going to have to be that you must 313 * strip a file to remove symbols before gzipping it so that we do not 314 * try to lseek() on it. 315 */ 316 chunk = ehdr->e_shnum * ehdr->e_shentsize; 317 if (chunk == 0 || ehdr->e_shoff == 0) 318 goto nosyms; 319 shdr = malloc(chunk); 320 if (shdr == NULL) 321 goto nosyms; 322 if (lseek(fd, ehdr->e_shoff, SEEK_SET) == -1) { 323 printf("\nelf_loadimage: cannot lseek() to section headers"); 324 goto nosyms; 325 } 326 if (read(fd, shdr, chunk) != chunk) { 327 printf("\nelf_loadimage: read section headers failed"); 328 goto nosyms; 329 } 330 symtabindex = -1; 331 symstrindex = -1; 332 for (i = 0; i < ehdr->e_shnum; i++) { 333 if (shdr[i].sh_type != SHT_SYMTAB) 334 continue; 335 for (j = 0; j < ehdr->e_phnum; j++) { 336 if (phdr[j].p_type != PT_LOAD) 337 continue; 338 if (shdr[i].sh_offset >= phdr[j].p_offset && 339 (shdr[i].sh_offset + shdr[i].sh_size <= 340 phdr[j].p_offset + phdr[j].p_filesz)) { 341 shdr[i].sh_offset = 0; 342 shdr[i].sh_size = 0; 343 break; 344 } 345 } 346 if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) 347 continue; /* alread loaded in a PT_LOAD above */ 348 /* Save it for loading below */ 349 symtabindex = i; 350 symstrindex = shdr[i].sh_link; 351 } 352 if (symtabindex < 0 || symstrindex < 0) 353 goto nosyms; 354 355 /* Ok, committed to a load. */ 356#ifndef ELF_VERBOSE 357 printf("syms=["); 358#endif 359 ssym = lastaddr; 360 for (i = symtabindex; i >= 0; i = symstrindex) { 361#ifdef ELF_VERBOSE 362 char *secname; 363 364 switch(shdr[i].sh_type) { 365 case SHT_SYMTAB: /* Symbol table */ 366 secname = "symtab"; 367 break; 368 case SHT_STRTAB: /* String table */ 369 secname = "strtab"; 370 break; 371 default: 372 secname = "WHOA!!"; 373 break; 374 } 375#endif 376 377 size = shdr[i].sh_size; 378 archsw.arch_copyin(&size, lastaddr, sizeof(size)); 379 lastaddr += sizeof(long); 380 381#ifdef ELF_VERBOSE 382 printf("\n%s: 0x%lx@0x%lx -> 0x%lx-0x%lx", secname, 383 shdr[i].sh_size, shdr[i].sh_offset, 384 lastaddr, lastaddr + shdr[i].sh_size); 385#else 386 if (i == symstrindex) 387 printf("+"); 388 printf("0x%lx+0x%lx", (long)sizeof(size), size); 389#endif 390 391 if (lseek(fd, shdr[i].sh_offset, SEEK_SET) == -1) { 392 printf("\nelf_loadimage: could not seek for symbols - skipped!"); 393 lastaddr = ssym; 394 ssym = 0; 395 goto nosyms; 396 } 397 if (archsw.arch_readin(fd, lastaddr, shdr[i].sh_size) != 398 shdr[i].sh_size) { 399 printf("\nelf_loadimage: could not read symbols - skipped!"); 400 lastaddr = ssym; 401 ssym = 0; 402 goto nosyms; 403 } 404 /* Reset offsets relative to ssym */ 405 lastaddr += shdr[i].sh_size; 406 lastaddr = roundup(lastaddr, sizeof(long)); 407 if (i == symtabindex) 408 symtabindex = -1; 409 else if (i == symstrindex) 410 symstrindex = -1; 411 } 412 esym = lastaddr; 413#ifndef ELF_VERBOSE 414 printf("]"); 415#endif 416 417 mod_addmetadata(mp, MODINFOMD_SSYM, sizeof(ssym), &ssym); 418 mod_addmetadata(mp, MODINFOMD_ESYM, sizeof(esym), &esym); 419 420nosyms: 421 printf("\n"); 422 423 ret = lastaddr - firstaddr; 424 mp->m_addr = firstaddr; 425 426 for (i = 0; i < ehdr->e_phnum; i++) { 427 if (phdr[i].p_type == PT_DYNAMIC) { 428 dp = (Elf_Dyn *)(phdr[i].p_vaddr); 429 mod_addmetadata(mp, MODINFOMD_DYNAMIC, sizeof(dp), &dp); 430 dp = NULL; 431 break; 432 } 433 } 434 435 if (kernel) /* kernel must not depend on anything */ 436 goto out; 437 438 ndp = 0; 439 for (i = 0; i < ehdr->e_phnum; i++) { 440 if (phdr[i].p_type == PT_DYNAMIC) { 441 ndp = phdr[i].p_filesz / sizeof(Elf_Dyn); 442 dp = malloc(phdr[i].p_filesz); 443 archsw.arch_copyout(phdr[i].p_vaddr + off, dp, phdr[i].p_filesz); 444 } 445 } 446 if (dp == NULL || ndp == 0) 447 goto out; 448 strtab = NULL; 449 strsz = 0; 450 for (i = 0; i < ndp; i++) { 451 if (dp[i].d_tag == NULL) 452 break; 453 switch (dp[i].d_tag) { 454 case DT_STRTAB: 455 strtab = (char *)(dp[i].d_un.d_ptr + off); 456 break; 457 case DT_STRSZ: 458 strsz = dp[i].d_un.d_val; 459 break; 460 default: 461 break; 462 } 463 } 464 if (strtab == NULL || strsz == 0) 465 goto out; 466 467 for (i = 0; i < ndp; i++) { 468 if (dp[i].d_tag == NULL) 469 break; 470 if (dp[i].d_tag != DT_NEEDED) 471 continue; 472 j = dp[i].d_un.d_ptr; 473 if (j < 1 || j > (strsz - 2)) 474 continue; /* bad symbol name index */ 475 s = strdupout((vm_offset_t)&strtab[j]); 476 mod_addmetadata(mp, MODINFOMD_DEPLIST, strlen(s) + 1, s); 477 free(s); 478 } 479 480out: 481 if (dp) 482 free(dp); 483 if (shdr) 484 free(shdr); 485 return ret; 486} 487