Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/elf/elf_load.c

    rfaba839 rdc0d8b52  
    11/*
    2  * Copyright (c) 2006 Sergey Bondari
    3  * Copyright (c) 2006 Jakub Jermar
    4  * Copyright (c) 2011 Jiri Svoboda
     2 * Copyright (c) 2016 Jiri Svoboda
    53 * All rights reserved.
    64 *
     
    3634 * @file
    3735 * @brief       Userspace ELF loader.
    38  *
    39  * This module allows loading ELF binaries (both executables and
    40  * shared objects) from VFS. The current implementation allocates
    41  * anonymous memory, fills it with segment data and then adjusts
    42  * the memory areas' flags to the final value. In the future,
    43  * the segments will be mapped directly from the file.
    4436 */
    4537
     38#include <elf/elf_load.h>
     39#include <elf/elf_mod.h>
     40#include <errno.h>
    4641#include <stdio.h>
    47 #include <sys/types.h>
    48 #include <align.h>
    49 #include <assert.h>
    50 #include <as.h>
    51 #include <elf/elf.h>
    52 #include <unistd.h>
    53 #include <fcntl.h>
    54 #include <smc.h>
    55 #include <loader/pcb.h>
    56 #include <entry_point.h>
     42#include <stdlib.h>
    5743
    58 #include <elf/elf_load.h>
     44#ifdef CONFIG_RTLD
     45#include <rtld/rtld.h>
     46#endif
    5947
    6048#define DPRINTF(...)
    6149
    62 static const char *error_codes[] = {
    63         "no error",
    64         "invalid image",
    65         "address space error",
    66         "incompatible image",
    67         "unsupported image type",
    68         "irrecoverable error"
    69 };
     50/** Load ELF program.
     51 *
     52 * @param file_name File name
     53 * @param info Place to store ELF program information
     54 * @return EOK on success or non-zero error code
     55 */
     56int elf_load(const char *file_name, elf_info_t *info)
     57{
     58#ifdef CONFIG_RTLD
     59        rtld_t *env;
     60#endif
     61        int rc;
    7062
    71 static unsigned int elf_load(elf_ld_t *elf, size_t so_bias);
    72 static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry);
    73 static int section_header(elf_ld_t *elf, elf_section_header_t *entry);
    74 static int load_segment(elf_ld_t *elf, elf_segment_header_t *entry);
    75 
    76 /** Load ELF binary from a file.
    77  *
    78  * Load an ELF binary from the specified file. If the file is
    79  * an executable program, it is loaded unbiased. If it is a shared
    80  * object, it is loaded with the bias @a so_bias. Some information
    81  * extracted from the binary is stored in a elf_info_t structure
    82  * pointed to by @a info.
    83  *
    84  * @param file_name Path to the ELF file.
    85  * @param so_bias   Bias to use if the file is a shared object.
    86  * @param info      Pointer to a structure for storing information
    87  *                  extracted from the binary.
    88  *
    89  * @return EOK on success or negative error code.
    90  *
    91  */
    92 int elf_load_file(const char *file_name, size_t so_bias, eld_flags_t flags,
    93     elf_info_t *info)
    94 {
    95         elf_ld_t elf;
    96 
    97         int fd;
    98         int rc;
    99        
    100         fd = open(file_name, O_RDONLY);
    101         if (fd < 0) {
    102                 DPRINTF("failed opening file\n");
    103                 return -1;
     63        rc = elf_load_file(file_name, 0, 0, &info->finfo);
     64        if (rc != EE_OK) {
     65                DPRINTF("Failed to load executable '%s'.\n", file_name);
     66                return rc;
    10467        }
    10568
    106         elf.fd = fd;
    107         elf.info = info;
    108         elf.flags = flags;
     69        if (info->finfo.interp == NULL) {
     70                /* Statically linked program */
     71                DPRINTF("Binary is statically linked.\n");
     72                info->env = NULL;
     73                return EE_OK;
     74        }
    10975
    110         rc = elf_load(&elf, so_bias);
     76        DPRINTF("Binary is dynamically linked.\n");
     77#ifdef CONFIG_RTLD
     78        DPRINTF( "- prog dynamic: %p\n", info->finfo.dynamic);
    11179
    112         close(fd);
    113 
     80        rc = rtld_prog_process(&info->finfo, &env);
     81        info->env = env;
     82#else
     83        rc = EE_UNSUPPORTED;
     84#endif
    11485        return rc;
    11586}
    11687
    117 /** Create the program control block (PCB).
     88/** Set ELF-related PCB entries.
    11889 *
    11990 * Fills the program control block @a pcb with information from
     
    12192 *
    12293 * @param info  Program info structure
    123  * @return EOK on success or negative error code
     94 * @param pcb PCB
    12495 */
    125 void elf_create_pcb(elf_info_t *info, pcb_t *pcb)
     96void elf_set_pcb(elf_info_t *info, pcb_t *pcb)
    12697{
    127         pcb->entry = info->entry;
    128         pcb->dynamic = info->dynamic;
    129         pcb->rtld_runtime = NULL;
    130 }
    131 
    132 
    133 /** Load an ELF binary.
    134  *
    135  * The @a elf structure contains the loader state, including
    136  * an open file, from which the binary will be loaded,
    137  * a pointer to the @c info structure etc.
    138  *
    139  * @param elf           Pointer to loader state buffer.
    140  * @param so_bias       Bias to use if the file is a shared object.
    141  * @return EE_OK on success or EE_xx error code.
    142  */
    143 static unsigned int elf_load(elf_ld_t *elf, size_t so_bias)
    144 {
    145         elf_header_t header_buf;
    146         elf_header_t *header = &header_buf;
    147         int i, rc;
    148 
    149         rc = read_all(elf->fd, header, sizeof(elf_header_t));
    150         if (rc != sizeof(elf_header_t)) {
    151                 DPRINTF("Read error.\n");
    152                 return EE_INVALID;
    153         }
    154 
    155         elf->header = header;
    156 
    157         /* Identify ELF */
    158         if (header->e_ident[EI_MAG0] != ELFMAG0 ||
    159             header->e_ident[EI_MAG1] != ELFMAG1 ||
    160             header->e_ident[EI_MAG2] != ELFMAG2 ||
    161             header->e_ident[EI_MAG3] != ELFMAG3) {
    162                 DPRINTF("Invalid header.\n");
    163                 return EE_INVALID;
    164         }
    165        
    166         /* Identify ELF compatibility */
    167         if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
    168             header->e_machine != ELF_MACHINE ||
    169             header->e_ident[EI_VERSION] != EV_CURRENT ||
    170             header->e_version != EV_CURRENT ||
    171             header->e_ident[EI_CLASS] != ELF_CLASS) {
    172                 DPRINTF("Incompatible data/version/class.\n");
    173                 return EE_INCOMPATIBLE;
    174         }
    175 
    176         if (header->e_phentsize != sizeof(elf_segment_header_t)) {
    177                 DPRINTF("e_phentsize:%d != %d\n", header->e_phentsize,
    178                     sizeof(elf_segment_header_t));
    179                 return EE_INCOMPATIBLE;
    180         }
    181 
    182         if (header->e_shentsize != sizeof(elf_section_header_t)) {
    183                 DPRINTF("e_shentsize:%d != %d\n", header->e_shentsize,
    184                     sizeof(elf_section_header_t));
    185                 return EE_INCOMPATIBLE;
    186         }
    187 
    188         /* Check if the object type is supported. */
    189         if (header->e_type != ET_EXEC && header->e_type != ET_DYN) {
    190                 DPRINTF("Object type %d is not supported\n", header->e_type);
    191                 return EE_UNSUPPORTED;
    192         }
    193 
    194         /* Shared objects can be loaded with a bias */
    195         if (header->e_type == ET_DYN)
    196                 elf->bias = so_bias;
    197         else
    198                 elf->bias = 0;
    199 
    200         elf->info->interp = NULL;
    201         elf->info->dynamic = NULL;
    202 
    203         /* Walk through all segment headers and process them. */
    204         for (i = 0; i < header->e_phnum; i++) {
    205                 elf_segment_header_t segment_hdr;
    206 
    207                 /* Seek to start of segment header */
    208                 lseek(elf->fd, header->e_phoff
    209                         + i * sizeof(elf_segment_header_t), SEEK_SET);
    210 
    211                 rc = read_all(elf->fd, &segment_hdr,
    212                     sizeof(elf_segment_header_t));
    213                 if (rc != sizeof(elf_segment_header_t)) {
    214                         DPRINTF("Read error.\n");
    215                         return EE_INVALID;
    216                 }
    217 
    218                 rc = segment_header(elf, &segment_hdr);
    219                 if (rc != EE_OK)
    220                         return rc;
    221         }
    222 
    223         DPRINTF("Parse sections.\n");
    224 
    225         /* Inspect all section headers and proccess them. */
    226         for (i = 0; i < header->e_shnum; i++) {
    227                 elf_section_header_t section_hdr;
    228 
    229                 /* Seek to start of section header */
    230                 lseek(elf->fd, header->e_shoff
    231                     + i * sizeof(elf_section_header_t), SEEK_SET);
    232 
    233                 rc = read_all(elf->fd, &section_hdr,
    234                     sizeof(elf_section_header_t));
    235                 if (rc != sizeof(elf_section_header_t)) {
    236                         DPRINTF("Read error.\n");
    237                         return EE_INVALID;
    238                 }
    239 
    240                 rc = section_header(elf, &section_hdr);
    241                 if (rc != EE_OK)
    242                         return rc;
    243         }
    244 
    245         elf->info->entry =
    246             (entry_point_t)((uint8_t *)header->e_entry + elf->bias);
    247 
    248         DPRINTF("Done.\n");
    249 
    250         return EE_OK;
    251 }
    252 
    253 /** Print error message according to error code.
    254  *
    255  * @param rc Return code returned by elf_load().
    256  *
    257  * @return NULL terminated description of error.
    258  */
    259 const char *elf_error(unsigned int rc)
    260 {
    261         assert(rc < sizeof(error_codes) / sizeof(char *));
    262 
    263         return error_codes[rc];
    264 }
    265 
    266 /** Process segment header.
    267  *
    268  * @param entry Segment header.
    269  *
    270  * @return EE_OK on success, error code otherwise.
    271  */
    272 static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry)
    273 {
    274         switch (entry->p_type) {
    275         case PT_NULL:
    276         case PT_PHDR:
    277         case PT_NOTE:
    278                 break;
    279         case PT_LOAD:
    280                 return load_segment(elf, entry);
    281                 break;
    282         case PT_INTERP:
    283                 /* Assume silently interp == "/app/dload" */
    284                 elf->info->interp = "/app/dload";
    285                 break;
    286         case PT_DYNAMIC:
    287                 /* Record pointer to dynamic section into info structure */
    288                 elf->info->dynamic =
    289                     (void *)((uint8_t *)entry->p_vaddr + elf->bias);
    290                 DPRINTF("dynamic section found at 0x%x\n",
    291                         (uintptr_t)elf->info->dynamic);
    292                 break;
    293         case 0x70000000:
    294                 /* FIXME: MIPS reginfo */
    295                 break;
    296         case PT_SHLIB:
    297 //      case PT_LOPROC:
    298 //      case PT_HIPROC:
    299         default:
    300                 DPRINTF("Segment p_type %d unknown.\n", entry->p_type);
    301                 return EE_UNSUPPORTED;
    302                 break;
    303         }
    304         return EE_OK;
    305 }
    306 
    307 /** Load segment described by program header entry.
    308  *
    309  * @param elf   Loader state.
    310  * @param entry Program header entry describing segment to be loaded.
    311  *
    312  * @return EE_OK on success, error code otherwise.
    313  */
    314 int load_segment(elf_ld_t *elf, elf_segment_header_t *entry)
    315 {
    316         void *a;
    317         int flags = 0;
    318         uintptr_t bias;
    319         uintptr_t base;
    320         void *seg_ptr;
    321         uintptr_t seg_addr;
    322         size_t mem_sz;
    323         ssize_t rc;
    324 
    325         bias = elf->bias;
    326 
    327         seg_addr = entry->p_vaddr + bias;
    328         seg_ptr = (void *) seg_addr;
    329 
    330         DPRINTF("Load segment at addr %p, size 0x%x\n", (void *) seg_addr,
    331                 entry->p_memsz);
    332 
    333         if (entry->p_align > 1) {
    334                 if ((entry->p_offset % entry->p_align) !=
    335                     (seg_addr % entry->p_align)) {
    336                         DPRINTF("Align check 1 failed offset%%align=%d, "
    337                             "vaddr%%align=%d\n",
    338                             entry->p_offset % entry->p_align,
    339                             seg_addr % entry->p_align
    340                         );
    341                         return EE_INVALID;
    342                 }
    343         }
    344 
    345         /* Final flags that will be set for the memory area */
    346 
    347         if (entry->p_flags & PF_X)
    348                 flags |= AS_AREA_EXEC;
    349         if (entry->p_flags & PF_W)
    350                 flags |= AS_AREA_WRITE;
    351         if (entry->p_flags & PF_R)
    352                 flags |= AS_AREA_READ;
    353         flags |= AS_AREA_CACHEABLE;
    354        
    355         base = ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE);
    356         mem_sz = entry->p_memsz + (entry->p_vaddr - base);
    357 
    358         DPRINTF("Map to seg_addr=%p-%p.\n", (void *) seg_addr,
    359             (void *) (entry->p_vaddr + bias +
    360             ALIGN_UP(entry->p_memsz, PAGE_SIZE)));
    361 
    362         /*
    363          * For the course of loading, the area needs to be readable
    364          * and writeable.
    365          */
    366         a = as_area_create((uint8_t *) base + bias, mem_sz,
    367             AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE);
    368         if (a == AS_MAP_FAILED) {
    369                 DPRINTF("memory mapping failed (0x%x, %d)\n",
    370                     base + bias, mem_sz);
    371                 return EE_MEMORY;
    372         }
    373 
    374         DPRINTF("as_area_create(%p, %#zx, %d) -> %p\n",
    375             (void *) (base + bias), mem_sz, flags, (void *) a);
    376 
    377         /*
    378          * Load segment data
    379          */
    380         rc = lseek(elf->fd, entry->p_offset, SEEK_SET);
    381         if (rc < 0) {
    382                 printf("seek error\n");
    383                 return EE_INVALID;
    384         }
    385 
    386 /*      rc = read(fd, (void *)(entry->p_vaddr + bias), entry->p_filesz);
    387         if (rc < 0) { printf("read error\n"); return EE_INVALID; }*/
    388 
    389         /* Long reads are not possible yet. Load segment piecewise. */
    390 
    391         unsigned left, now;
    392         uint8_t *dp;
    393 
    394         left = entry->p_filesz;
    395         dp = seg_ptr;
    396 
    397         while (left > 0) {
    398                 now = 16384;
    399                 if (now > left) now = left;
    400 
    401                 rc = read_all(elf->fd, dp, now);
    402 
    403                 if (rc != (ssize_t) now) {
    404                         DPRINTF("Read error.\n");
    405                         return EE_INVALID;
    406                 }
    407 
    408                 left -= now;
    409                 dp += now;
    410         }
    411 
    412         /*
    413          * The caller wants to modify the segments first. He will then
    414          * need to set the right access mode and ensure SMC coherence.
    415          */
    416         if ((elf->flags & ELDF_RW) != 0) return EE_OK;
    417 
    418 //      printf("set area flags to %d\n", flags);
    419         rc = as_area_change_flags(seg_ptr, flags);
    420         if (rc != 0) {
    421                 DPRINTF("Failed to set memory area flags.\n");
    422                 return EE_MEMORY;
    423         }
    424 
    425         if (flags & AS_AREA_EXEC) {
    426                 /* Enforce SMC coherence for the segment */
    427                 if (smc_coherence(seg_ptr, entry->p_filesz))
    428                         return EE_MEMORY;
    429         }
    430 
    431         return EE_OK;
    432 }
    433 
    434 /** Process section header.
    435  *
    436  * @param elf   Loader state.
    437  * @param entry Segment header.
    438  *
    439  * @return EE_OK on success, error code otherwise.
    440  */
    441 static int section_header(elf_ld_t *elf, elf_section_header_t *entry)
    442 {
    443         switch (entry->sh_type) {
    444         case SHT_PROGBITS:
    445                 if (entry->sh_flags & SHF_TLS) {
    446                         /* .tdata */
    447                 }
    448                 break;
    449         case SHT_NOBITS:
    450                 if (entry->sh_flags & SHF_TLS) {
    451                         /* .tbss */
    452                 }
    453                 break;
    454         case SHT_DYNAMIC:
    455                 /* Record pointer to dynamic section into info structure */
    456                 elf->info->dynamic =
    457                     (void *)((uint8_t *)entry->sh_addr + elf->bias);
    458                 DPRINTF("Dynamic section found at %p.\n",
    459                     (void *) elf->info->dynamic);
    460                 break;
    461         default:
    462                 break;
    463         }
    464        
    465         return EE_OK;
     98        pcb->entry = info->finfo.entry;
     99        pcb->dynamic = info->finfo.dynamic;
     100        pcb->rtld_runtime = info->env;
    466101}
    467102
Note: See TracChangeset for help on using the changeset viewer.