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_zalloc (sizeof (*elf
));
70 if (grub_file_seek (elf
->file
, 0) == (grub_off_t
) -1)
73 if (grub_file_read (elf
->file
, &elf
->ehdr
, sizeof (elf
->ehdr
))
74 != sizeof (elf
->ehdr
))
77 grub_error (GRUB_ERR_READ_ERROR
, "Cannot read ELF header.");
81 if (grub_elf_check_header (elf
))
87 grub_free (elf
->phdrs
);
93 grub_elf_open (const char *name
)
98 file
= grub_gzfile_open (name
, 1);
102 elf
= grub_elf_file (file
);
104 grub_file_close (file
);
113 grub_elf_is_elf32 (grub_elf_t elf
)
115 return elf
->ehdr
.ehdr32
.e_ident
[EI_CLASS
] == ELFCLASS32
;
119 grub_elf32_load_phdrs (grub_elf_t elf
)
121 grub_ssize_t phdrs_size
;
123 phdrs_size
= elf
->ehdr
.ehdr32
.e_phnum
* elf
->ehdr
.ehdr32
.e_phentsize
;
125 grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
126 (unsigned long long) elf
->ehdr
.ehdr32
.e_phoff
,
127 (unsigned long) phdrs_size
);
129 elf
->phdrs
= grub_malloc (phdrs_size
);
133 if ((grub_file_seek (elf
->file
, elf
->ehdr
.ehdr32
.e_phoff
) == (grub_off_t
) -1)
134 || (grub_file_read (elf
->file
, elf
->phdrs
, phdrs_size
) != phdrs_size
))
137 return grub_error (GRUB_ERR_READ_ERROR
, "Cannot read program headers");
140 return GRUB_ERR_NONE
;
144 grub_elf32_phdr_iterate (grub_elf_t elf
,
145 int NESTED_FUNC_ATTR (*hook
) (grub_elf_t
, Elf32_Phdr
*, void *),
152 if (grub_elf32_load_phdrs (elf
))
156 for (i
= 0; i
< elf
->ehdr
.ehdr32
.e_phnum
; i
++)
158 Elf32_Phdr
*phdr
= phdrs
+ i
;
160 "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
163 (unsigned long) phdr
->p_paddr
,
164 (unsigned long) phdr
->p_memsz
,
165 (unsigned long) phdr
->p_filesz
);
166 if (hook (elf
, phdr
, hook_arg
))
173 /* Calculate the amount of memory spanned by the segments. */
175 grub_elf32_size (grub_elf_t elf
)
177 Elf32_Addr segments_start
= (Elf32_Addr
) -1;
178 Elf32_Addr segments_end
= 0;
181 /* Run through the program headers to calculate the total memory size we
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 UNUSED _elf
, Elf32_Phdr
*phdr
, void UNUSED
*_arg
)
186 /* Only consider loadable segments. */
187 if (phdr
->p_type
!= PT_LOAD
)
190 if (phdr
->p_paddr
< segments_start
)
191 segments_start
= phdr
->p_paddr
;
192 if (phdr
->p_paddr
+ phdr
->p_memsz
> segments_end
)
193 segments_end
= phdr
->p_paddr
+ phdr
->p_memsz
;
197 grub_elf32_phdr_iterate (elf
, calcsize
, 0);
201 grub_error (GRUB_ERR_BAD_OS
, "No program headers present");
205 if (segments_end
< segments_start
)
207 /* Very bad addresses. */
208 grub_error (GRUB_ERR_BAD_OS
, "Bad program header load addresses");
212 return segments_end
- segments_start
;
216 /* Load every loadable segment into memory specified by `_load_hook'. */
218 grub_elf32_load (grub_elf_t _elf
, grub_elf32_load_hook_t _load_hook
,
219 grub_addr_t
*base
, grub_size_t
*size
)
221 grub_addr_t load_base
= (grub_addr_t
) -1ULL;
222 grub_size_t load_size
= 0;
225 auto int NESTED_FUNC_ATTR
grub_elf32_load_segment (grub_elf_t elf
, Elf32_Phdr
*phdr
, void *hook
);
226 int NESTED_FUNC_ATTR
grub_elf32_load_segment (grub_elf_t elf
, Elf32_Phdr
*phdr
, void *hook
)
228 grub_elf32_load_hook_t load_hook
= (grub_elf32_load_hook_t
) hook
;
229 grub_addr_t load_addr
;
232 load_addr
= phdr
->p_paddr
;
233 if (load_hook
&& load_hook (phdr
, &load_addr
, &do_load
))
239 if (load_addr
< load_base
)
240 load_base
= load_addr
;
242 grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
243 (unsigned long long) load_addr
,
244 (unsigned long long) phdr
->p_memsz
);
246 if (grub_file_seek (elf
->file
, phdr
->p_offset
) == (grub_off_t
) -1)
249 return grub_error (GRUB_ERR_BAD_OS
,
250 "Invalid offset in program header.");
256 read
= grub_file_read (elf
->file
, (void *) load_addr
, phdr
->p_filesz
);
257 if (read
!= (grub_ssize_t
) phdr
->p_filesz
)
259 /* XXX How can we free memory from `load_hook'? */
261 return grub_error (GRUB_ERR_BAD_OS
,
262 "Couldn't read segment from file: "
263 "wanted 0x%lx bytes; read 0x%lx bytes.",
264 phdr
->p_filesz
, read
);
268 if (phdr
->p_filesz
< phdr
->p_memsz
)
269 grub_memset ((void *) (long) (load_addr
+ phdr
->p_filesz
),
270 0, phdr
->p_memsz
- phdr
->p_filesz
);
272 load_size
+= phdr
->p_memsz
;
277 err
= grub_elf32_phdr_iterate (_elf
, grub_elf32_load_segment
, _load_hook
);
292 grub_elf_is_elf64 (grub_elf_t elf
)
294 return elf
->ehdr
.ehdr64
.e_ident
[EI_CLASS
] == ELFCLASS64
;
298 grub_elf64_load_phdrs (grub_elf_t elf
)
300 grub_ssize_t phdrs_size
;
302 phdrs_size
= elf
->ehdr
.ehdr64
.e_phnum
* elf
->ehdr
.ehdr64
.e_phentsize
;
304 grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
305 (unsigned long long) elf
->ehdr
.ehdr64
.e_phoff
,
306 (unsigned long) phdrs_size
);
308 elf
->phdrs
= grub_malloc (phdrs_size
);
312 if ((grub_file_seek (elf
->file
, elf
->ehdr
.ehdr64
.e_phoff
) == (grub_off_t
) -1)
313 || (grub_file_read (elf
->file
, elf
->phdrs
, phdrs_size
) != phdrs_size
))
316 return grub_error (GRUB_ERR_READ_ERROR
, "Cannot read program headers");
319 return GRUB_ERR_NONE
;
323 grub_elf64_phdr_iterate (grub_elf_t elf
,
324 int NESTED_FUNC_ATTR (*hook
) (grub_elf_t
, Elf64_Phdr
*, void *),
331 if (grub_elf64_load_phdrs (elf
))
335 for (i
= 0; i
< elf
->ehdr
.ehdr64
.e_phnum
; i
++)
337 Elf64_Phdr
*phdr
= phdrs
+ i
;
339 "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
342 (unsigned long) phdr
->p_paddr
,
343 (unsigned long) phdr
->p_memsz
,
344 (unsigned long) phdr
->p_filesz
);
345 if (hook (elf
, phdr
, hook_arg
))
352 /* Calculate the amount of memory spanned by the segments. */
354 grub_elf64_size (grub_elf_t elf
)
356 Elf64_Addr segments_start
= (Elf64_Addr
) -1;
357 Elf64_Addr segments_end
= 0;
360 /* Run through the program headers to calculate the total memory size we
362 auto int NESTED_FUNC_ATTR
calcsize (grub_elf_t _elf
, Elf64_Phdr
*phdr
, void *_arg
);
363 int NESTED_FUNC_ATTR
calcsize (grub_elf_t UNUSED _elf
, Elf64_Phdr
*phdr
, void UNUSED
*_arg
)
365 /* Only consider loadable segments. */
366 if (phdr
->p_type
!= PT_LOAD
)
369 if (phdr
->p_paddr
< segments_start
)
370 segments_start
= phdr
->p_paddr
;
371 if (phdr
->p_paddr
+ phdr
->p_memsz
> segments_end
)
372 segments_end
= phdr
->p_paddr
+ phdr
->p_memsz
;
376 grub_elf64_phdr_iterate (elf
, calcsize
, 0);
380 grub_error (GRUB_ERR_BAD_OS
, "No program headers present");
384 if (segments_end
< segments_start
)
386 /* Very bad addresses. */
387 grub_error (GRUB_ERR_BAD_OS
, "Bad program header load addresses");
391 return segments_end
- segments_start
;
395 /* Load every loadable segment into memory specified by `_load_hook'. */
397 grub_elf64_load (grub_elf_t _elf
, grub_elf64_load_hook_t _load_hook
,
398 grub_addr_t
*base
, grub_size_t
*size
)
400 grub_addr_t load_base
= (grub_addr_t
) -1ULL;
401 grub_size_t load_size
= 0;
404 auto int NESTED_FUNC_ATTR
grub_elf64_load_segment (grub_elf_t elf
, Elf64_Phdr
*phdr
,
406 int NESTED_FUNC_ATTR
grub_elf64_load_segment (grub_elf_t elf
, Elf64_Phdr
*phdr
, void *hook
)
408 grub_elf64_load_hook_t load_hook
= (grub_elf64_load_hook_t
) hook
;
409 grub_addr_t load_addr
;
412 load_addr
= phdr
->p_paddr
;
413 if (load_hook
&& load_hook (phdr
, &load_addr
, &do_load
))
419 if (load_addr
< load_base
)
420 load_base
= load_addr
;
422 grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
423 (unsigned long long) load_addr
,
424 (unsigned long long) phdr
->p_memsz
);
426 if (grub_file_seek (elf
->file
, phdr
->p_offset
) == (grub_off_t
) -1)
429 return grub_error (GRUB_ERR_BAD_OS
,
430 "Invalid offset in program header.");
436 read
= grub_file_read (elf
->file
, (void *) load_addr
, phdr
->p_filesz
);
437 if (read
!= (grub_ssize_t
) phdr
->p_filesz
)
439 /* XXX How can we free memory from `load_hook'? */
441 return grub_error (GRUB_ERR_BAD_OS
,
442 "Couldn't read segment from file: "
443 "wanted 0x%lx bytes; read 0x%lx bytes.",
444 phdr
->p_filesz
, read
);
448 if (phdr
->p_filesz
< phdr
->p_memsz
)
449 grub_memset ((void *) (long) (load_addr
+ phdr
->p_filesz
),
450 0, phdr
->p_memsz
- phdr
->p_filesz
);
452 load_size
+= phdr
->p_memsz
;
457 err
= grub_elf64_phdr_iterate (_elf
, grub_elf64_load_segment
, _load_hook
);