elf_load.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2006 Sergey Bondari
00003  * Copyright (c) 2006 Jakub Jermar
00004  * Copyright (c) 2011 Jiri Svoboda
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * - Redistributions of source code must retain the above copyright
00012  *   notice, this list of conditions and the following disclaimer.
00013  * - Redistributions in binary form must reproduce the above copyright
00014  *   notice, this list of conditions and the following disclaimer in the
00015  *   documentation and/or other materials provided with the distribution.
00016  * - The name of the author may not be used to endorse or promote products
00017  *   derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00020  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00021  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00022  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00024  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00025  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00026  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00028  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  */
00030 
00046 #include <stdio.h>
00047 #include <sys/types.h>
00048 #include <align.h>
00049 #include <assert.h>
00050 #include <as.h>
00051 #include <unistd.h>
00052 #include <fcntl.h>
00053 #include <smc.h>
00054 #include <loader/pcb.h>
00055 #include <entry_point.h>
00056 
00057 #include "elf.h"
00058 #include "elf_load.h"
00059 
00060 #define DPRINTF(...)
00061 
00062 static const char *error_codes[] = {
00063         "no error",
00064         "invalid image",
00065         "address space error",
00066         "incompatible image",
00067         "unsupported image type",
00068         "irrecoverable error"
00069 };
00070 
00071 static unsigned int elf_load(elf_ld_t *elf, size_t so_bias);
00072 static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry);
00073 static int section_header(elf_ld_t *elf, elf_section_header_t *entry);
00074 static int load_segment(elf_ld_t *elf, elf_segment_header_t *entry);
00075 
00077 static int my_read(int fd, void *buf, size_t len)
00078 {
00079         int cnt = 0;
00080         do {
00081                 buf += cnt;
00082                 len -= cnt;
00083                 cnt = read(fd, buf, len);
00084         } while ((cnt > 0) && ((len - cnt) > 0));
00085 
00086         return cnt;
00087 }
00088 
00105 int elf_load_file(const char *file_name, size_t so_bias, eld_flags_t flags,
00106     elf_info_t *info)
00107 {
00108         elf_ld_t elf;
00109 
00110         int fd;
00111         int rc;
00112         
00113         fd = open(file_name, O_RDONLY);
00114         if (fd < 0) {
00115                 DPRINTF("failed opening file\n");
00116                 return -1;
00117         }
00118 
00119         elf.fd = fd;
00120         elf.info = info;
00121         elf.flags = flags;
00122 
00123         rc = elf_load(&elf, so_bias);
00124 
00125         close(fd);
00126 
00127         return rc;
00128 }
00129 
00138 void elf_create_pcb(elf_info_t *info, pcb_t *pcb)
00139 {
00140         pcb->entry = info->entry;
00141         pcb->dynamic = info->dynamic;
00142         pcb->rtld_runtime = NULL;
00143 }
00144 
00145 
00156 static unsigned int elf_load(elf_ld_t *elf, size_t so_bias)
00157 {
00158         elf_header_t header_buf;
00159         elf_header_t *header = &header_buf;
00160         int i, rc;
00161 
00162         rc = my_read(elf->fd, header, sizeof(elf_header_t));
00163         if (rc < 0) {
00164                 DPRINTF("Read error.\n"); 
00165                 return EE_INVALID;
00166         }
00167 
00168         elf->header = header;
00169 
00170         /* Identify ELF */
00171         if (header->e_ident[EI_MAG0] != ELFMAG0 ||
00172             header->e_ident[EI_MAG1] != ELFMAG1 || 
00173             header->e_ident[EI_MAG2] != ELFMAG2 ||
00174             header->e_ident[EI_MAG3] != ELFMAG3) {
00175                 DPRINTF("Invalid header.\n");
00176                 return EE_INVALID;
00177         }
00178         
00179         /* Identify ELF compatibility */
00180         if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
00181             header->e_machine != ELF_MACHINE || 
00182             header->e_ident[EI_VERSION] != EV_CURRENT ||
00183             header->e_version != EV_CURRENT ||
00184             header->e_ident[EI_CLASS] != ELF_CLASS) {
00185                 DPRINTF("Incompatible data/version/class.\n");
00186                 return EE_INCOMPATIBLE;
00187         }
00188 
00189         if (header->e_phentsize != sizeof(elf_segment_header_t)) {
00190                 DPRINTF("e_phentsize:%d != %d\n", header->e_phentsize,
00191                     sizeof(elf_segment_header_t));
00192                 return EE_INCOMPATIBLE;
00193         }
00194 
00195         if (header->e_shentsize != sizeof(elf_section_header_t)) {
00196                 DPRINTF("e_shentsize:%d != %d\n", header->e_shentsize,
00197                     sizeof(elf_section_header_t));
00198                 return EE_INCOMPATIBLE;
00199         }
00200 
00201         /* Check if the object type is supported. */
00202         if (header->e_type != ET_EXEC && header->e_type != ET_DYN) {
00203                 DPRINTF("Object type %d is not supported\n", header->e_type);
00204                 return EE_UNSUPPORTED;
00205         }
00206 
00207         /* Shared objects can be loaded with a bias */
00208         if (header->e_type == ET_DYN)
00209                 elf->bias = so_bias;
00210         else
00211                 elf->bias = 0;
00212 
00213         elf->info->interp = NULL;
00214         elf->info->dynamic = NULL;
00215 
00216         /* Walk through all segment headers and process them. */
00217         for (i = 0; i < header->e_phnum; i++) {
00218                 elf_segment_header_t segment_hdr;
00219 
00220                 /* Seek to start of segment header */
00221                 lseek(elf->fd, header->e_phoff
00222                         + i * sizeof(elf_segment_header_t), SEEK_SET);
00223 
00224                 rc = my_read(elf->fd, &segment_hdr,
00225                     sizeof(elf_segment_header_t));
00226                 if (rc < 0) {
00227                         DPRINTF("Read error.\n");
00228                         return EE_INVALID;
00229                 }
00230 
00231                 rc = segment_header(elf, &segment_hdr);
00232                 if (rc != EE_OK)
00233                         return rc;
00234         }
00235 
00236         DPRINTF("Parse sections.\n");
00237 
00238         /* Inspect all section headers and proccess them. */
00239         for (i = 0; i < header->e_shnum; i++) {
00240                 elf_section_header_t section_hdr;
00241 
00242                 /* Seek to start of section header */
00243                 lseek(elf->fd, header->e_shoff
00244                     + i * sizeof(elf_section_header_t), SEEK_SET);
00245 
00246                 rc = my_read(elf->fd, &section_hdr,
00247                     sizeof(elf_section_header_t));
00248                 if (rc < 0) {
00249                         DPRINTF("Read error.\n");
00250                         return EE_INVALID;
00251                 }
00252 
00253                 rc = section_header(elf, &section_hdr);
00254                 if (rc != EE_OK)
00255                         return rc;
00256         }
00257 
00258         elf->info->entry =
00259             (entry_point_t)((uint8_t *)header->e_entry + elf->bias);
00260 
00261         DPRINTF("Done.\n");
00262 
00263         return EE_OK;
00264 }
00265 
00272 const char *elf_error(unsigned int rc)
00273 {
00274         assert(rc < sizeof(error_codes) / sizeof(char *));
00275 
00276         return error_codes[rc];
00277 }
00278 
00285 static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry)
00286 {
00287         switch (entry->p_type) {
00288         case PT_NULL:
00289         case PT_PHDR:
00290         case PT_NOTE:
00291                 break;
00292         case PT_LOAD:
00293                 return load_segment(elf, entry);
00294                 break;
00295         case PT_INTERP:
00296                 /* Assume silently interp == "/app/dload" */
00297                 elf->info->interp = "/app/dload";
00298                 break;
00299         case PT_DYNAMIC:
00300                 /* Record pointer to dynamic section into info structure */
00301                 elf->info->dynamic =
00302                     (void *)((uint8_t *)entry->p_vaddr + elf->bias);
00303                 DPRINTF("dynamic section found at 0x%x\n",
00304                         (uintptr_t)elf->info->dynamic);
00305                 break;
00306         case 0x70000000:
00307                 /* FIXME: MIPS reginfo */
00308                 break;
00309         case PT_SHLIB:
00310 //      case PT_LOPROC:
00311 //      case PT_HIPROC:
00312         default:
00313                 DPRINTF("Segment p_type %d unknown.\n", entry->p_type);
00314                 return EE_UNSUPPORTED;
00315                 break;
00316         }
00317         return EE_OK;
00318 }
00319 
00327 int load_segment(elf_ld_t *elf, elf_segment_header_t *entry)
00328 {
00329         void *a;
00330         int flags = 0;
00331         uintptr_t bias;
00332         uintptr_t base;
00333         void *seg_ptr;
00334         uintptr_t seg_addr;
00335         size_t mem_sz;
00336         int rc;
00337 
00338         bias = elf->bias;
00339 
00340         seg_addr = entry->p_vaddr + bias;
00341         seg_ptr = (void *) seg_addr;
00342 
00343         DPRINTF("Load segment at addr %p, size 0x%x\n", (void *) seg_addr,
00344                 entry->p_memsz);
00345 
00346         if (entry->p_align > 1) {
00347                 if ((entry->p_offset % entry->p_align) !=
00348                     (seg_addr % entry->p_align)) {
00349                         DPRINTF("Align check 1 failed offset%%align=%d, "
00350                             "vaddr%%align=%d\n",
00351                             entry->p_offset % entry->p_align,
00352                             seg_addr % entry->p_align
00353                         );
00354                         return EE_INVALID;
00355                 }
00356         }
00357 
00358         /* Final flags that will be set for the memory area */
00359 
00360         if (entry->p_flags & PF_X)
00361                 flags |= AS_AREA_EXEC;
00362         if (entry->p_flags & PF_W)
00363                 flags |= AS_AREA_WRITE;
00364         if (entry->p_flags & PF_R)
00365                 flags |= AS_AREA_READ;
00366         flags |= AS_AREA_CACHEABLE;
00367         
00368         base = ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE);
00369         mem_sz = entry->p_memsz + (entry->p_vaddr - base);
00370 
00371         DPRINTF("Map to seg_addr=%p-%p.\n", (void *) seg_addr,
00372             (void *) (entry->p_vaddr + bias +
00373             ALIGN_UP(entry->p_memsz, PAGE_SIZE)));
00374 
00375         /*
00376          * For the course of loading, the area needs to be readable
00377          * and writeable.
00378          */
00379         a = as_area_create((uint8_t *)base + bias, mem_sz,
00380             AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE);
00381         if (a == (void *)(-1)) {
00382                 DPRINTF("memory mapping failed (0x%x, %d)\n",
00383                         base+bias, mem_sz);
00384                 return EE_MEMORY;
00385         }
00386 
00387         DPRINTF("as_area_create(%p, %#zx, %d) -> %p\n",
00388             (void *) (base + bias), mem_sz, flags, (void *) a);
00389 
00390         /*
00391          * Load segment data
00392          */
00393         rc = lseek(elf->fd, entry->p_offset, SEEK_SET);
00394         if (rc < 0) {
00395                 printf("seek error\n");
00396                 return EE_INVALID;
00397         }
00398 
00399 /*      rc = read(fd, (void *)(entry->p_vaddr + bias), entry->p_filesz);
00400         if (rc < 0) { printf("read error\n"); return EE_INVALID; }*/
00401 
00402         /* Long reads are not possible yet. Load segment piecewise. */
00403 
00404         unsigned left, now;
00405         uint8_t *dp;
00406 
00407         left = entry->p_filesz;
00408         dp = seg_ptr;
00409 
00410         while (left > 0) {
00411                 now = 16384;
00412                 if (now > left) now = left;
00413 
00414                 rc = my_read(elf->fd, dp, now);
00415 
00416                 if (rc < 0) { 
00417                         DPRINTF("Read error.\n");
00418                         return EE_INVALID;
00419                 }
00420 
00421                 left -= now;
00422                 dp += now;
00423         }
00424 
00425         /*
00426          * The caller wants to modify the segments first. He will then
00427          * need to set the right access mode and ensure SMC coherence.
00428          */
00429         if ((elf->flags & ELDF_RW) != 0) return EE_OK;
00430 
00431 //      printf("set area flags to %d\n", flags);
00432         rc = as_area_change_flags(seg_ptr, flags);
00433         if (rc != 0) {
00434                 DPRINTF("Failed to set memory area flags.\n");
00435                 return EE_MEMORY;
00436         }
00437 
00438         if (flags & AS_AREA_EXEC) {
00439                 /* Enforce SMC coherence for the segment */
00440                 if (smc_coherence(seg_ptr, entry->p_filesz))
00441                         return EE_MEMORY;
00442         }
00443 
00444         return EE_OK;
00445 }
00446 
00454 static int section_header(elf_ld_t *elf, elf_section_header_t *entry)
00455 {
00456         switch (entry->sh_type) {
00457         case SHT_PROGBITS:
00458                 if (entry->sh_flags & SHF_TLS) {
00459                         /* .tdata */
00460                 }
00461                 break;
00462         case SHT_NOBITS:
00463                 if (entry->sh_flags & SHF_TLS) {
00464                         /* .tbss */
00465                 }
00466                 break;
00467         case SHT_DYNAMIC:
00468                 /* Record pointer to dynamic section into info structure */
00469                 elf->info->dynamic =
00470                     (void *)((uint8_t *)entry->sh_addr + elf->bias);
00471                 DPRINTF("Dynamic section found at %p.\n",
00472                     (void *) elf->info->dynamic);
00473                 break;
00474         default:
00475                 break;
00476         }
00477         
00478         return EE_OK;
00479 }
00480 

Generated on Thu Jun 2 07:45:50 2011 for HelenOS/USB by  doxygen 1.4.7