~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/kern/elf.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* elf.c - load ELF files */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2003,2004,2005,2006,2007,2008,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/err.h>
 
21
#include <grub/elf.h>
 
22
#include <grub/elfload.h>
 
23
#include <grub/file.h>
 
24
#include <grub/misc.h>
 
25
#include <grub/mm.h>
 
26
 
 
27
/* Check if EHDR is a valid ELF header.  */
 
28
static grub_err_t
 
29
grub_elf_check_header (grub_elf_t elf)
 
30
{
 
31
  Elf32_Ehdr *e = &elf->ehdr.ehdr32;
 
32
 
 
33
  if (e->e_ident[EI_MAG0] != ELFMAG0
 
34
      || e->e_ident[EI_MAG1] != ELFMAG1
 
35
      || e->e_ident[EI_MAG2] != ELFMAG2
 
36
      || e->e_ident[EI_MAG3] != ELFMAG3
 
37
      || e->e_ident[EI_VERSION] != EV_CURRENT
 
38
      || e->e_version != EV_CURRENT)
 
39
    return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
 
40
 
 
41
  return GRUB_ERR_NONE;
 
42
}
 
43
 
 
44
grub_err_t
 
45
grub_elf_close (grub_elf_t elf)
 
46
{
 
47
  grub_file_t file = elf->file;
 
48
 
 
49
  grub_free (elf->phdrs);
 
50
  grub_free (elf);
 
51
 
 
52
  if (file)
 
53
    grub_file_close (file);
 
54
 
 
55
  return grub_errno;
 
56
}
 
57
 
 
58
grub_elf_t
 
59
grub_elf_file (grub_file_t file)
 
60
{
 
61
  grub_elf_t elf;
 
62
 
 
63
  elf = grub_zalloc (sizeof (*elf));
 
64
  if (! elf)
 
65
    return 0;
 
66
 
 
67
  elf->file = file;
 
68
 
 
69
  if (grub_file_seek (elf->file, 0) == (grub_off_t) -1)
 
70
    goto fail;
 
71
 
 
72
  if (grub_file_read (elf->file, &elf->ehdr, sizeof (elf->ehdr))
 
73
      != sizeof (elf->ehdr))
 
74
    {
 
75
      grub_error_push ();
 
76
      grub_error (GRUB_ERR_READ_ERROR, "cannot read ELF header");
 
77
      goto fail;
 
78
    }
 
79
 
 
80
  if (grub_elf_check_header (elf))
 
81
    goto fail;
 
82
 
 
83
  return elf;
 
84
 
 
85
fail:
 
86
  grub_free (elf->phdrs);
 
87
  grub_free (elf);
 
88
  return 0;
 
89
}
 
90
 
 
91
grub_elf_t
 
92
grub_elf_open (const char *name)
 
93
{
 
94
  grub_file_t file;
 
95
  grub_elf_t elf;
 
96
 
 
97
  file = grub_file_open (name);
 
98
  if (! file)
 
99
    return 0;
 
100
 
 
101
  elf = grub_elf_file (file);
 
102
  if (! elf)
 
103
    grub_file_close (file);
 
104
 
 
105
  return elf;
 
106
}
 
107
 
 
108
 
 
109
/* 32-bit */
 
110
 
 
111
int
 
112
grub_elf_is_elf32 (grub_elf_t elf)
 
113
{
 
114
  return elf->ehdr.ehdr32.e_ident[EI_CLASS] == ELFCLASS32;
 
115
}
 
116
 
 
117
static grub_err_t
 
118
grub_elf32_load_phdrs (grub_elf_t elf)
 
119
{
 
120
  grub_ssize_t phdrs_size;
 
121
 
 
122
  phdrs_size = elf->ehdr.ehdr32.e_phnum * elf->ehdr.ehdr32.e_phentsize;
 
123
 
 
124
  grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
 
125
                (unsigned long long) elf->ehdr.ehdr32.e_phoff,
 
126
                (unsigned long) phdrs_size);
 
127
 
 
128
  elf->phdrs = grub_malloc (phdrs_size);
 
129
  if (! elf->phdrs)
 
130
    return grub_errno;
 
131
 
 
132
  if ((grub_file_seek (elf->file, elf->ehdr.ehdr32.e_phoff) == (grub_off_t) -1)
 
133
      || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size))
 
134
    {
 
135
      grub_error_push ();
 
136
      return grub_error (GRUB_ERR_READ_ERROR, "cannot read program headers");
 
137
    }
 
138
 
 
139
  return GRUB_ERR_NONE;
 
140
}
 
141
 
 
142
grub_err_t
 
143
grub_elf32_phdr_iterate (grub_elf_t elf,
 
144
                         int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf32_Phdr *, void *),
 
145
                         void *hook_arg)
 
