load_elf.c revision 39887
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 * $Id: load_elf.c,v 1.1 1998/09/30 19:38:26 peter Exp $ 28 */ 29 30#include <sys/param.h> 31#include <sys/exec.h> 32#include <sys/reboot.h> 33#include <string.h> 34#include <machine/bootinfo.h> 35#include <machine/elf.h> 36#include <stand.h> 37#define FREEBSD_ELF 38#include <link.h> 39 40#include "bootstrap.h" 41 42static int elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t *loadaddr, Elf_Ehdr *ehdr, int kernel); 43#if 0 44static vm_offset_t elf_findkldident(struct loaded_module *mp, Elf_Ehdr *ehdr); 45static int elf_fixupkldmod(struct loaded_module *mp, Elf_Ehdr *ehdr); 46#endif 47 48char *elf_kerneltype = "elf kernel"; 49char *elf_moduletype = "elf module"; 50 51/* 52 * Attempt to load the file (file) as an ELF module. It will be stored at 53 * (dest), and a pointer to a module structure describing the loaded object 54 * will be saved in (result). 55 */ 56int 57elf_loadmodule(char *filename, vm_offset_t dest, struct loaded_module **result) 58{ 59 struct loaded_module *mp, *kmp; 60 Elf_Ehdr ehdr; 61 int fd; 62 vm_offset_t addr; 63 int err, kernel; 64 u_int pad; 65 66 mp = NULL; 67 68 /* 69 * Open the image, read and validate the ELF header 70 */ 71 if (filename == NULL) /* can't handle nameless */ 72 return(EFTYPE); 73 if ((fd = open(filename, O_RDONLY)) == -1) 74 return(errno); 75 if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) { 76 err = EFTYPE; /* could be EIO, but may be small file */ 77 goto oerr; 78 } 79 80 /* Is it ELF? */ 81 if (!IS_ELF(ehdr)) { 82 err = EFTYPE; 83 goto oerr; 84 } 85 if (ehdr.e_ident[EI_CLASS] != ELF_TARG_CLASS || /* Layout ? */ 86 ehdr.e_ident[EI_DATA] != ELF_TARG_DATA || 87 ehdr.e_ident[EI_VERSION] != EV_CURRENT || /* Version ? */ 88 ehdr.e_version != EV_CURRENT || 89 ehdr.e_machine != ELF_TARG_MACH) { /* Machine ? */ 90 err = EFTYPE; 91 goto oerr; 92 } 93 94 95 /* 96 * Check to see what sort of module we are. 97 */ 98 kmp = mod_findmodule(NULL, NULL); 99 if (ehdr.e_type == ET_DYN) { 100 /* Looks like a kld module */ 101 if (kmp == NULL) { 102 printf("elf_loadmodule: can't load module before kernel\n"); 103 err = EPERM; 104 goto oerr; 105 } 106 if (strcmp(elf_kerneltype, kmp->m_type)) { 107 printf("elf_loadmodule: can't load module with kernel type '%s'\n", kmp->m_type); 108 err = EPERM; 109 goto oerr; 110 } 111 /* Looks OK, got ahead */ 112 kernel = 0; 113 114 /* Page-align the load address */ 115 addr = dest; 116 pad = (u_int)addr & PAGE_MASK; 117 if (pad != 0) { 118 pad = PAGE_SIZE - pad; 119 addr += pad; 120 } 121 } else if (ehdr.e_type == ET_EXEC) { 122 /* Looks like a kernel */ 123 if (kmp != NULL) { 124 printf("elf_loadmodule: kernel already loaded\n"); 125 err = EPERM; 126 goto oerr; 127 } 128 /* 129 * Calculate destination address based on kernel entrypoint 130 */ 131 dest = (vm_offset_t) ehdr.e_entry; 132 if (dest == 0) { 133 printf("elf_loadmodule: not a kernel (maybe static binary?)\n"); 134 err = EPERM; 135 goto oerr; 136 } 137 kernel = 1; 138 139 addr = dest; 140 } else { 141 err = EFTYPE; 142 goto oerr; 143 } 144 145 /* 146 * Ok, we think we should handle this. 147 */ 148 mp = mod_allocmodule(); 149 if (kernel) 150 mp->m_name = strdup(filename); /* XXX should we prune the name? */ 151 mp->m_type = strdup(kernel ? elf_kerneltype : elf_moduletype); 152 153 printf("%s at %p\n", filename, (void *) addr); 154 155 mp->m_size = elf_loadimage(mp, fd, &addr, &ehdr, kernel); 156 if (mp->m_size == 0) 157 goto ioerr; 158 mp->m_addr = addr; /* save the aligned load address */ 159 160#if 0 161 /* Handle KLD module data */ 162 if (!kernel && ((err = elf_fixupkldmod(mp, &ehdr)) != 0)) 163 goto oerr; 164#endif 165 166 /* save exec header as metadata */ 167 mod_addmetadata(mp, MODINFOMD_ELFHDR, sizeof(ehdr), &ehdr); 168 169 /* Load OK, return module pointer */ 170 *result = (struct loaded_module *)mp; 171 err = 0; 172 goto out; 173 174 ioerr: 175 err = EIO; 176 oerr: 177 mod_discard(mp); 178 out: 179 close(fd); 180 return(err); 181} 182 183/* 184 * With the file (fd) open on the image, and (ehdr) containing 185 * the Elf header, load the image at (addr) 186 */ 187static int 188elf_loadimage(struct loaded_module *mp, int fd, vm_offset_t *addr, 189 Elf_Ehdr *ehdr, int kernel) 190{ 191 int i, j; 192 Elf_Phdr *phdr; 193 Elf_Shdr *shdr; 194 Elf_Ehdr local_ehdr; 195 int ret; 196 vm_offset_t firstaddr; 197 vm_offset_t lastaddr; 198 vm_offset_t off; 199 void *buf; 200 size_t resid, chunk; 201 vm_offset_t dest; 202 char *secname; 203 vm_offset_t shdrpos; 204 vm_offset_t ssym, esym; 205 206 ret = 0; 207 firstaddr = lastaddr = 0; 208 if (kernel) 209#ifdef __i386__ 210 off = 0x10000000; /* -0xf0000000 - i386 relocates after locore */ 211#else 212 off = 0; /* alpha is direct mapped for kernels */ 213#endif 214 else 215 off = *addr; /* load relative to passed address */ 216 217 phdr = malloc(ehdr->e_phnum * sizeof(*phdr)); 218 if (phdr == NULL) 219 goto out; 220 221 if (lseek(fd, ehdr->e_phoff, SEEK_SET) == -1) { 222 printf("elf_loadexec: lseek for phdr failed\n"); 223 goto out; 224 } 225 if (read(fd, phdr, ehdr->e_phnum * sizeof(*phdr)) != 226 ehdr->e_phnum * sizeof(*phdr)) { 227 printf("elf_loadexec: cannot read program header\n"); 228 goto out; 229 } 230 231 for (i = 0; i < ehdr->e_phnum; i++) { 232 /* We want to load PT_LOAD segments only.. */ 233 if (phdr[i].p_type != PT_LOAD) 234 continue; 235 236 printf("Segment: 0x%lx@0x%lx -> 0x%lx-0x%lx", 237 (long)phdr[i].p_filesz, (long)phdr[i].p_offset, 238 (long)(phdr[i].p_vaddr + off), 239 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 240 241 if (lseek(fd, phdr[i].p_offset, SEEK_SET) == -1) { 242 printf("\nelf_loadexec: cannot seek\n"); 243 goto out; 244 } 245 if (archsw.arch_readin(fd, phdr[i].p_vaddr + off, phdr[i].p_filesz) != 246 phdr[i].p_filesz) { 247 printf("\nelf_loadexec: archsw.readin failed\n"); 248 goto out; 249 } 250 /* clear space from oversized segments; eg: bss */ 251 if (phdr[i].p_filesz < phdr[i].p_memsz) { 252 printf(" (bss: 0x%lx-0x%lx)", 253 (long)(phdr[i].p_vaddr + off + phdr[i].p_filesz), 254 (long)(phdr[i].p_vaddr + off + phdr[i].p_memsz - 1)); 255 256 /* no archsw.arch_bzero */ 257 buf = malloc(PAGE_SIZE); 258 bzero(buf, PAGE_SIZE); 259 resid = phdr[i].p_memsz - phdr[i].p_filesz; 260 dest = phdr[i].p_vaddr + off + phdr[i].p_filesz; 261 while (resid > 0) { 262 chunk = min(PAGE_SIZE, resid); 263 archsw.arch_copyin(buf, dest, chunk); 264 resid -= chunk; 265 dest += chunk; 266 } 267 free(buf); 268 } 269 printf("\n"); 270 271 if (firstaddr < (phdr[i].p_vaddr + off)) 272 firstaddr = phdr[i].p_vaddr + off; 273 if (lastaddr < (phdr[i].p_vaddr + off + phdr[i].p_memsz)) 274 lastaddr = phdr[i].p_vaddr + off + phdr[i].p_memsz; 275 } 276 277 /* 278 * Now grab the symbol tables. This isn't easy if we're reading a 279 * .gz file. I think the rule is going to have to be that you must 280 * strip a file to remove symbols before gzipping it so that we do not 281 * try to lseek() on it. The layout is a bit wierd, but it's what 282 * the NetBSD-derived ddb/db_elf.c wants. 283 */ 284 lastaddr = roundup(lastaddr, sizeof(long)); 285 chunk = ehdr->e_shnum * ehdr->e_shentsize; 286 shdr = malloc(chunk); 287 if (shdr == NULL) 288 goto nosyms; 289 ssym = lastaddr; 290 printf("Symbols: ELF Ehdr @ 0x%x; ", lastaddr); 291 lastaddr += sizeof(*ehdr); 292 lastaddr = roundup(lastaddr, sizeof(long)); 293 /* Copy out executable header modified for base offsets */ 294 local_ehdr = *ehdr; 295 local_ehdr.e_phoff = 0; 296 local_ehdr.e_phentsize = 0; 297 local_ehdr.e_phnum = 0; 298 local_ehdr.e_shoff = lastaddr - ssym; 299 archsw.arch_copyin(&local_ehdr, ssym, sizeof(*ehdr)); 300 if (lseek(fd, ehdr->e_shoff, SEEK_SET) == -1) { 301 printf("elf_loadimage: cannot lseek() to section headers\n"); 302 lastaddr = ssym; /* wind back */ 303 ssym = 0; 304 goto nosyms; 305 } 306 if (read(fd, shdr, chunk) != chunk) { 307 printf("elf_loadimage: read section headers failed\n"); 308 lastaddr = ssym; /* wind back */ 309 ssym = 0; 310 goto nosyms; 311 } 312 shdrpos = lastaddr; 313 printf("Section table: 0x%x@0x%x\n", chunk, shdrpos); 314 lastaddr += chunk; 315 lastaddr = roundup(lastaddr, sizeof(long)); 316 for (i = 0; i < ehdr->e_shnum; i++) { 317 switch(shdr[i].sh_type) { 318 /* 319 * These are the symbol tables. Their names are relative to 320 * an arbitary string table. 321 */ 322 case SHT_SYMTAB: /* Symbol table */ 323 secname = "symtab"; 324 break; 325 case SHT_DYNSYM: /* Dynamic linking symbol table */ 326 secname = "dynsym"; 327 break; 328 /* 329 * And here are the string tables. These can be referred to from 330 * a number of sources, including the dynsym, the section table 331 * names itself, etc. 332 */ 333 case SHT_STRTAB: /* String table */ 334 secname = "strtab"; 335 break; 336 default: /* Skip it */ 337 continue; 338 } 339 for (j = 0; j < ehdr->e_phnum; j++) { 340 if (phdr[j].p_type != PT_LOAD) 341 continue; 342 if (shdr[i].sh_offset >= phdr[j].p_offset && 343 (shdr[i].sh_offset + shdr[i].sh_size <= 344 phdr[j].p_offset + phdr[j].p_filesz)) { 345 shdr[i].sh_offset = 0; 346 shdr[i].sh_size = 0; 347 break; 348 } 349 } 350 if (shdr[i].sh_offset == 0 || shdr[i].sh_size == 0) 351 continue; /* alread loaded in a PT_LOAD above */ 352 353 printf("%s: 0x%x@0x%x -> 0x%x-0x%x\n", secname, 354 shdr[i].sh_size, shdr[i].sh_offset, 355 lastaddr, lastaddr + shdr[i].sh_size); 356 357 if (lseek(fd, shdr[i].sh_offset, SEEK_SET) == -1) { 358 printf("\nelf_loadimage: could not seek for symbols - skipped!\n"); 359 shdr[i].sh_offset = 0; 360 shdr[i].sh_size = 0; 361 continue; 362 } 363 if (archsw.arch_readin(fd, lastaddr, shdr[i].sh_size) != 364 shdr[i].sh_size) { 365 printf("\nelf_loadimage: could not read symbols - skipped!\n"); 366 shdr[i].sh_offset = 0; 367 shdr[i].sh_size = 0; 368 continue; 369 } 370 /* Reset offsets relative to ssym */ 371 shdr[i].sh_offset = lastaddr - ssym; 372 lastaddr += shdr[i].sh_size; 373 lastaddr = roundup(lastaddr, sizeof(long)); 374 } 375 archsw.arch_copyin(shdr, lastaddr, sizeof(*ehdr)); 376 esym = lastaddr; 377 378 mod_addmetadata(mp, MODINFOMD_ELFSSYM, sizeof(ssym), &ssym); 379 mod_addmetadata(mp, MODINFOMD_ELFESYM, sizeof(esym), &esym); 380 381nosyms: 382 383 ret = lastaddr - firstaddr; 384 *addr = firstaddr; 385out: 386 if (phdr) 387 free(phdr); 388 return ret; 389} 390