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
, (char *) &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%x.\n",
127 (unsigned long long) elf
->ehdr
.ehdr32
.e_phoff
,
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
;
232 if (phdr
->p_type
!= PT_LOAD
)
235 if (load_hook
&& load_hook (phdr
, &load_addr
))
237 load_addr
= phdr
->p_paddr
;
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%x.\n",
305 (unsigned long long) elf
->ehdr
.ehdr64
.e_phoff
,
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
;
411 if (phdr
->p_type
!= PT_LOAD
)
414 if (load_hook
&& load_hook (phdr
, &load_addr
))
416 load_addr
= phdr
->p_paddr
;
418 if (load_addr
< load_base
)
419 load_base
= load_addr
;
421 grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
422 (unsigned long long) load_addr
,
423 (unsigned long long) phdr
->p_memsz
);
425 if (grub_file_seek (elf
->file
, phdr
->p_offset
) == (grub_off_t
) -1)
428 return grub_error (GRUB_ERR_BAD_OS
,
429 "Invalid offset in program header.");
435 read
= grub_file_read (elf
->file
, (void *) load_addr
, phdr
->p_filesz
);
436 if (read
!= (grub_ssize_t
) phdr
->p_filesz
)
438 /* XXX How can we free memory from `load_hook'? */
440 return grub_error (GRUB_ERR_BAD_OS
,
441 "Couldn't read segment from file: "
442 "wanted 0x%lx bytes; read 0x%lx bytes.",
443 phdr
->p_filesz
, read
);
447 if (phdr
->p_filesz
< phdr
->p_memsz
)
448 grub_memset ((void *) (long) (load_addr
+ phdr
->p_filesz
),
449 0, phdr
->p_memsz
- phdr
->p_filesz
);
451 load_size
+= phdr
->p_memsz
;
456 err
= grub_elf64_phdr_iterate (_elf
, grub_elf64_load_segment
, _load_hook
);