1 /* elf.c - load ELF files */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2004,2005,2006,2007,2008 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/elfload.h>
23 #include <grub/file.h>
24 #include <grub/gzio.h>
25 #include <grub/misc.h>
28 /* Check if EHDR is a valid ELF header. */
30 grub_elf_check_header (grub_elf_t elf
)
32 Elf32_Ehdr
*e
= &elf
->ehdr
.ehdr32
;
34 if (e
->e_ident
[EI_MAG0
] != ELFMAG0
35 || e
->e_ident
[EI_MAG1
] != ELFMAG1
36 || e
->e_ident
[EI_MAG2
] != ELFMAG2
37 || e
->e_ident
[EI_MAG3
] != ELFMAG3
38 || e
->e_ident
[EI_VERSION
] != EV_CURRENT
39 || e
->e_version
!= EV_CURRENT
)
40 return grub_error (GRUB_ERR_BAD_OS
, "invalid arch independent ELF magic");
46 grub_elf_close (grub_elf_t elf
)
48 grub_file_t file
= elf
->file
;
50 grub_free (elf
->phdrs
);
54 grub_file_close (file
);
60 grub_elf_file (grub_file_t file
)
64 elf
= grub_malloc (sizeof (*elf
));
71 if (grub_file_seek (elf
->file
, 0) == (grub_off_t
) -1)
74 if (grub_file_read (elf
->file
, &elf
->ehdr
, sizeof (elf
->ehdr
))
75 != sizeof (elf
->ehdr
))
78 grub_error (GRUB_ERR_READ_ERROR
, "Cannot read ELF header.");
82 if (grub_elf_check_header (elf
))
88 grub_free (elf
->phdrs
);
94 grub_elf_open (const char *name
)
99 file
= grub_gzfile_open (name
, 1);
103 elf
= grub_elf_file (file
);
105 grub_file_close (file
);
114 grub_elf_is_elf32 (grub_elf_t elf
)
116 return elf
->ehdr
.ehdr32
.e_ident
[EI_CLASS
] == ELFCLASS32
;
120 grub_elf32_load_phdrs (grub_elf_t elf
)
122 grub_ssize_t phdrs_size
;
124 phdrs_size
= elf
->ehdr
.ehdr32
.e_phnum
* elf
->ehdr
.ehdr32
.e_phentsize
;
126 grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
127 (unsigned long long) elf
->ehdr
.ehdr32
.e_phoff
,
128 (unsigned long) phdrs_size
);
130 elf
->phdrs
= grub_malloc (phdrs_size
);
134 if ((grub_file_seek (elf
->file
, elf
->ehdr
.ehdr32
.e_phoff
) == (grub_off_t
) -1)
135 || (grub_file_read (elf
->file
, elf
->phdrs
, phdrs_size
) != phdrs_size
))
138 return grub_error (GRUB_ERR_READ_ERROR
, "Cannot read program headers");
141 return GRUB_ERR_NONE
;
145 grub_elf32_phdr_iterate (grub_elf_t elf
,
146 int NESTED_FUNC_ATTR (*hook
) (grub_elf_t
, Elf32_Phdr
*, void *),
153 if (grub_elf32_load_phdrs (elf
))
157 for (i
= 0; i
< elf
->ehdr
.ehdr32
.e_phnum
; i
++)
159 Elf32_Phdr
*phdr
= phdrs
+ i
;
161 "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
164 (unsigned long) phdr
->p_paddr
,
165 (unsigned long) phdr
->p_memsz
,
166 (unsigned long) phdr
->p_filesz
);
167 if (hook (elf
, phdr
, hook_arg
))
174 /* Calculate the amount of memory spanned by the segments. */
176 grub_elf32_size (grub_elf_t elf
)
178 Elf32_Addr segments_start
= (Elf32_Addr
) -1;
179 Elf32_Addr segments_end
= 0;
182 /* Run through the program headers to calculate the total memory size we
184 auto int NESTED_FUNC_ATTR
calcsize (grub_elf_t _elf
, Elf32_Phdr
*phdr
, void *_arg
);
185 int NESTED_FUNC_ATTR
calcsize (grub_elf_t UNUSED _elf
, Elf32_Phdr
*phdr
, void UNUSED
*_arg
)
187 /* Only consider loadable segments. */
188 if (phdr
->p_type
!= PT_LOAD
)
191 if (phdr
->p_paddr
< segments_start
)
192 segments_start
= phdr
->p_paddr
;
193 if (phdr
->p_paddr
+ phdr
->p_memsz
> segments_end
)
194 segments_end
= phdr
->p_paddr
+ phdr
->p_memsz
;
198 grub_elf32_phdr_iterate (elf
, calcsize
, 0);
202 grub_error (GRUB_ERR_BAD_OS
, "No program headers present");
206 if (segments_end
< segments_start
)
208 /* Very bad addresses. */
209 grub_error (GRUB_ERR_BAD_OS
, "Bad program header load addresses");
213 return segments_end
- segments_start
;
217 /* Load every loadable segment into memory specified by `_load_hook'. */
219 grub_elf32_load (grub_elf_t _elf
, grub_elf32_load_hook_t _load_hook
,
220 grub_addr_t
*base
, grub_size_t
*size
)
222 grub_addr_t load_base
= (grub_addr_t
) -1ULL;
223 grub_size_t load_size
= 0;
226 auto int NESTED_FUNC_ATTR
grub_elf32_load_segment (grub_elf_t elf
, Elf32_Phdr
*phdr
, void *hook
);
227 int NESTED_FUNC_ATTR
grub_elf32_load_segment (grub_elf_t elf
, Elf32_Phdr
*phdr
, void *hook
)
229 grub_elf32_load_hook_t load_hook
= (grub_elf32_load_hook_t
) hook
;
230 grub_addr_t load_addr
;
233 load_addr
= phdr
->p_paddr
;
234 if (load_hook
&& load_hook (phdr
, &load_addr
, &do_load
))
240 if (load_addr
< load_base
)
241 load_base
= load_addr
;
243 grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
244 (unsigned long long) load_addr
,
245 (unsigned long long) phdr
->p_memsz
);
247 if (grub_file_seek (elf
->file
, phdr
->p_offset
) == (grub_off_t
) -1)
250 return grub_error (GRUB_ERR_BAD_OS
,
251 "Invalid offset in program header.");
257 read
= grub_file_read (elf
->file
, (void *) load_addr
, phdr
->p_filesz
);
258 if (read
!= (grub_ssize_t
) phdr
->p_filesz
)
260 /* XXX How can we free memory from `load_hook'? */
262 return grub_error (GRUB_ERR_BAD_OS
,
263 "Couldn't read segment from file: "
264 "wanted 0x%lx bytes; read 0x%lx bytes.",
265 phdr
->p_filesz
, read
);
269 if (phdr
->p_filesz
< phdr
->p_memsz
)
270 grub_memset ((void *) (long) (load_addr
+ phdr
->p_filesz
),
271 0, phdr
->p_memsz
- phdr
->p_filesz
);
273 load_size
+= phdr
->p_memsz
;
278 err
= grub_elf32_phdr_iterate (_elf
, grub_elf32_load_segment
, _load_hook
);
293 grub_elf_is_elf64 (grub_elf_t elf
)
295 return elf
->ehdr
.ehdr64
.e_ident
[EI_CLASS
] == ELFCLASS64
;
299 grub_elf64_load_phdrs (grub_elf_t elf
)
301 grub_ssize_t phdrs_size
;
303 phdrs_size
= elf
->ehdr
.ehdr64
.e_phnum
* elf
->ehdr
.ehdr64
.e_phentsize
;
305 grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
306 (unsigned long long) elf
->ehdr
.ehdr64
.e_phoff
,
307 (unsigned long) phdrs_size
);
309 elf
->phdrs
= grub_malloc (phdrs_size
);
313 if ((grub_file_seek (elf
->file
, elf
->ehdr
.ehdr64
.e_phoff
) == (grub_off_t
) -1)
314 || (grub_file_read (elf
->file
, elf
->phdrs
, phdrs_size
) != phdrs_size
))
317 return grub_error (GRUB_ERR_READ_ERROR
, "Cannot read program headers");
320 return GRUB_ERR_NONE
;
324 grub_elf64_phdr_iterate (grub_elf_t elf
,
325 int NESTED_FUNC_ATTR (*hook
) (grub_elf_t
, Elf64_Phdr
*, void *),
332 if (grub_elf64_load_phdrs (elf
))
336 for (i
= 0; i
< elf
->ehdr
.ehdr64
.e_phnum
; i
++)
338 Elf64_Phdr
*phdr
= phdrs
+ i
;
340 "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
343 (unsigned long) phdr
->p_paddr
,
344 (unsigned long) phdr
->p_memsz
,
345 (unsigned long) phdr
->p_filesz
);
346 if (hook (elf
, phdr
, hook_arg
))
353 /* Calculate the amount of memory spanned by the segments. */
355 grub_elf64_size (grub_elf_t elf
)
357 Elf64_Addr segments_start
= (Elf64_Addr
) -1;
358 Elf64_Addr segments_end
= 0;
361 /* Run through the program headers to calculate the total memory size we
363 auto int NESTED_FUNC_ATTR
calcsize (grub_elf_t _elf
, Elf64_Phdr
*phdr
, void *_arg
);
364 int NESTED_FUNC_ATTR
calcsize (grub_elf_t UNUSED _elf
, Elf64_Phdr
*phdr
, void UNUSED
*_arg
)
366 /* Only consider loadable segments. */
367 if (phdr
->p_type
!= PT_LOAD
)
370 if (phdr
->p_paddr
< segments_start
)
371 segments_start
= phdr
->p_paddr
;
372 if (phdr
->p_paddr
+ phdr
->p_memsz
> segments_end
)
373 segments_end
= phdr
->p_paddr
+ phdr
->p_memsz
;
377 grub_elf64_phdr_iterate (elf
, calcsize
, 0);
381 grub_error (GRUB_ERR_BAD_OS
, "No program headers present");
385 if (segments_end
< segments_start
)
387 /* Very bad addresses. */
388 grub_error (GRUB_ERR_BAD_OS
, "Bad program header load addresses");
392 return segments_end
- segments_start
;
396 /* Load every loadable segment into memory specified by `_load_hook'. */
398 grub_elf64_load (grub_elf_t _elf
, grub_elf64_load_hook_t _load_hook
,
399 grub_addr_t
*base
, grub_size_t
*size
)
401 grub_addr_t load_base
= (grub_addr_t
) -1ULL;
402 grub_size_t load_size
= 0;
405 auto int NESTED_FUNC_ATTR
grub_elf64_load_segment (grub_elf_t elf
, Elf64_Phdr
*phdr
,
407 int NESTED_FUNC_ATTR
grub_elf64_load_segment (grub_elf_t elf
, Elf64_Phdr
*phdr
, void *hook
)
409 grub_elf64_load_hook_t load_hook
= (grub_elf64_load_hook_t
) hook
;
410 grub_addr_t load_addr
;
413 load_addr
= phdr
->p_paddr
;
414 if (load_hook
&& load_hook (phdr
, &load_addr
, &do_load
))
420 if (load_addr
< load_base
)
421 load_base
= load_addr
;
423 grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
424 (unsigned long long) load_addr
,
425 (unsigned long long) phdr
->p_memsz
);
427 if (grub_file_seek (elf
->file
, phdr
->p_offset
) == (grub_off_t
) -1)
430 return grub_error (GRUB_ERR_BAD_OS
,
431 "Invalid offset in program header.");
437 read
= grub_file_read (elf
->file
, (void *) load_addr
, phdr
->p_filesz
);
438 if (read
!= (grub_ssize_t
) phdr
->p_filesz
)
440 /* XXX How can we free memory from `load_hook'? */
442 return grub_error (GRUB_ERR_BAD_OS
,
443 "Couldn't read segment from file: "
444 "wanted 0x%lx bytes; read 0x%lx bytes.",
445 phdr
->p_filesz
, read
);
449 if (phdr
->p_filesz
< phdr
->p_memsz
)
450 grub_memset ((void *) (long) (load_addr
+ phdr
->p_filesz
),
451 0, phdr
->p_memsz
- phdr
->p_filesz
);
453 load_size
+= phdr
->p_memsz
;
458 err
= grub_elf64_phdr_iterate (_elf
, grub_elf64_load_segment
, _load_hook
);