1
/* dl-386.c - arch-dependent part of loadable module support */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2002,2005,2007,2009 Free Software Foundation, Inc.
6
* GRUB is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
11
* GRUB is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
22
#include <grub/misc.h>
24
#include <grub/cpu/types.h>
27
/* Dummy __gnu_local_gp. Resolved by linker. */
28
static char __gnu_local_gp_dummy;
30
/* Check if EHDR is a valid ELF header. */
32
grub_arch_dl_check_header (void *ehdr)
36
/* Check the magic numbers. */
37
#ifdef WORDS_BIGENDIAN
38
if (e->e_ident[EI_CLASS] != ELFCLASS32
39
|| e->e_ident[EI_DATA] != ELFDATA2MSB
40
|| e->e_machine != EM_MIPS)
42
if (e->e_ident[EI_CLASS] != ELFCLASS32
43
|| e->e_ident[EI_DATA] != ELFDATA2LSB
44
|| e->e_machine != EM_MIPS)
46
return grub_error (GRUB_ERR_BAD_OS, "invalid arch specific ELF magic");
51
/* Relocate symbols. */
53
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
59
grub_size_t gp_size = 0;
60
/* FIXME: suboptimal. */
61
grub_uint32_t *gp, *gpptr;
64
/* Find a symbol table. */
65
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
67
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
68
if (s->sh_type == SHT_SYMTAB)
72
return grub_error (GRUB_ERR_BAD_MODULE, "no symtab found");
74
entsize = s->sh_entsize;
77
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
79
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
80
if (s->sh_type == SHT_MIPS_REGINFO)
84
return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found");
86
gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5];
88
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
90
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
91
if (s->sh_type == SHT_REL)
93
grub_dl_segment_t seg;
95
/* Find the target segment. */
96
for (seg = mod->segment; seg; seg = seg->next)
97
if (seg->section == s->sh_info)
104
for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
105
max = rel + s->sh_size / s->sh_entsize;
108
switch (ELF_R_TYPE (rel->r_info))
119
if (gp_size > 0x08000)
120
return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n");
122
gpptr = gp = grub_malloc (gp_size);
126
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
128
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
129
if (s->sh_type == SHT_REL)
131
grub_dl_segment_t seg;
133
/* Find the target segment. */
134
for (seg = mod->segment; seg; seg = seg->next)
135
if (seg->section == s->sh_info)
142
for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
143
max = rel + s->sh_size / s->sh_entsize;
150
if (seg->size < rel->r_offset)
151
return grub_error (GRUB_ERR_BAD_MODULE,
152
"reloc offset is out of the segment");
154
addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
155
sym = (Elf_Sym *) ((char *) mod->symtab
156
+ entsize * ELF_R_SYM (rel->r_info));
157
if (sym->st_value == (grub_addr_t) &__gnu_local_gp_dummy)
158
sym->st_value = (grub_addr_t) gp;
160
switch (ELF_R_TYPE (rel->r_info))
167
/* Handle partner lo16 relocation. Lower part is
168
treated as signed. Hence add 0x8000 to compensate.
170
value = (*(grub_uint16_t *) addr << 16)
171
+ sym->st_value + 0x8000;
172
for (rel2 = rel + 1; rel2 < max; rel2++)
173
if (ELF_R_SYM (rel2->r_info)
174
== ELF_R_SYM (rel->r_info)
175
&& ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
177
value += *(grub_int16_t *)
178
((char *) seg->addr + rel2->r_offset);
181
*(grub_uint16_t *) addr = (value >> 16) & 0xffff;
185
*(grub_uint16_t *) addr += (sym->st_value) & 0xffff;
188
*(grub_uint32_t *) addr += sym->st_value;
191
*(grub_uint32_t *) addr = sym->st_value
192
+ *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp;
199
raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
201
value += sym->st_value;
202
raw = (value >> 2) & 0x3ffffff;
204
*(grub_uint32_t *) addr =
205
raw | ((*(grub_uint32_t *) addr) & 0xfc000000);
211
*gpptr = sym->st_value + *(grub_uint16_t *) addr;
212
*(grub_uint16_t *) addr
213
= sizeof (grub_uint32_t) * (gpptr - gp);
219
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
220
"Unknown relocation type %d\n",
221
ELF_R_TYPE (rel->r_info));
229
return GRUB_ERR_NONE;
233
grub_arch_dl_init_linker (void)
235
grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0);