Subversion Repositories HelenOS

Rev

Rev 3157 | Rev 3169 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2928 svoboda 1
/*
2
 * Copyright (c) 2006 Sergey Bondari
3
 * Copyright (c) 2006 Jakub Jermar
4
 * Copyright (c) 2008 Jiri Svoboda
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 *
11
 * - Redistributions of source code must retain the above copyright
12
 *   notice, this list of conditions and the following disclaimer.
13
 * - Redistributions in binary form must reproduce the above copyright
14
 *   notice, this list of conditions and the following disclaimer in the
15
 *   documentation and/or other materials provided with the distribution.
16
 * - The name of the author may not be used to endorse or promote products
17
 *   derived from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 */
30
 
31
/** @addtogroup generic
32
 * @{
33
 */
34
 
35
/**
36
 * @file
3101 svoboda 37
 * @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.
2928 svoboda 44
 */
45
 
46
#include <stdio.h>
47
#include <sys/types.h>
48
#include <align.h>
49
#include <assert.h>
50
#include <as.h>
2962 svoboda 51
#include <unistd.h>
52
#include <fcntl.h>
3160 svoboda 53
#include <loader/pcb.h>
2962 svoboda 54
 
2928 svoboda 55
#include "elf.h"
3160 svoboda 56
#include "arch/pcb.h"
2962 svoboda 57
#include "elf_load.h"
3131 svoboda 58
#include "arch.h"
2928 svoboda 59
 
60
static char *error_codes[] = {
61
    "no error",
62
    "invalid image",
63
    "address space error",
64
    "incompatible image",
65
    "unsupported image type",
66
    "irrecoverable error"
67
};
68
 
2972 svoboda 69
static unsigned int elf_load(elf_ld_t *elf, size_t so_bias);
2964 svoboda 70
static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry);
71
static int section_header(elf_ld_t *elf, elf_section_header_t *entry);
72
static int load_segment(elf_ld_t *elf, elf_segment_header_t *entry);
2928 svoboda 73
 
3101 svoboda 74
/** Load ELF binary from a file.
75
 *
76
 * Load an ELF binary from the specified file. If the file is
77
 * an executable program, it is loaded unbiased. If it is a shared
78
 * object, it is loaded with the bias @a so_bias. Some information
79
 * extracted from the binary is stored in a elf_info_t structure
80
 * pointed to by @a info.
81
 *
82
 * @param file_name Path to the ELF file.
83
 * @param so_bias   Bias to use if the file is a shared object.
84
 * @param info      Pointer to a structure for storing information
85
 *          extracted from the binary.
86
 *
87
 * @return EOK on success or negative error code.
88
 */
2972 svoboda 89
int elf_load_file(char *file_name, size_t so_bias, elf_info_t *info)
2964 svoboda 90
{
91
    elf_ld_t elf;
2928 svoboda 92
 
2962 svoboda 93
    int fd;
94
    int rc;
95
 
3155 svoboda 96
//  printf("open and read '%s'...\n", file_name);
2962 svoboda 97
 
3101 svoboda 98
    fd = open(file_name, O_RDONLY);
2962 svoboda 99
    if (fd < 0) {
100
        printf("failed opening file\n");
101
        return -1;
102
    }
103
 
2964 svoboda 104
    elf.fd = fd;
105
    elf.info = info;
106
 
2972 svoboda 107
    rc = elf_load(&elf, so_bias);
2962 svoboda 108
 
109
    close(fd);
110
 
111
    return rc;
112
}
113
 
3101 svoboda 114
/** Run an ELF executable.
115
 *
116
 * Transfers control to the entry point of an ELF executable loaded
117
 * earlier with elf_load_file(). This function does not return.
118
 *
119
 * @param info  Info structure filled earlier by elf_load_file()
120
 */
2964 svoboda 121
void elf_run(elf_info_t *info)
2962 svoboda 122
{
3131 svoboda 123
    program_run(info->entry);
2962 svoboda 124
 
125
    /* not reached */
126
}
127
 
3101 svoboda 128
/** Create the program control block (PCB).
129
 *
130
 * Create and install the program control block, initialising it
131
 * with program information from @a info.
132
 *
133
 * @param info  Program info structure
134
 * @return EOK on success or negative error code
135
 */