146
{
 
147
  Elf32_Phdr *phdrs;
 
148
  unsigned int i;
 
149
 
 
150
  if (! elf->phdrs)
 
151
    if (grub_elf32_load_phdrs (elf))
 
152
      return grub_errno;
 
153
  phdrs = elf->phdrs;
 
154
 
 
155
  for (i = 0; i < elf->ehdr.ehdr32.e_phnum; i++)
 
156
    {
 
157
      Elf32_Phdr *phdr = phdrs + i;
 
158
      grub_dprintf ("elf",
 
159
                    "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
 
160
                    "filesz %lx\n",
 
161
                    i, phdr->p_type,
 
162
                    (unsigned long) phdr->p_paddr,
 
163
                    (unsigned long) phdr->p_memsz,
 
164
                    (unsigned long) phdr->p_filesz);
 
165
      if (hook (elf, phdr, hook_arg))
 
166
        break;
 
167
    }
 
168
 
 
169
  return grub_errno;
 
170
}
 
171
 
 
172
/* Calculate the amount of memory spanned by the segments.  */
 
173
grub_size_t
 
174
grub_elf32_size (grub_elf_t elf, Elf32_Addr *base, grub_uint32_t *max_align)
 
175
{
 
176
  Elf32_Addr segments_start = (Elf32_Addr) -1;
 
177
  Elf32_Addr segments_end = 0;
 
178
  int nr_phdrs = 0;
 
179
  grub_uint32_t curr_align = 1;
 
180
 
 
181
  /* Run through the program headers to calculate the total memory size we
 
182
   * should claim.  */
 
183
  auto int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf, Elf32_Phdr *phdr, void *_arg);
 
184
  int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf  __attribute__ ((unused)),
 
185
                                 Elf32_Phdr *phdr,
 
186
                                 void *_arg __attribute__ ((unused)))
 
187
    {
 
188
      /* Only consider loadable segments.  */
 
189
      if (phdr->p_type != PT_LOAD)
 
190
        return 0;
 
191
      nr_phdrs++;
 
192
      if (phdr->p_paddr < segments_start)
 
193
        segments_start = phdr->p_paddr;
 
194
      if (phdr->p_paddr + phdr->p_memsz > segments_end)
 
195
        segments_end = phdr->p_paddr + phdr->p_memsz;
 
196
      if (curr_align < phdr->p_align)
 
197
        curr_align = phdr->p_align;
 
198
      return 0;
 
199
    }
 
200
 
 
201
  grub_elf32_phdr_iterate (elf, calcsize, 0);
 
202
 
 
203
  if (base)
 
204
    *base = 0;
 
205
 
 
206
  if (nr_phdrs == 0)
 
207
    {
 
208
      grub_error (GRUB_ERR_BAD_OS, "no program headers present");
 
209
      return 0;
 
210
    }
 
211
 
 
212
  if (segments_end < segments_start)
 
213
    {
 
214
      /* Very bad addresses.  */
 
215
      grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
 
216
      return 0;
 
217
    }
 
218
 
 
219
  if (base)
 
220
    *base = segments_start;
 
221
  if (max_align)
 
222
    *max_align = curr_align;
 
223
  return segments_end - segments_start;
 
224
}
 
225
 
 
226
/* Load every loadable segment into memory specified by `_load_hook'.  */
 
227
grub_err_t
 
228
grub_elf32_load (grub_elf_t _elf, grub_elf32_load_hook_t _load_hook,
 
229
                 grub_addr_t *base, grub_size_t *size)
 
230
{
 
231
  grub_addr_t load_base = (grub_addr_t) -1ULL;
 
232
  grub_size_t load_size = 0;
 
233
  grub_err_t err;
 
234
 
 
235
  auto int NESTED_FUNC_ATTR grub_elf32_load_segment (grub_elf_t elf, Elf32_Phdr *phdr, void *hook);
 
236
  int NESTED_FUNC_ATTR grub_elf32_load_segment (grub_elf_t elf, Elf32_Phdr *phdr, void *hook)
 
237
  {
 
238
    grub_elf32_load_hook_t load_hook = (grub_elf32_load_hook_t) hook;
 
239
    grub_addr_t load_addr;
 
240
    int do_load = 1;
 
241
 
 
242
    load_addr = phdr->p_paddr;
 
243
    if (load_hook && load_hook (phdr, &load_addr, &do_load))
 
244
      return 1;
 
245
 
 
246
    if (! do_load)
 
247
      return 0;
 
248
 
 
249
    if (load_addr < load_base)
 
250
      load_base = load_addr;
 
251
 
 
252
    grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
 
253
                  (unsigned long long) load_addr,
 
254
                  (unsigned long long) phdr->p_memsz);
 
255
 
 
256
    if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
 
257
      {
 
258
        grub_error_push ();
 
259
        return grub_error (GRUB_ERR_BAD_OS,
 
260
                           "invalid offset in program header");
 
261
      }
 
262
 
 
263
    if (phdr->p_filesz)
 
264
      {
 
265
        grub_ssize_t read;
 
266
        read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz);
 
267
        if (read != (grub_ssize_t) phdr->p_filesz)
 
268
          {
 
269
            /* XXX How can we free memory from `load_hook'? */
 
270
            grub_error_push ();
 
271
            return grub_error (GRUB_ERR_BAD_OS,
 
272
                               "couldn't read segment from file: "
 
273
                               "wanted 0x%lx bytes; read 0x%lx bytes",
 
274
                               phdr->p_filesz, read);
 
275
          }
 
276
      }
 
