~ubuntu-branches/ubuntu/trusty/grub2/trusty-updates

« back to all changes in this revision

Viewing changes to grub-core/kern/ia64/dl.c

  • Committer: Package Import Robot
  • Author(s): Colin Watson
  • Date: 2012-09-13 18:02:04 UTC
  • mfrom: (1.17.15 upstream)
  • mto: (17.6.27 experimental)
  • mto: This revision was merged to the branch mainline in revision 145.
  • Revision ID: package-import@ubuntu.com-20120913180204-mojnmocbimlom4im
Tags: upstream-2.00
ImportĀ upstreamĀ versionĀ 2.00

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* dl.c - arch-dependent part of loadable module support */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2002,2004,2005,2007,2009  Free Software Foundation, Inc.
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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/>.
 
18
 */
 
19
 
 
20
#include <grub/dl.h>
 
21
#include <grub/elf.h>
 
22
#include <grub/misc.h>
 
23
#include <grub/err.h>
 
24
#include <grub/mm.h>
 
25
#include <grub/i18n.h>
 
26
 
 
27
/* Check if EHDR is a valid ELF header.  */
 
28
grub_err_t
 
29
grub_arch_dl_check_header (void *ehdr)
 
30
{
 
31
  Elf_Ehdr *e = ehdr;
 
32
 
 
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"));
 
38
 
 
39
  return GRUB_ERR_NONE;
 
40
}
 
41
 
 
42
#pragma GCC diagnostic ignored "-Wcast-align"
 
43
 
 
44
#define MASK20 ((1 << 20) - 1)
 
45
#define MASK19 ((1 << 19) - 1)
 
46
 
 
47
struct unaligned_uint32
 
48
{
 
49
  grub_uint32_t val;
 
50
}  __attribute__ ((packed));
 
51
 
 
52
static void
 
53
add_value_to_slot_20b (grub_addr_t addr, grub_uint32_t value)
 
54
{
 
55
  struct unaligned_uint32 *p;
 
56
  switch (addr & 3)
 
57
    {
 
58
    case 0:
 
59
      p = (struct unaligned_uint32 *) ((addr & ~3ULL) + 2);
 
60
      p->val = ((((((p->val >> 2) & MASK20) + value) & MASK20) << 2) 
 
61
                | (p->val & ~(MASK20 << 2)));
 
62
      break;
 
63
    case 1:
 
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)));
 
67
      break;
 
68
    case 2:
 
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)));
 
72
      break;
 
73
    }
 
74
}
 
75
 
 
76
#define MASKF21 ( ((1 << 23) - 1) & ~((1 << 7) | (1 << 8)) )
 
77
 
 
78
static grub_uint32_t
 
79
add_value_to_slot_21_real (grub_uint32_t a, grub_uint32_t value)
 
80
{
 
81
  grub_uint32_t high, mid, low, c;
 
82
  low  = (a & 0x00007f);
 
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
 
87
}
 
88
 
 
89
static void
 
90
add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value)
 
91
{
 
92
  struct unaligned_uint32 *p;
 
93
  switch (addr & 3)
 
94
    {
 
95
    case 0:
 
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));
 
98
      break;
 
99
    case 1:
 
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));
 
102
      break;
 
103
    case 2:
 
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));
 
106
      break;
 
107
    }
 
108
}
 
109
 
 
110
static const grub_uint8_t nopm[5] =
 
111
  {
 
112
    /* [MLX]       nop.m 0x0 */
 
113
    0x05, 0x00, 0x00, 0x00, 0x01
 
114
  };
 
115
 
 
116
static const grub_uint8_t jump[0x20] =
 
117
  {
 
118
    /* ld8 r16=[r15],8 */
 
119
    0x02, 0x80, 0x20, 0x1e, 0x18, 0x14,
 
120
    /* mov r14=r1;; */
 
121
    0xe0, 0x00, 0x04, 0x00, 0x42, 0x00,
 
122
    /* nop.i 0x0 */
 
123
    0x00, 0x00, 0x04, 0x00,
 
124
    /* ld8 r1=[r15] */
 
125
    0x11, 0x08, 0x00, 0x1e, 0x18, 0x10,
 
126
    /* mov b6=r16 */
 
127
    0x60, 0x80, 0x04, 0x80, 0x03, 0x00,
 
128
    /* br.few b6;; */
 
129
    0x60, 0x00, 0x80, 0x00
 
130
  };
 
131
 
 
132
struct ia64_trampoline
 
