Subversion Repositories HelenOS

Rev

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