~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, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

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
}