revert between 56095 -> 55830 in arch
[AROS.git] / arch / ppc-chrp / boot / openfirmware / src / elf.c
blob6006d4304d25d3f21c1f4668f412365592aaebc7
1 /*
2 * $Id$
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17 * MA 02111-1307 USA
20 #define DEBUG 0
21 #include <debug.h>
22 #include <elf.h>
23 #include <support.h>
25 static char *ptr_ro = (char *)KERNEL_PHYS_BASE;
26 static char *ptr_rw = (char *)KERNEL_PHYS_BASE;
28 char *get_ptr_ro()
30 return ptr_ro;
33 void ptr_ro_add(int amount)
35 ptr_ro += amount;
38 char *get_ptr_rw()
40 return ptr_rw;
43 struct bss_tracker tracker[MAX_BSS_SECTIONS];
44 static struct bss_tracker *bss_tracker = &tracker[0];
46 extern list_t *debug_info;
49 * read_block function copies the range of memory within ELF file to any specified location.
51 static inline void read_block(void *file, long offset, void *dest, long length)
53 memcpy(dest, (void *)((unsigned long)file + offset), length);
57 * Test for correct ELF header here
59 static int check_header(struct elfheader *eh)
63 eh->ident[0] != 0x7f ||
64 eh->ident[1] != 'E' ||
65 eh->ident[2] != 'L' ||
66 eh->ident[3] != 'F'
69 return 0;
72 if (eh->type != ET_REL || eh->machine != EM_PPC)
74 return 0;
76 return 1;
80 * Get the memory for chunk and load it
82 static int load_hunk(void *file, struct sheader *sh, unsigned long virt)
84 void *ptr=(void*)0;
86 /* empty chunk? Who cares :) */
87 if (!sh->size)
88 return 1;
90 /* Allocate a chunk with write access - take aligned memory beneath the RO kernel */
91 if (sh->flags & SHF_WRITE)
93 ptr = (void*)(((unsigned long)ptr_rw - (unsigned long)sh->size - (unsigned long)sh->addralign + 1) & ~((unsigned long)sh->addralign-1));
94 ptr_rw = ptr;
96 else
98 /* Read-Only mode? Get the memory from the kernel space, align it accorting to the demand */
99 ptr_ro = (char *)(((unsigned long)ptr_ro + (unsigned long)sh->addralign - 1) & ~((unsigned long)sh->addralign-1));
100 ptr = ptr_ro;
101 ptr_ro = ptr_ro + sh->size;
104 sh->addr = (long)ptr;
106 /* copy block of memory from ELF file if it exists */
107 if (sh->type != SHT_NOBITS)
109 read_block(file, sh->offset, (void *)((unsigned long)sh->addr), sh->size);
111 else
113 bzero(ptr, sh->size);
114 bss_tracker->addr = (void*)((unsigned long)ptr + virt - KERNEL_PHYS_BASE);
115 bss_tracker->length = sh->size;
116 bss_tracker++;
117 bss_tracker->addr = NULL;
118 bss_tracker->length = 0;
121 return 1;
124 /* Perform relocations of given section */
125 static int relocate(struct elfheader *eh, struct sheader *sh, long shrel_idx, uint32_t virt)
127 struct sheader *shrel = &sh[shrel_idx];
128 struct sheader *shsymtab = &sh[shrel->link];
129 struct sheader *toreloc = &sh[shrel->info];
131 struct symbol *symtab = (struct symbol *)((unsigned long)shsymtab->addr);
132 struct relo *rel = (struct relo *)((unsigned long)shrel->addr);
133 char *section = (char *)((unsigned long)toreloc->addr);
135 unsigned int numrel = (unsigned long)shrel->size / (unsigned long)shrel->entsize;
136 unsigned int i;
138 uint32_t virtoffset;
140 struct symbol *SysBase_sym = (void*)0;
142 for (i=0; i<numrel; i++, rel++)
144 struct symbol *sym = &symtab[ELF32_R_SYM(rel->info)];
145 uint32_t *p = (uint32_t *)&section[rel->offset];
146 uint32_t s;
147 virtoffset = virt;
149 switch (sym->shindex)
151 case SHN_UNDEF:
152 bug("[ELF Loader] Undefined symbol '%s' in section '%s'\r\n",
153 (char *)((uint32_t)sh[shsymtab->link].addr) + sym->name,
154 (char *)((uint32_t)sh[eh->shstrndx].addr) + toreloc->name);
155 return 0;
157 case SHN_COMMON:
158 bug("[ELF Loader] COMMON symbol '%s' in section '%s'\r\n",
159 (char *)((uint32_t)sh[shsymtab->link].addr) + sym->name,
160 (char *)((uint32_t)sh[eh->shstrndx].addr) + toreloc->name);
162 return 0;
164 case SHN_ABS:
165 if (SysBase_sym == (void*)0)
167 if (strncmp((char *)((uint32_t)sh[shsymtab->link].addr) + sym->name, "SysBase", 8) == 0)
169 SysBase_sym = sym;
170 goto SysBase_yes;
172 else
173 goto SysBase_no;
175 else
176 if (SysBase_sym == sym)
178 SysBase_yes: s = (uint32_t)4UL;
179 virtoffset = 0;
181 else
182 SysBase_no: s = sym->value;
183 break;
184 default:
185 s = (uint32_t)sh[sym->shindex].addr + sym->value;
188 switch (ELF32_R_TYPE(rel->info))
190 case R_PPC_ADDR32:
191 *p = s + rel->addend + virtoffset;
192 break;
194 case R_PPC_ADDR16_LO:
196 unsigned short *c = (unsigned short *) p;
197 *c = (s + rel->addend + virtoffset) & 0xffff;
199 break;
201 case R_PPC_ADDR16_HA:
203 unsigned short *c = (unsigned short *) p;
204 uint32_t temp = s + rel->addend + virtoffset;
205 *c = temp >> 16;
206 if ((temp & 0x8000) != 0)
207 (*c)++;
209 break;
211 case R_PPC_REL24:
212 *p &= ~0x3fffffc;
213 *p |= (s + rel->addend - (uint32_t) p) & 0x3fffffc;
214 break;
216 case R_PPC_REL32:
217 *p = s + rel->addend - (uint32_t) p;
218 break;
220 case R_PPC_NONE:
221 break;
223 default:
224 bug("[BOOT] Unknown relocation %d in ELF file\r\n", ELF32_R_TYPE(rel->info));
225 return 0;
228 return 1;
231 int load_elf_file(const char *name, void *file, unsigned long virt)
233 struct elfheader *eh;
234 struct sheader *sh;
235 long i;
236 int shown = 0;
237 module_t *mod;
239 eh = file;
241 /* Check the header of ELF file */
242 if (!check_header(eh))
244 D(bug("[BOOT] ELF header invalid\r\n"));
245 return 0;
248 sh = (struct sheader *)((unsigned long)file + eh->shoff);
250 /* Iterate over the section header in order to prepare memory and eventually load some hunks */
251 for (i=0; i < eh->shnum; i++)
253 /* Load the symbol and string tables */
254 if (sh[i].type == SHT_SYMTAB || sh[i].type == SHT_STRTAB)
256 sh[i].addr = (unsigned long)file + sh[i].offset;
258 /* Does the section require memoy allcation? */
259 else if (sh[i].flags & SHF_ALLOC)
261 /* Yup, it does. Load the hunk */
262 if (!load_hunk(file, &sh[i], virt))
264 return 0;
266 else
268 if (sh[i].size && !shown)
270 D(bug("[BOOT] ELF: section loaded at %p (Virtual addr: %p)\r\n", sh[i].addr, sh[i].addr +
271 virt - KERNEL_PHYS_BASE));
272 #if !DEBUG
273 bug("@ %p", sh[i].addr + virt - KERNEL_PHYS_BASE);
274 #endif
275 shown = 1;
281 /* For every loaded section perform the relocations */
282 for (i=0; i < eh->shnum; i++)
284 if (sh[i].type == SHT_RELA && sh[sh[i].info].addr)
286 sh[i].addr = (uint32_t)file + sh[i].offset;
287 if (!sh[i].addr || !relocate(eh, sh, i, virt - KERNEL_PHYS_BASE))
289 return 0;
295 /* Register the module */
297 mod = __claim(sizeof(module_t));
299 D(bug("[BOOT] module=%p\n", mod));
301 if (mod)
303 int i,j;
305 new_list(&mod->m_symbols);
307 mod->m_name = __claim(strlen(name)+1);
308 mod->m_lowest = 0xffffffff;
309 mod->m_highest = 0x00000000;
310 mod->m_str = NULL;
312 if (mod->m_name)
314 memcpy(mod->m_name, name, strlen(name)+1);
316 D(bug("[BOOT] name=%s\n", mod->m_name));
318 for (i=0; i < eh->shnum; i++)
320 /* If we have string table, copy it */
321 if (sh[i].type == SHT_STRTAB && i != eh->shstrndx)
323 if (!mod->m_str)
325 mod->m_str = __claim(sh[i].size);
326 memcpy(mod->m_str, (void *)sh[i].addr, sh[i].size);
328 D(bug("[BOOT] symbol table copied from %p to %p\n", sh[i].addr, mod->m_str));
332 if ((sh[i].flags & (SHF_ALLOC | SHF_EXECINSTR)) == (SHF_ALLOC | SHF_EXECINSTR))
334 if (sh[i].addr)
336 intptr_t addr = sh[i].addr + virt - KERNEL_PHYS_BASE;
338 if (addr < mod->m_lowest)
339 mod->m_lowest = addr;
340 if (addr + sh[i].size > mod->m_highest)
341 mod->m_highest = addr + sh[i].size;
346 D(bug("[BOOT] m_lowest=%p, m_highest=%p\n", mod->m_lowest, mod->m_highest));
348 for (i=0; i < eh->shnum; i++)
350 if (sh[i].addr && sh[i].type == SHT_SYMTAB)
352 struct symbol *st = (struct symbol *)sh[i].addr;
354 for (j=0; j < (sh[i].size / sizeof(struct symbol)); j++)
356 if (sh[st[j].shindex].addr && (sh[st[j].shindex].flags & (SHF_ALLOC | SHF_EXECINSTR)) == (SHF_ALLOC | SHF_EXECINSTR))
358 symbol_t *sym = __claim(sizeof(symbol_t));
359 sym->s_name = &mod->m_str[st[j].name];
360 sym->s_lowest = sh[st[j].shindex].addr + st[j].value + virt - KERNEL_PHYS_BASE;
361 sym->s_highest = sym->s_lowest + st[j].size;
363 add_head(&mod->m_symbols, (node_t *)sym);
367 break;
371 add_head(debug_info, (node_t *)mod);
375 return 1;