133
{
 
134
  /* nop.m */
 
135
  grub_uint8_t nop[5];
 
136
  /* movl r15 = addr*/
 
137
  grub_uint8_t addr_hi[6];
 
138
  grub_uint8_t e0;
 
139
  grub_uint8_t addr_lo[4];
 
140
  grub_uint8_t jump[0x20];
 
141
};
 
142
 
 
143
static void
 
144
make_trampoline (struct ia64_trampoline *tr, grub_uint64_t addr)
 
145
{
 
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;
 
155
  tr->e0 = 0xe0;
 
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));
 
162
}
 
163
 
 
164
/* Relocate symbols.  */
 
165
grub_err_t
 
166
grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
 
167
{
 
168
  Elf_Ehdr *e = ehdr;
 
169
  Elf_Shdr *s;
 
170
  Elf_Word entsize;
 
171
  unsigned i;
 
172
  grub_uint64_t *gp, *gpptr;
 
173
  struct ia64_trampoline *tr;
 
174
 
 
175
  gp = (grub_uint64_t *) mod->base;
 
176
  gpptr = (grub_uint64_t *) mod->got;
 
177
  tr = mod->tramp;
 
178
 
 
179
  /* Find a symbol table.  */
 
180
  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
 
181
       i < e->e_shnum;
 
182
       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
 
183
    if (s->sh_type == SHT_SYMTAB)
 
184
      break;
 
185
 
 
186
  if (i == e->e_shnum)
 
187
    return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table"));
 
188
 
 
189
  entsize = s->sh_entsize;
 
190
 
 
191
  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
 
192
       i < e->e_shnum;
 
193
       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
 
194
    if (s->sh_type == SHT_RELA)
 
195
      {
 
196
        grub_dl_segment_t seg;
 
197
 
 
198
        /* Find the target segment.  */
 
199
        for (seg = mod->segment; seg; seg = seg->next)
 
200
          if (seg->section == s->sh_info)
 
201
            break;
 
202
 
 
203
        if (seg)
 
204
          {
 
205
            Elf_Rela *rel, *max;
 
206
 
 
207
            for (rel = (Elf_Rela *) ((char *) e + s->sh_offset),
 
208
                   max = rel + s->sh_size / s->sh_entsize;
 
209
                 rel < max;
 
210
                 rel++)
 
211
              {
 
212
                grub_addr_t addr;
 
213
                Elf_Sym *sym;
 
214
                grub_uint64_t value;
 
215
 
 
216
                if (seg->size < (rel->r_offset & ~3))
 
217
                  return grub_error (GRUB_ERR_BAD_MODULE,
 
218
                                     "reloc offset is out of the segment");
 
219
 
 
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));
 
223
 
 
224
                /* On the PPC the value does not have an explicit
 
225
                   addend, add it.  */
 
226
                value = sym->st_value + rel->r_addend;
 
227
 
 
228
                switch (ELF_R_TYPE (rel->r_info))
 
229
                  {
 
230
                  case R_IA64_PCREL21B:
 
231
                    {
 
232
                      grub_uint64_t noff;
 
233
                      make_trampoline (tr, value);
 
234
                      noff = ((char *) tr - (char *) (addr & ~3)) >> 4;
 
235
                      tr++;
 
236
                      if (noff & ~MASK19)
 
237
                        return grub_error (GRUB_ERR_BAD_OS,
 
238
                                           "trampoline offset too big (%lx)", noff);
 
239
                      add_value_to_slot_20b (addr, noff);
 
240
                    }
 
241
                    break;
 
242
                  case R_IA64_SEGREL64LSB:
 
243
                    *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr;
 
244
                    break;
 
245
                  case R_IA64_FPTR64LSB:
 
246
                  case R_IA64_DIR64LSB:
 
247
                    *(grub_uint64_t *) addr += value;
 
248
                    break;
 
249
                  case R_IA64_PCREL64LSB:
 
250
                    *(grub_uint64_t *) addr += value - addr;
 
251
                    break;
 
252
                  case R_IA64_GPREL22:
 
253
                    add_value_to_slot_21 (addr, value - (grub_addr_t) gp);
 
254
                    break;
 
255
 
 
256
                  case R_IA64_LTOFF22X:
 
257
                  case R_IA64_LTOFF22:
 
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:
 
261
                    *gpptr = value;
 
262
                    add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) gp);
 
263
                    gpptr++;
 
264
                    break;
 
265
 
 
266
                    /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV.  */
 
267
                  case R_IA64_LDXMOV:
 
268
                    break;
 
269
                  default:
 
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));
 
273
                  }
 
274
              }
 
275
          }
 
276
      }
 
277
 
 
278
  return GRUB_ERR_NONE;
 
279
}