summaryrefslogtreecommitdiff
path: root/emulator/elf.c
blob: 43409cc875affe73bd13f3a83143e997e478a2a5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>

#define ET_NONE 0x00
#define ET_REL  0x01
#define ET_EXEC 0x02
#define ET_DYN  0x03
#define ET_CORE 0x04

struct Elf32Header
{
    uint8_t  ident[16];
    uint16_t type;
    uint16_t machine;
    uint32_t version;
    uint32_t entry;
    uint32_t phoff;
    uint32_t shoff;
    uint32_t flags;
    uint16_t ehsize;
    uint16_t phentsize;
    uint16_t phnum;
    uint16_t shentsize;
    uint16_t shnum;
    uint16_t shstrndx;
};

#define PT_NULL    0x00000000
#define PT_LOAD    0x00000001
#define PT_DYNAMIC 0x00000002
#define PT_INTERP  0x00000003
#define PT_NOTE    0x00000004
#define PT_SHLIB   0x00000005
#define PT_PHDR    0x00000006
#define PT_TLS     0x00000007

#define PF_X 0x1
#define PF_W 0x2
#define PF_R 0x4

struct Elf32ProgramHeader
{
    uint32_t type;
    uint32_t offset;
    uint32_t vaddr;
    uint32_t paddr;
    uint32_t filesz;
    uint32_t memsz;
    uint32_t flags;
    uint32_t align;
};

bool load_elf(const char* file, char* mem, uint32_t mem_size, uint32_t* start_address)
{
    FILE* fp = fopen(file, "rb");
    if (fp == NULL)
    {
        printf("Couldn't open input file\n");
        return false;
    }

    struct Elf32Header header;
    size_t read = fread(&header, sizeof(struct Elf32Header), 1, fp);
    if (read != 1)
    {
        printf("ELF header too small\n");
        return false;
    }

    static uint8_t kValidIdent[7] = {
        0x7f, 0x45, 0x4c, 0x46,
        1, 1, 1
    };
    if (memcmp(kValidIdent, header.ident, 7) != 0)
    {
        printf("Invalid ELF header\n");
        return false;
    }

    if (header.type != ET_EXEC)
    {
        printf("Can only link EXEC ELF files\n");
        return false;
    }

    memset(mem, 0, mem_size);
    
    assert(header.phnum == 0 || header.phentsize == sizeof(struct Elf32ProgramHeader));
    for (uint32_t i = 0; i < header.phnum; ++i)
    {
        struct Elf32ProgramHeader pheader;

        fseek(fp, header.phoff + i * header.phentsize, SEEK_SET);
        read = fread(&pheader, sizeof(struct Elf32ProgramHeader), 1, fp);
        if (read != 1)
        {
            printf("Error reading program header at index %u\n", i);
            return false;
        }

        if (pheader.type == PT_LOAD)
        {
            if (pheader.paddr + pheader.memsz > mem_size)
            {
                printf("Memory not large enough for ELF file\n");
                return false;
            }

            fseek(fp, pheader.offset, SEEK_SET);
            read = fread(mem + pheader.paddr, pheader.filesz, 1, fp);
            if (read != 1)
            {
                printf("Error when copying ELF segment\n");
                return false;
            }
        }
    }

    if (header.entry > mem_size)
    {
        printf("Entry address out of bounds\n");
        return false;
    }

    *start_address = header.entry;
    fclose(fp);

    return true;
}