277
 
 
278
    if (phdr->p_filesz < phdr->p_memsz)
 
279
      grub_memset ((void *) (long) (load_addr + phdr->p_filesz),
 
280
                   0, phdr->p_memsz - phdr->p_filesz);
 
281
 
 
282
    load_size += phdr->p_memsz;
 
283
 
 
284
    return 0;
 
285
  }
 
286
 
 
287
  err = grub_elf32_phdr_iterate (_elf, grub_elf32_load_segment, _load_hook);
 
288
 
 
289
  if (base)
 
290
    *base = load_base;
 
291
  if (size)
 
292
    *size = load_size;
 
293
 
 
294
  return err;
 
295
}
 
296
 
 
297
 
 
298
/* 64-bit */
 
299
 
 
300
int
 
301
grub_elf_is_elf64 (grub_elf_t elf)
 
302
{
 
303
  return elf->ehdr.ehdr64.e_ident[EI_CLASS] == ELFCLASS64;
 
304
}
 
305
 
 
306
static grub_err_t
 
307
grub_elf64_load_phdrs (grub_elf_t elf)
 
308
{
 
309
  grub_ssize_t phdrs_size;
 
310
 
 
311
  phdrs_size = elf->ehdr.ehdr64.e_phnum * elf->ehdr.ehdr64.e_phentsize;
 
312
 
 
313
  grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
 
314
                (unsigned long long) elf->ehdr.ehdr64.e_phoff,
 
315
                (unsigned long) phdrs_size);
 
316
 
 
317
  elf->phdrs = grub_malloc (phdrs_size);
 
318
  if (! elf->phdrs)
 
319
    return grub_errno;
 
320
 
 
321
  if ((grub_file_seek (elf->file, elf->ehdr.ehdr64.e_phoff) == (grub_off_t) -1)
 
322
      || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size))
 
323
    {
 
324
      grub_error_push ();
 
325
      return grub_error (GRUB_ERR_READ_ERROR, "cannot read program headers");
 
326
    }
 
327
 
 
328
  return GRUB_ERR_NONE;
 
329
}
 
330
 
 
331
grub_err_t
 
332
grub_elf64_phdr_iterate (grub_elf_t elf,
 
333
                         int NESTED_FUNC_ATTR (*hook) (grub_elf_t, Elf64_Phdr *, void *),
 
334
                         void *hook_arg)
 
335
{
 
336
  Elf64_Phdr *phdrs;
 
337
  unsigned int i;
 
338
 
 
339
  if (! elf->phdrs)
 
340
    if (grub_elf64_load_phdrs (elf))
 
341
      return grub_errno;
 
342
  phdrs = elf->phdrs;
 
343
 
 
344
  for (i = 0; i < elf->ehdr.ehdr64.e_phnum; i++)
 
345
    {
 
346
      Elf64_Phdr *phdr = phdrs + i;
 
347
      grub_dprintf ("elf",
 
348
                    "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
 
349
                    "filesz %lx\n",
 
350
                    i, phdr->p_type,
 
351
                    (unsigned long) phdr->p_paddr,
 
352
                    (unsigned long) phdr->p_memsz,
 
353
                    (unsigned long) phdr->p_filesz);
 
354
      if (hook (elf, phdr, hook_arg))
 
355
        break;
 
356
    }
 
357
 
 
358
  return grub_errno;
 
359
}
 
360
 
 
361
/* Calculate the amount of memory spanned by the segments.  */
 
362
grub_size_t
 
363
grub_elf64_size (grub_elf_t elf, Elf64_Addr *base, grub_uint64_t *max_align)
 
364
{
 
365
  Elf64_Addr segments_start = (Elf64_Addr) -1;
 
366
  Elf64_Addr segments_end = 0;
 
367
  int nr_phdrs = 0;
 
368
  grub_uint64_t curr_align = 1;
 
369
 
 
370
  /* Run through the program headers to calculate the total memory size we
 
371
   * should claim.  */
 
372
  auto int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf, Elf64_Phdr *phdr, void *_arg);
 
