1
/* dl.c - arch-dependent part of loadable module support */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2002,2004,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>
25
#include <grub/i18n.h>
27
/* Check if EHDR is a valid ELF header. */
29
grub_arch_dl_check_header (void *ehdr)
33
/* Check the magic numbers. */
34
if (e->e_ident[EI_CLASS] != ELFCLASS64
35
|| e->e_ident[EI_DATA] != ELFDATA2LSB
36
|| e->e_machine != EM_IA_64)
37
return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
42
#pragma GCC diagnostic ignored "-Wcast-align"
44
#define MASK20 ((1 << 20) - 1)
45
#define MASK19 ((1 << 19) - 1)
47
struct unaligned_uint32
50
} __attribute__ ((packed));
53
add_value_to_slot_20b (grub_addr_t addr, grub_uint32_t value)
55
struct unaligned_uint32 *p;
59
p = (struct unaligned_uint32 *) ((addr & ~3ULL) + 2);
60
p->val = ((((((p->val >> 2) & MASK20) + value) & MASK20) << 2)
61
| (p->val & ~(MASK20 << 2)));
64
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 7);
65
p->val = ((((((p->val >> 3) & MASK20) + value) & MASK20) << 3)
66
| (p->val & ~(MASK20 << 3)));
69
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 12);
70
p->val = ((((((p->val >> 4) & MASK20) + value) & MASK20) << 4)
71
| (p->val & ~(MASK20 << 4)));
76
#define MASKF21 ( ((1 << 23) - 1) & ~((1 << 7) | (1 << 8)) )
79
add_value_to_slot_21_real (grub_uint32_t a, grub_uint32_t value)
81
grub_uint32_t high, mid, low, c;
83
mid = (a & 0x7fc000) >> 7;
84
high = (a & 0x003e00) << 7;
85
c = (low | mid | high) + value;
86
return (c & 0x7f) | ((c << 7) & 0x7fc000) | ((c >> 7) & 0x0003e00); //0x003e00
90
add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value)
92
struct unaligned_uint32 *p;
96
p = (struct unaligned_uint32 *) ((addr & ~3ULL) + 2);
97
p->val = ((add_value_to_slot_21_real (((p->val >> 2) & MASKF21), value) & MASKF21) << 2) | (p->val & ~(MASKF21 << 2));
100
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 7);
101
p->val = ((add_value_to_slot_21_real (((p->val >> 3) & MASKF21), value) & MASKF21) << 3) | (p->val & ~(MASKF21 << 3));
104
p = (struct unaligned_uint32 *) ((grub_uint8_t *) (addr & ~3ULL) + 12);
105
p->val = ((add_value_to_slot_21_real (((p->val >> 4) & MASKF21), value) & MASKF21) << 4) | (p->val & ~(MASKF21 << 4));
110
static const grub_uint8_t nopm[5] =
112
/* [MLX] nop.m 0x0 */
113
0x05, 0x00, 0x00, 0x00, 0x01
116
static const grub_uint8_t jump[0x20] =
118
/* ld8 r16=[r15],8 */
119
0x02, 0x80, 0x20, 0x1e, 0x18, 0x14,
121
0xe0, 0x00, 0x04, 0x00, 0x42, 0x00,
123
0x00, 0x00, 0x04, 0x00,
125
0x11, 0x08, 0x00, 0x1e, 0x18, 0x10,
127
0x60, 0x80, 0x04, 0x80, 0x03, 0x00,
129
0x60, 0x00, 0x80, 0x00
132
struct ia64_trampoline
137
grub_uint8_t addr_hi[6];
139
grub_uint8_t addr_lo[4];
140
grub_uint8_t jump[0x20];
144
make_trampoline (struct ia64_trampoline *tr, grub_uint64_t addr)
146
COMPILE_TIME_ASSERT (sizeof (struct ia64_trampoline)
147
== GRUB_IA64_DL_TRAMP_SIZE);
148
grub_memcpy (tr->nop, nopm, sizeof (tr->nop));
149
tr->addr_hi[0] = ((addr & 0xc00000) >> 16);
150
tr->addr_hi[1] = (addr >> 24) & 0xff;
151
tr->addr_hi[2] = (addr >> 32) & 0xff;
152
tr->addr_hi[3] = (addr >> 40) & 0xff;
153
tr->addr_hi[4] = (addr >> 48) & 0xff;
154
tr->addr_hi[5] = (addr >> 56) & 0xff;
156
tr->addr_lo[0] = ((addr & 0x000f) << 4) | 0x01;
157
tr->addr_lo[1] = (((addr & 0x0070) >> 4) | ((addr & 0x070000) >> 11)
158
| ((addr & 0x200000) >> 17));
159
tr->addr_lo[2] = ((addr & 0x1f80) >> 5) | ((addr & 0x180000) >> 19);
160
tr->addr_lo[3] = ((addr & 0xe000) >> 13) | 0x60;
161
grub_memcpy (tr->jump, jump, sizeof (tr->jump));
164
/* Relocate symbols. */
166
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
172
grub_uint64_t *gp, *gpptr;
173
struct ia64_trampoline *tr;
175
gp = (grub_uint64_t *) mod->base;
176
gpptr = (grub_uint64_t *) mod->got;
179
/* Find a symbol table. */
180
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
182
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
183
if (s->sh_type == SHT_SYMTAB)
187
return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
189
entsize = s->sh_entsize;
191
for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
193
i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
194
if (s->sh_type == SHT_RELA)
196
grub_dl_segment_t seg;
198
/* Find the target segment. */
199
for (seg = mod->segment; seg; seg = seg->next)
200
if (seg->section == s->sh_info)
207
for (rel = (Elf_Rela *) ((char *) e + s->sh_offset),
208
max = rel + s->sh_size / s->sh_entsize;
216
if (seg->size < (rel->r_offset & ~3))
217
return grub_error (GRUB_ERR_BAD_MODULE,
218
"reloc offset is out of the segment");
220
addr = (grub_addr_t) seg->addr + rel->r_offset;
221
sym = (Elf_Sym *) ((char *) mod->symtab
222
+ entsize * ELF_R_SYM (rel->r_info));
224
/* On the PPC the value does not have an explicit
226
value = sym->st_value + rel->r_addend;
228
switch (ELF_R_TYPE (rel->r_info))
230
case R_IA64_PCREL21B:
233
make_trampoline (tr, value);
234
noff = ((char *) tr - (char *) (addr & ~3)) >> 4;
237
return grub_error (GRUB_ERR_BAD_OS,
238
"trampoline offset too big (%lx)", noff);
239
add_value_to_slot_20b (addr, noff);
242
case R_IA64_SEGREL64LSB:
243
*(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr;
245
case R_IA64_FPTR64LSB:
246
case R_IA64_DIR64LSB:
247
*(grub_uint64_t *) addr += value;
249
case R_IA64_PCREL64LSB:
250
*(grub_uint64_t *) addr += value - addr;
253
add_value_to_slot_21 (addr, value - (grub_addr_t) gp);
256
case R_IA64_LTOFF22X:
258
if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
259
value = *(grub_uint64_t *) sym->st_value + rel->r_addend;
260
case R_IA64_LTOFF_FPTR22:
262
add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) gp);
266
/* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */
270
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
271
N_("relocation 0x%x is not implemented yet"),
272
ELF_R_TYPE (rel->r_info));
278
return GRUB_ERR_NONE;