2964 svoboda 136
int elf_create_pcb(elf_info_t *info)
2962 svoboda 137
{
138
    pcb_t *pcb;
139
    void *a;
140
 
2990 svoboda 141
    pcb = __pcb_get();
2962 svoboda 142
 
143
    a = as_area_create(pcb, sizeof(pcb_t), AS_AREA_READ | AS_AREA_WRITE);
144
    if (a == (void *)(-1)) {
145
        printf("elf_create_pcb: memory mapping failed\n");
146
        return EE_MEMORY;
147
    }
148
 
2964 svoboda 149
    pcb->entry = info->entry;
2965 svoboda 150
    pcb->dynamic = info->dynamic;
2962 svoboda 151
 
152
    return 0;
153
}
154
 
155
 
3101 svoboda 156
/** Load an ELF binary.
2928 svoboda 157
 *
3101 svoboda 158
 * The @a elf structure contains the loader state, including
159
 * an open file, from which the binary will be loaded,
160
 * a pointer to the @c info structure etc.
161
 *
162
 * @param elf       Pointer to loader state buffer.
163
 * @param so_bias   Bias to use if the file is a shared object.
164
 * @return EE_OK on success or EE_xx error code.
2928 svoboda 165
 */
2972 svoboda 166
static unsigned int elf_load(elf_ld_t *elf, size_t so_bias)
2928 svoboda 167
{
2964 svoboda 168
    elf_header_t header_buf;
169
    elf_header_t *header = &header_buf;
2928 svoboda 170
    int i, rc;
171
 
2964 svoboda 172
    rc = read(elf->fd, header, sizeof(elf_header_t));
2928 svoboda 173
    if (rc < 0) {
174
        printf("read error\n");
175
        return EE_INVALID;
176
    }
177
 
2964 svoboda 178
    elf->header = header;
179
 
3155 svoboda 180
//  printf("ELF-load:");
2928 svoboda 181
    /* Identify ELF */
182
    if (header->e_ident[EI_MAG0] != ELFMAG0 ||
183
        header->e_ident[EI_MAG1] != ELFMAG1 ||
184
        header->e_ident[EI_MAG2] != ELFMAG2 ||
185
        header->e_ident[EI_MAG3] != ELFMAG3) {
186
        printf("invalid header\n");
187
        return EE_INVALID;
188
    }
189
 
190
    /* Identify ELF compatibility */
191
    if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
192
        header->e_machine != ELF_MACHINE ||
193
        header->e_ident[EI_VERSION] != EV_CURRENT ||
194
        header->e_version != EV_CURRENT ||
195
        header->e_ident[EI_CLASS] != ELF_CLASS) {
196
        printf("incompatible data/version/class\n");
197
        return EE_INCOMPATIBLE;
198
    }
199
 
2992 svoboda 200
    if (header->e_phentsize != sizeof(elf_segment_header_t)) {
201
        printf("e_phentsize:%d != %d\n", header->e_phentsize,
202
            sizeof(elf_segment_header_t));
2928 svoboda 203
        return EE_INCOMPATIBLE;
2992 svoboda 204
    }
2928 svoboda 205
 
2992 svoboda 206
    if (header->e_shentsize != sizeof(elf_section_header_t)) {
207
        printf("e_shentsize:%d != %d\n", header->e_shentsize,
208
            sizeof(elf_section_header_t));
2928 svoboda 209
        return EE_INCOMPATIBLE;
2992 svoboda 210
    }
2928 svoboda 211
 
212
    /* Check if the object type is supported. */
213
    if (header->e_type != ET_EXEC && header->e_type != ET_DYN) {
214
        printf("Object type %d is not supported\n", header->e_type);
215
        return EE_UNSUPPORTED;
216
    }
217
 
2972 svoboda 218
    /* Shared objects can be loaded with a bias */
3155 svoboda 219
//  printf("Object type: %d\n", header->e_type);
2964 svoboda 220
    if (header->e_type == ET_DYN)
2972 svoboda 221
        elf->bias = so_bias;
2964 svoboda 222
    else
223
        elf->bias = 0;
224
 
3155 svoboda 225
//  printf("Bias set to 0x%x\n", elf->bias);
3004 svoboda 226
    elf->info->interp = NULL;
227
    elf->info->dynamic = NULL;
2971 svoboda 228
 
3155 svoboda 229
//  printf("parse segments\n");
2928 svoboda 230
 
231
    /* Walk through all segment headers and process them. */
232
    for (i = 0; i < header->e_phnum; i++) {
2964 svoboda 233
        elf_segment_header_t segment_hdr;
2928 svoboda 234
 
2961 svoboda 235
        /* Seek to start of segment header */
2964 svoboda 236
        lseek(elf->fd, header->e_phoff
237
                + i * sizeof(elf_segment_header_t), SEEK_SET);
2928 svoboda 238
 
2964 svoboda 239
        rc = read(elf->fd, &segment_hdr, sizeof(elf_segment_header_t));
240
        if (rc < 0) { printf("read error\n"); return EE_INVALID; }
241
 
242
        rc = segment_header(elf, &segment_hdr);
2928 svoboda 243
        if (rc != EE_OK)
244
            return rc;
245
    }
