scoutos / prex-0.9.0 / bsp / boot / common / elf.c @ 03e9c04a
History | View | Annotate | Download (9.78 KB)
1 | 03e9c04a | Brad Neuman | /*-
|
---|---|---|---|
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 | } |