Statistics
| Branch: | Revision:

root / prex-0.9.0 / bsp / boot / common / elf.c @ 03e9c04a

History | View | Annotate | Download (9.78 KB)

1
/*-
2
 * Copyright (c) 2005-2006, Kohsuke Ohtani
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. Neither the name of the author nor the names of any co-contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29

    
30
/*
31
 * elf.c - ELF file format support
32
 */
33

    
34
#include <boot.h>
35
#include <load.h>
36
#include <sys/elf.h>
37
#include <elf_reloc.h>
38

    
39
/* forward declarations */
40
static int        load_executable(char *, struct module *);
41
static int        load_relocatable(char *, struct module *);
42

    
43
#define SHF_VALID        (SHF_ALLOC | SHF_EXECINSTR | SHF_ALLOC | SHF_WRITE)
44

    
45
static char *sect_addr[32];        /* array of section address */
46
static int strshndx;                /* index of string section */
47

    
48
/*
49
 * Load the program from specified ELF image stored in memory.
50
 * The boot information is filled after loading the program.
51
 */
52
int
53
load_elf(char *img, struct module *m)
54
{
55
        Elf32_Ehdr *ehdr;
56
        Elf32_Phdr *phdr;
57

    
58
        ELFDBG(("\nelf_load\n"));
59

    
60
        ehdr = (Elf32_Ehdr *)img;
61

    
62
        /*  Check ELF header */
63
        if ((ehdr->e_ident[EI_MAG0] != ELFMAG0) ||
64
            (ehdr->e_ident[EI_MAG1] != ELFMAG1) ||
65
            (ehdr->e_ident[EI_MAG2] != ELFMAG2) ||
66
            (ehdr->e_ident[EI_MAG3] != ELFMAG3)) {
67
                DPRINTF(("Invalid ELF image\n"));
68
                return -1;
69
        }
70

    
71
        phdr = (Elf32_Phdr *)((paddr_t)ehdr + ehdr->e_ehsize);
72

    
73
        if (nr_img == 0) {
74
                /*  Initialize the load address */
75
                load_base = (vaddr_t)ptokv(phdr->p_paddr);
76
                if (load_base == 0) {
77
                        DPRINTF(("Invalid load address\n"));
78
                        return -1;
79
                }
80
                ELFDBG(("kernel base=%lx\n", load_base));
81
                load_start = load_base;
82
        }
83
        else if (nr_img == 1) {
84
                /* 2nd image => Driver */
85
                ELFDBG(("driver base=%lx\n", load_base));
86
        }
87
        else {
88
                /* Other images => Boot tasks */
89
                ELFDBG(("task base=%lx\n", load_base));
90
        }
91

    
92
        switch (ehdr->e_type) {
93
        case ET_EXEC:
94
                if (load_executable(img, m) != 0)
95
                        return -1;
96
                break;
97
        case ET_REL:
98
                if (load_relocatable(img, m) != 0)
99
                        return -1;
100
                break;
101
        default:
102
                ELFDBG(("Unsupported file type\n"));
103
                return -1;
104
        }
105
        nr_img++;
106
        return 0;
107
}
108

    
109
static int
110
load_executable(char *img, struct module *m)
111
{
112
        Elf32_Ehdr *ehdr;
113
        Elf32_Phdr *phdr;
114
        paddr_t phys_base;
115
        int i;
116

    
117
        phys_base = load_base;
118
        ehdr = (Elf32_Ehdr *)img;
119
        phdr = (Elf32_Phdr *)((paddr_t)ehdr + ehdr->e_phoff);
120
        m->phys = load_base;
121
        phys_base = load_base;
122
        ELFDBG(("phys addr=%lx\n", phys_base));
123

    
124
        for (i = 0; i < (int)ehdr->e_phnum; i++, phdr++) {
125
                if (phdr->p_type != PT_LOAD)
126
                        continue;
127

    
128
                ELFDBG(("p_flags=%x\n", (int)phdr->p_flags));
129
                ELFDBG(("p_align=%x\n", (int)phdr->p_align));
130
                ELFDBG(("p_paddr=%x\n", phdr->p_paddr));
131

    
132
                if (i >= 2) {
133
                        ELFDBG(("skipping extra phdr\n"));
134
                        continue;
135
                }
136
                if (phdr->p_flags & PF_X) {
137
                        /* Text */
138
                        m->text = phdr->p_vaddr;
139
                        m->textsz = (size_t)phdr->p_memsz;
140
                } else {
141
                        /* Data & BSS */
142
                        m->data = phdr->p_vaddr;
143
                        m->datasz = (size_t)phdr->p_filesz;
144
                        m->bsssz =
145
                                (size_t)(phdr->p_memsz - phdr->p_filesz);
146
                        load_base = phys_base + (m->data - m->text);
147
                }
148
                if (phdr->p_filesz > 0) {
149
                        memcpy((char *)load_base, img + phdr->p_offset,
150
                               (size_t)phdr->p_filesz);
151
                        ELFDBG(("load: offset=%lx size=%x\n",
152
                                 load_base, (int)phdr->p_filesz));
153
                }
154
                if (!(phdr->p_flags & PF_X)) {
155
                        if (m->bsssz > 0) {
156
                                /* Zero fill BSS */
157
                                memset((char *)load_base + m->datasz,
158
                                       0, m->bsssz);
159
                        }
160
                        load_base += phdr->p_memsz;
161
                }
162
        }
163
        /* workaround for data/bss size is 0 */
164
        if (m->data == 0)
165
                load_base = phys_base + m->textsz;
166

    
167
        load_base = round_page(load_base);
168
        m->size = (size_t)(load_base - m->phys);
169
        m->entry = ehdr->e_entry;
170
        ELFDBG(("module size=%x entry=%lx\n", m->size, m->entry));
171

    
172
        if (m->size == 0)
173
                panic("Module size is 0!");
174
        return 0;
175
}
176

    
177
static int
178
relocate_section_rela(Elf32_Sym *sym_table, Elf32_Rela *rela,
179
                      char *target_sect, int nr_reloc, char *strtab)