246
 
3155 svoboda 247
//  printf("parse sections\n");
2928 svoboda 248
 
249
    /* Inspect all section headers and proccess them. */
250
    for (i = 0; i < header->e_shnum; i++) {
2964 svoboda 251
        elf_section_header_t section_hdr;
2928 svoboda 252
 
2961 svoboda 253
        /* Seek to start of section header */
2964 svoboda 254
        lseek(elf->fd, header->e_shoff
255
            + i * sizeof(elf_section_header_t), SEEK_SET);
2961 svoboda 256
 
2964 svoboda 257
        rc = read(elf->fd, &section_hdr, sizeof(elf_section_header_t));
258
        if (rc < 0) { printf("read error\n"); return EE_INVALID; }
259
 
260
        rc = section_header(elf, &section_hdr);
2928 svoboda 261
        if (rc != EE_OK)
2961 svoboda 262
            return rc;
2928 svoboda 263
    }
264
 
2964 svoboda 265
    elf->info->entry =
266
        (entry_point_t)((uint8_t *)header->e_entry + elf->bias);
267
 
3155 svoboda 268
//  printf("done\n");
2928 svoboda 269
 
270
    return EE_OK;
271
}
272
 
273
/** Print error message according to error code.
274
 *
275
 * @param rc Return code returned by elf_load().
276
 *
277
 * @return NULL terminated description of error.
278
 */
279
char *elf_error(unsigned int rc)
280
{
281
    assert(rc < sizeof(error_codes) / sizeof(char *));
282
 
283
    return error_codes[rc];
284
}
285
 
286
/** Process segment header.
287
 *
3101 svoboda 288
 * @param entry Segment header.
2928 svoboda 289
 *
290
 * @return EE_OK on success, error code otherwise.
291
 */
2964 svoboda 292
static int segment_header(elf_ld_t *elf, elf_segment_header_t *entry)
2928 svoboda 293
{
294
    switch (entry->p_type) {
295
    case PT_NULL:
296
    case PT_PHDR:
297
        break;
298
    case PT_LOAD:
2964 svoboda 299
        return load_segment(elf, entry);
2928 svoboda 300
        break;
3004 svoboda 301
    case PT_INTERP:
302
        /* Assume silently interp == "/rtld.so" */
303
        elf->info->interp = "/rtld.so";
304
        break;
2928 svoboda 305
    case PT_DYNAMIC:
306
    case PT_SHLIB:
307
    case PT_NOTE:
308
    case PT_LOPROC:
309
    case PT_HIPROC:
310
    default:
311
        printf("segment p_type %d unknown\n", entry->p_type);
312
        return EE_UNSUPPORTED;
313
        break;
314
    }
315
    return EE_OK;
316
}
317
 
318
/** Load segment described by program header entry.
319
 *
3101 svoboda 320
 * @param elf   Loader state.
2928 svoboda 321
 * @param entry Program header entry describing segment to be loaded.
322
 *
323
 * @return EE_OK on success, error code otherwise.
324
 */
2964 svoboda 325
int load_segment(elf_ld_t *elf, elf_segment_header_t *entry)
2928 svoboda 326
{
327
    void *a;
328
    int flags = 0;
2932 svoboda 329
    uintptr_t bias;
3010 svoboda 330
    uintptr_t base;
331
    size_t mem_sz;
2928 svoboda 332
    int rc;
333
 
3155 svoboda 334
//  printf("load segment at addr 0x%x, size 0x%x\n", entry->p_vaddr,
335
//      entry->p_memsz);
2928 svoboda 336
 
2964 svoboda 337
    bias = elf->bias;
2928 svoboda 338
 
339
    if (entry->p_align > 1) {
340
        if ((entry->p_offset % entry->p_align) !=
341
            (entry->p_vaddr % entry->p_align)) {
342
            printf("align check 1 failed offset%%align=%d, vaddr%%align=%d\n",
343
            entry->p_offset % entry->p_align,
344
            entry->p_vaddr % entry->p_align
345
            );
346
            return EE_INVALID;
347
        }
348
    }
349
 
2992 svoboda 350
    /* Final flags that will be set for the memory area */
351
 
2985 svoboda 352
    if (entry->p_flags & PF_X)
2928 svoboda 353
        flags |= AS_AREA_EXEC;
354
    if (entry->p_flags & PF_W)
355
        flags |= AS_AREA_WRITE;
356
    if (entry->p_flags & PF_R)
357
        flags |= AS_AREA_READ;
358
    flags |= AS_AREA_CACHEABLE;
3010 svoboda 359
 
360
    base = ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE);