373
  int NESTED_FUNC_ATTR calcsize (grub_elf_t _elf  __attribute__ ((unused)),
 
374
                                 Elf64_Phdr *phdr,
 
375
                                 void *_arg __attribute__ ((unused)))
 
376
    {
 
377
      /* Only consider loadable segments.  */
 
378
      if (phdr->p_type != PT_LOAD)
 
379
        return 0;
 
380
      nr_phdrs++;
 
381
      if (phdr->p_paddr < segments_start)
 
382
        segments_start = phdr->p_paddr;
 
383
      if (phdr->p_paddr + phdr->p_memsz > segments_end)
 
384
        segments_end = phdr->p_paddr + phdr->p_memsz;
 
385
      if (curr_align < phdr->p_align)
 
386
        curr_align = phdr->p_align;
 
387
      return 0;
 
388
    }
 
389
 
 
390
  grub_elf64_phdr_iterate (elf, calcsize, 0);
 
391
 
 
392
  if (base)
 
393
    *base = 0;
 
394
 
 
395
  if (nr_phdrs == 0)
 
396
    {
 
397
      grub_error (GRUB_ERR_BAD_OS, "no program headers present");
 
398
      return 0;
 
399
    }
 
400
 
 
401
  if (segments_end < segments_start)
 
402
    {
 
403
      /* Very bad addresses.  */
 
404
      grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
 
405
      return 0;
 
406
    }
 
407
 
 
408
  if (base)
 
409
    *base = segments_start;
 
410
  if (max_align)
 
411
    *max_align = curr_align;
 
412
  return segments_end - segments_start;
 
413
}
 
414
 
 
415
/* Load every loadable segment into memory specified by `_load_hook'.  */
 
416
grub_err_t
 
417
grub_elf64_load (grub_elf_t _elf, grub_elf64_load_hook_t _load_hook,
 
418
                 grub_addr_t *base, grub_size_t *size)
 
419
{
 
420
  grub_addr_t load_base = (grub_addr_t) -1ULL;
 
421
  grub_size_t load_size = 0;
 
422
  grub_err_t err;
 
423
 
 
424
  auto int NESTED_FUNC_ATTR grub_elf64_load_segment (grub_elf_t elf, Elf64_Phdr *phdr,
 
425
                                                     void *hook);
 
426
  int NESTED_FUNC_ATTR grub_elf64_load_segment (grub_elf_t elf, Elf64_Phdr *phdr, void *hook)
 
427
  {
 
428
    grub_elf64_load_hook_t load_hook = (grub_elf64_load_hook_t) hook;
 
429
    grub_addr_t load_addr;
 
430
    int do_load = 1;
 
431
 
 
432
    load_addr = phdr->p_paddr;
 
433
    if (load_hook && load_hook (phdr, &load_addr, &do_load))
 
434
      return 1;
 
435
 
 
436
    if (! do_load)
 
437
      return 0;
 
438
 
 
439
    if (load_addr < load_base)
 
440
      load_base = load_addr;
 
441
 
 
442
    grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
 
443
                  (unsigned long long) load_addr,
 
444
                  (unsigned long long) phdr->p_memsz);
 
445
 
 
446
    if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
 
447
      {
 
448
        grub_error_push ();
 
449
        return grub_error (GRUB_ERR_BAD_OS,
 
450
                           "invalid offset in program header");
 
451
      }
 
452
 
 
453
    if (phdr->p_filesz)
 
454
      {
 
455
        grub_ssize_t read;
 
456
        read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz);
 
457
        if (read != (grub_ssize_t) phdr->p_filesz)
 
458
          {
 
459
            /* XXX How can we free memory from `load_hook'?  */
 
460
            grub_error_push ();
 
461
            return grub_error (GRUB_ERR_BAD_OS,
 
462
                              "couldn't read segment from file: "
 
463
                              "wanted 0x%lx bytes; read 0x%lx bytes",
 
464
                              phdr->p_filesz, read);
 
465
          }
 
466
      }
 
467
 
 
468
    if (phdr->p_filesz < phdr->p_memsz)
 
469
      grub_memset ((void *) (long) (load_addr + phdr->p_filesz),
 
470
                   0, phdr->p_memsz - phdr->p_filesz);
 
471
 
 
472
    load_size += phdr->p_memsz;
 
473
 
 
474
    return 0;
 
475
  }
 
476
 
 
477
  err = grub_elf64_phdr_iterate (_elf, grub_elf64_load_segment, _load_hook);
 
478
 
 
479
  if (base)
 
480
    *base = load_base;
 
481
  if (size)
 
482
    *size = load_size;
 
483
 
 
484
  return err;
 
485
}