180
{
181
        Elf32_Sym *sym;
182
        Elf32_Addr sym_val;
183
        int i;
184

    
185
        for (i = 0; i < nr_reloc; i++) {
186
                sym = &sym_table[ELF32_R_SYM(rela->r_info)];
187
                ELFDBG(("%s\n", strtab + sym->st_name));
188
                if (sym->st_shndx != STN_UNDEF) {
189
                        sym_val = (Elf32_Addr)sect_addr[sym->st_shndx]
190
                                + sym->st_value;
191
                        if (relocate_rela(rela, sym_val, target_sect) != 0)
192
                                return -1;
193
                } else if (ELF32_ST_BIND(sym->st_info) != STB_WEAK) {
194
                        DPRINTF(("Undefined symbol for rela[%x] sym=%lx\n",
195
                                 i, (long)sym));
196
                        return -1;
197
                } else {
198
                        DPRINTF(("Undefined weak symbol for rela[%x]\n", i));
199
                }
200
                rela++;
201
        }
202
        return 0;
203
}
204

    
205
static int
206
relocate_section_rel(Elf32_Sym *sym_table, Elf32_Rel *rel,
207
                     char *target_sect, int nr_reloc, char *strtab)
208
{
209
        Elf32_Sym *sym;
210
        Elf32_Addr sym_val;
211
        int i;
212

    
213
        for (i = 0; i < nr_reloc; i++) {
214
                sym = &sym_table[ELF32_R_SYM(rel->r_info)];
215
                ELFDBG(("%s\n", strtab + sym->st_name));
216
                if (sym->st_shndx != STN_UNDEF) {
217
                        sym_val = (Elf32_Addr)sect_addr[sym->st_shndx]
218
                                + sym->st_value;
219
                        if (relocate_rel(rel, sym_val, target_sect) != 0)
220
                                return -1;
221
                } else if (ELF32_ST_BIND(sym->st_info) != STB_WEAK) {
222
                        DPRINTF(("Undefined symbol for rel[%x] sym=%lx\n",
223
                                 i, (long)sym));
224
                        return -1;
225
                } else {
226
                        DPRINTF(("Undefined weak symbol for rel[%x]\n", i));
227
                }
228
                rel++;
229
        }
230
        return 0;
231
}
232

    
233
static int
234
relocate_section(char *img, Elf32_Shdr *shdr)
235
{
236
        Elf32_Sym *symtab;
237
        char *target_sect;
238
        int nr_reloc, error;
239
        char *strtab;
240

    
241
        ELFDBG(("relocate_section\n"));
242

    
243
        if (shdr->sh_entsize == 0)
244
                return 0;
245
        if ((target_sect = sect_addr[shdr->sh_info]) == 0)
246
                return -1;
247
        if ((symtab = (Elf32_Sym *)sect_addr[shdr->sh_link]) == 0)
248
                return -1;
249
        if ((strtab = sect_addr[strshndx]) == 0)
250
                return -1;
251
        ELFDBG(("strtab=%x\n", strtab));
252

    
253
        nr_reloc = (int)(shdr->sh_size / shdr->sh_entsize);
254
        switch (shdr->sh_type) {
255
        case SHT_REL:
256
                error = relocate_section_rel(symtab,
257
                                (Elf32_Rel *)(img + shdr->sh_offset),
258
                                target_sect, nr_reloc, strtab);
259
                break;
260

    
261
        case SHT_RELA:
262
                error = relocate_section_rela(symtab,
263
                                (Elf32_Rela *)(img + shdr->sh_offset),
264
                                target_sect, nr_reloc, strtab);
265
                break;
266

    
267
        default:
268
                error = -1;
269
                break;
270
        }
271
        return error;
272
}
273

    
274
static int
275
load_relocatable(char *img, struct module *m)
276
{
277
        Elf32_Ehdr *ehdr;
278
        Elf32_Shdr *shdr;
279
        paddr_t sect_base, bss_base;
280
        int i;
281

    
282
        strshndx = 0;
283
        ehdr = (Elf32_Ehdr *)img;
284
        shdr = (Elf32_Shdr *)((paddr_t)ehdr + ehdr->e_shoff);
285
        bss_base = 0;
286
        m->phys = load_base;
287
        ELFDBG(("phys addr=%lx\n", load_base));
288

    
289
        /* Copy sections */
290
        for (i = 0; i < (int)ehdr->e_shnum; i++, shdr++) {
291
                sect_addr[i] = 0;
292
                if (shdr->sh_type == SHT_PROGBITS) {
293

    
294
                        ELFDBG(("sh_addr=%x\n", shdr->sh_addr));
295
                        ELFDBG(("sh_size=%x\n", shdr->sh_size));
296
                        ELFDBG(("sh_offset=%x\n", shdr->sh_offset));
297
                        ELFDBG(("sh_flags=%x\n", shdr->sh_flags));
298

    
299
                        switch (shdr->sh_flags & SHF_VALID) {
300
                        case (SHF_ALLOC | SHF_EXECINSTR):
301
                                /* Text */
302
                                m->text = (vaddr_t)ptokv(load_base);
303
                                break;
304
                        case (SHF_ALLOC | SHF_WRITE):
305
                                /* Data */
306
                                if (m->data == 0) {
307
                                        m->data = (vaddr_t)ptokv(load_base +
308
                                                                 shdr->sh_addr);
309
                                }
310
                                break;
311
                        case SHF_ALLOC:
312
                                /* rodata */
313
                                /* Note: rodata is treated as text. */
314
                                break;
315
                        default:
316
                                continue;
317
                        }
318
                        sect_base = load_base + shdr->sh_addr;
319
                        memcpy((char *)sect_base, img + shdr->sh_offset,
320
                               (size_t)shdr->sh_size);
321
                        ELFDBG(("load: offset=%lx size=%x\n",
322
                                 sect_base, (int)shdr->sh_size));
323

    
324
                        sect_addr[i] = (char *)sect_base;
325
                } else if (shdr->sh_type == SHT_NOBITS) {
326
                        /* BSS */
327
                        m->bsssz = (size_t)shdr->sh_size;
328
                        sect_base = load_base + shdr->sh_addr;
329
                        bss_base = sect_base;
330

    
331
                        /* Zero fill BSS */
332
                        memset((char *)bss_base, 0, (size_t)shdr->sh_size);
333

    
334
                        sect_addr[i] = (char *)sect_base;
335
                } else if (shdr->sh_type == SHT_SYMTAB) {
336
                        /* Symbol table */
337
                        ELFDBG(("load: symtab index=%d link=%d\n",
338
                                i, shdr->sh_link));
339
                        sect_addr[i] = img + shdr->sh_offset;
340
                        if (strshndx != 0)
341
                                panic("Multiple symtab found!");
342
                        strshndx = (int)shdr->sh_link;
343
                } else if (shdr->sh_type == SHT_STRTAB) {
344
                        /* String table */
345
                        sect_addr[i] = img + shdr->sh_offset;
346
                        ELFDBG(("load: strtab index=%d addr=%x\n",
347
                                i, sect_addr[i]));
348
                }
349
        }
350
        m->textsz = (size_t)(m->data - m->text);
351
        m->datasz = (size_t)((char *)ptokv(bss_base) - m->data);
352

    
353
        load_base = bss_base + m->bsssz;
354
        load_base = round_page(load_base);
355

    
356
        ELFDBG(("module load_base=%lx text=%lx\n", load_base, m->text));
357
        m->size = (size_t)(load_base - kvtop(m->text));
358
        m->entry = (vaddr_t)ptokv(ehdr->e_entry + m->phys);
359
        ELFDBG(("module size=%x entry=%lx\n", m->size, m->entry));
360

    
361
        /* Process relocation */
362
        shdr = (Elf32_Shdr *)((paddr_t)ehdr + ehdr->e_shoff);
363
        for (i = 0; i < (int)ehdr->e_shnum; i++, shdr++) {
364
                if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) {
365
                        if (relocate_section(img, shdr) != 0) {
366
                                DPRINTF(("Relocation error: module=%s\n", m->name));
367
                                return -1;
368
                        }
369
                }
370
        }
371
        return 0;
372
}