361
    mem_sz = entry->p_memsz + (entry->p_vaddr - base);
2985 svoboda 362
 
3155 svoboda 363
//  printf("map to p_vaddr=0x%x-0x%x...\n", entry->p_vaddr + bias,
364
//  entry->p_vaddr + bias + ALIGN_UP(entry->p_memsz, PAGE_SIZE));
2928 svoboda 365
 
2985 svoboda 366
    /*
367
     * For the course of loading, the area needs to be readable
368
     * and writeable.
369
     */
3010 svoboda 370
    a = as_area_create((uint8_t *)base + bias,
371
        mem_sz, AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE);
2928 svoboda 372
    if (a == (void *)(-1)) {
373
        printf("memory mapping failed\n");
374
        return EE_MEMORY;
375
    }
376
 
3155 svoboda 377
//  printf("as_area_create(0x%lx, 0x%x, %d) -> 0x%lx\n",
378
//      entry->p_vaddr+bias, entry->p_memsz, flags, (uintptr_t)a);
2928 svoboda 379
 
380
    /*
381
     * Load segment data
382
     */
3155 svoboda 383
//  printf("seek to %d\n", entry->p_offset);
2964 svoboda 384
    rc = lseek(elf->fd, entry->p_offset, SEEK_SET);
2928 svoboda 385
    if (rc < 0) { printf("seek error\n"); return EE_INVALID; }
386
 
3155 svoboda 387
//  printf("read 0x%x bytes to address 0x%x\n", entry->p_filesz, entry->p_vaddr+bias);
2949 svoboda 388
/*  rc = read(fd, (void *)(entry->p_vaddr + bias), entry->p_filesz);
389
    if (rc < 0) { printf("read error\n"); return EE_INVALID; }*/
3101 svoboda 390
 
391
    /* Long reads are not possible yet. Load segment picewise */
392
 
2949 svoboda 393
    unsigned left, now;
394
    uint8_t *dp;
2928 svoboda 395
 
2949 svoboda 396
    left = entry->p_filesz;
397
    dp = (uint8_t *)(entry->p_vaddr + bias);
398
 
399
    while (left > 0) {
2977 svoboda 400
        now = 16384;
3101 svoboda 401
        if (now > left) now = left;
402
 
3155 svoboda 403
//      printf("read %d...", now);
2964 svoboda 404
        rc = read(elf->fd, dp, now);
3155 svoboda 405
//      printf("->%d\n", rc);
3101 svoboda 406
 
2949 svoboda 407
        if (rc < 0) { printf("read error\n"); return EE_INVALID; }
3101 svoboda 408
 
2949 svoboda 409
        left -= now;
410
        dp += now;
411
    }
412
 
3155 svoboda 413
//  printf("set area flags to %d\n", flags);
2985 svoboda 414
    rc = as_area_change_flags((uint8_t *)entry->p_vaddr + bias, flags);
415
    if (rc != 0) {
416
        printf("failed to set memory area flags\n");
417
        return EE_MEMORY;
418
    }
419
 
2928 svoboda 420
    return EE_OK;
421
}
422
 
423
/** Process section header.
424
 *
3101 svoboda 425
 * @param elf   Loader state.
2928 svoboda 426
 * @param entry Segment header.
427
 *
428
 * @return EE_OK on success, error code otherwise.
429
 */
2964 svoboda 430
static int section_header(elf_ld_t *elf, elf_section_header_t *entry)
2928 svoboda 431
{
432
    switch (entry->sh_type) {
433
    case SHT_PROGBITS:
434
        if (entry->sh_flags & SHF_TLS) {
435
            /* .tdata */
436
        }
437
        break;
438
    case SHT_NOBITS:
439
        if (entry->sh_flags & SHF_TLS) {
440
            /* .tbss */
441
        }
442
        break;
2961 svoboda 443
    case SHT_DYNAMIC:
3101 svoboda 444
        /* Record pointer to dynamic section into info structure */
2964 svoboda 445
        elf->info->dynamic =
446
            (void *)((uint8_t *)entry->sh_addr + elf->bias);
447
        printf("dynamic section found at 0x%x\n",
448
            (uintptr_t)elf->info->dynamic);
2961 svoboda 449
        break;
2928 svoboda 450
    default:
451
        break;
452
    }
453
 
454
    return EE_OK;
455
}
456
 
457
/** @}
458
 */