This patch adds generic ELF loading infrastructure for both 32-bit and 64-bit ELF. It provides an "iterate" function for program headers, and a "load" function for convenience.
I have converted the PowerPC Linux loader to use this infrastructure (see next mail), and possibly the i386 multiboot loader as well (though I will need others to test it). The module loader looks a little more annoying because it actually loads *sections*, not segments. I will be away next week but intend to commit this soon after, so please review. This code is literally duplicated, once for 32-bit and once for 64-bit. I'm trying to come up with a better way of doing that, and I'm considering something like this: elf32.c: #define Elf_Ehdr Elf32_Ehdr #define Elf_Phdr Elf32_Phdr #include "elfclass.c" elf64.c: #define Elf_Ehdr Elf64_Ehdr #define Elf_Phdr Elf64_Phdr #include "elfclass.c" If you strongly object to #including C files, or have a better way of doing this, please speak up. -Hollis diff -r b53dfa2812ad include/grub/elfload.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/grub/elfload.h Fri Oct 13 16:23:22 2006 -0500 @@ -0,0 +1,57 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef GRUB_ELFLOAD_HEADER +#define GRUB_ELFLOAD_HEADER 1 + +#include <grub/err.h> +#include <grub/elf.h> +#include <grub/file.h> +#include <grub/symbol.h> +#include <grub/types.h> + +struct grub_elf_file +{ + grub_file_t file; + union { + Elf64_Ehdr ehdr64; + Elf32_Ehdr ehdr32; + } ehdr; + void *phdrs; +}; +typedef struct grub_elf_file *grub_elf_t; + +grub_elf_t EXPORT_FUNC(grub_elf_open) (const char *name); +grub_err_t EXPORT_FUNC(grub_elf_close) (grub_elf_t elf); + +int EXPORT_FUNC(grub_elf_is_elf32) (grub_elf_t elf); +grub_err_t EXPORT_FUNC(grub_elf32_phdr_iterate) (grub_elf_t elf, + int (*hook) (grub_elf_t, Elf32_Phdr *, void *), + void *hook_arg); +grub_err_t EXPORT_FUNC(grub_elf32_load) (grub_elf_t elf, + int (*) (Elf32_Phdr *)); + +int EXPORT_FUNC(grub_elf_is_elf64) (grub_elf_t elf); +grub_err_t EXPORT_FUNC(grub_elf64_phdr_iterate) (grub_elf_t elf, + int (*hook) (grub_elf_t, Elf64_Phdr *, void *), + void *hook_arg); +grub_err_t EXPORT_FUNC(grub_elf64_load) (grub_elf_t elf, + int (*) (Elf64_Phdr *)); + +#endif /* ! GRUB_ELFLOAD_HEADER */ diff -r b53dfa2812ad kern/elf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kern/elf.c Fri Oct 13 16:23:22 2006 -0500 @@ -0,0 +1,280 @@ +/* elf.c - load ELF files */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <grub/err.h> +#include <grub/elf.h> +#include <grub/elfload.h> +#include <grub/file.h> +#include <grub/misc.h> +#include <grub/mm.h> + +/* Check if EHDR is a valid ELF header. */ +static grub_err_t +grub_elf_check_header (grub_elf_t elf) +{ + Elf32_Ehdr *e = &elf->ehdr.ehdr32; + + if (e->e_ident[EI_MAG0] != ELFMAG0 + || e->e_ident[EI_MAG1] != ELFMAG1 + || e->e_ident[EI_MAG2] != ELFMAG2 + || e->e_ident[EI_MAG3] != ELFMAG3 + || e->e_ident[EI_VERSION] != EV_CURRENT + || e->e_version != EV_CURRENT) + return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic"); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_elf_close (grub_elf_t elf) +{ + grub_file_t file = elf->file; + + grub_free (elf->phdrs); + grub_free (elf); + + if (file) + grub_file_close (file); + + return grub_errno; +} + +grub_elf_t +grub_elf_open (const char *name) +{ + grub_elf_t elf; + + elf = grub_malloc (sizeof (*elf)); + if (! elf) + return 0; + + elf->file = 0; + elf->phdrs = 0; + + elf->file = grub_file_open (name); + if (! elf->file) + goto fail; + + if (grub_file_read (elf->file, (char *) &elf->ehdr, sizeof (elf->ehdr)) + != sizeof (elf->ehdr)) + { + grub_error (GRUB_ERR_READ_ERROR, "Cannot read ELF header."); + goto fail; + } + + if (grub_elf_check_header (elf)) + goto fail; + + return elf; + +fail: + grub_elf_close (elf); + return 0; +} + + +/* 32-bit */ + +int +grub_elf_is_elf32 (grub_elf_t elf) +{ + return elf->ehdr.ehdr32.e_ident[EI_CLASS] == ELFCLASS32; +} + +typedef int (*grub_elf32_load_hook_t) (Elf32_Phdr *phdr); + +static int +grub_elf32_load_segment (grub_elf_t elf, Elf32_Phdr *phdr, + void *hook) +{ + grub_elf32_load_hook_t load_hook = (grub_elf32_load_hook_t) hook; + + if (phdr->p_type != PT_LOAD) + return 0; + + if (load_hook && load_hook (phdr)) + return 1; + + grub_dprintf ("loader", "Loading segment at %llx, size 0x%llx\n", + (unsigned long long) phdr->p_paddr, + (unsigned long long) phdr->p_filesz); + + if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1) + { + return grub_error (GRUB_ERR_BAD_OS, "Invalid offset in program header"); + } + + if (grub_file_read (elf->file, (void *) (long) phdr->p_paddr, phdr->p_filesz) + != (grub_ssize_t) phdr->p_filesz) + { + return grub_error (GRUB_ERR_BAD_OS, "Couldn't load segment"); + } + + if (phdr->p_filesz < phdr->p_memsz) + grub_memset ((void *) (unsigned long) (phdr->p_paddr + phdr->p_filesz), + 0, phdr->p_memsz - phdr->p_filesz); + + return 0; +} + +static grub_err_t +grub_elf32_load_phdrs (grub_elf_t elf) +{ + grub_ssize_t phdrs_size; + + phdrs_size = elf->ehdr.ehdr32.e_phnum * elf->ehdr.ehdr32.e_phentsize; + + grub_dprintf ("elf", "loading program headers: offset 0x%llx, size 0x%x\n", + (unsigned long long) elf->ehdr.ehdr32.e_phoff, + phdrs_size); + + elf->phdrs = grub_malloc (phdrs_size); + if (! elf->phdrs) + return grub_errno; + + if ((grub_file_seek (elf->file, elf->ehdr.ehdr32.e_phoff) == (grub_off_t) -1) + || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size)) + return grub_error (GRUB_ERR_READ_ERROR, "Cannot read program headers"); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_elf32_phdr_iterate (grub_elf_t elf, + int (*hook) (grub_elf_t, Elf32_Phdr *, void *), + void *hook_arg) +{ + Elf32_Phdr *phdrs; + unsigned int i; + + if (! elf->phdrs) + if (grub_elf32_load_phdrs (elf)) + return grub_errno; + + phdrs = (Elf32_Phdr *) elf->phdrs; + for (i = 0; i < elf->ehdr.ehdr32.e_phnum; i++) + if (hook (elf, phdrs + i, hook_arg)) + break; + + return grub_errno; +} + +/* Load every loadable segment into memory. */ +grub_err_t +grub_elf32_load (grub_elf_t elf, int (*phdr_hook) (Elf32_Phdr *)) +{ + return grub_elf32_phdr_iterate (elf, grub_elf32_load_segment, + phdr_hook); +} + + +/* 64-bit */ + +int +grub_elf_is_elf64 (grub_elf_t elf) +{ + return elf->ehdr.ehdr64.e_ident[EI_CLASS] == ELFCLASS64; +} + +typedef int (*grub_elf64_load_hook_t) (Elf64_Phdr *phdr); + +static int +grub_elf64_load_segment (grub_elf_t elf, Elf64_Phdr *phdr, + void *hook) +{ + grub_elf64_load_hook_t load_hook = (grub_elf64_load_hook_t) hook; + + if (phdr->p_type != PT_LOAD) + return 0; + + if (load_hook && load_hook (phdr)) + return 1; + + grub_dprintf ("loader", "Loading segment at %llx, size 0x%llx\n", + (unsigned long long) phdr->p_paddr, + (unsigned long long) phdr->p_filesz); + + if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1) + { + return grub_error (GRUB_ERR_BAD_OS, "Invalid offset in program header"); + } + + if (grub_file_read (elf->file, (void *) (long) phdr->p_paddr, phdr->p_filesz) + != (grub_ssize_t) phdr->p_filesz) + { + return grub_error (GRUB_ERR_BAD_OS, "Couldn't load segment"); + } + + if (phdr->p_filesz < phdr->p_memsz) + grub_memset ((void *) (unsigned long) (phdr->p_paddr + phdr->p_filesz), + 0, phdr->p_memsz - phdr->p_filesz); + + return 0; +} + +static grub_err_t +grub_elf64_load_phdrs (grub_elf_t elf) +{ + grub_ssize_t phdrs_size; + + phdrs_size = elf->ehdr.ehdr64.e_phnum * elf->ehdr.ehdr64.e_phentsize; + + grub_dprintf ("elf", "loading program headers: offset 0x%llx, size 0x%x\n", + (unsigned long long) elf->ehdr.ehdr64.e_phoff, + phdrs_size); + + elf->phdrs = grub_malloc (phdrs_size); + if (! elf->phdrs) + return grub_errno; + + if ((grub_file_seek (elf->file, elf->ehdr.ehdr64.e_phoff) == (grub_off_t) -1) + || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size)) + return grub_error (GRUB_ERR_READ_ERROR, "Cannot read program headers"); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_elf64_phdr_iterate (grub_elf_t elf, + int (*hook) (grub_elf_t, Elf64_Phdr *, void *), + void *hook_arg) +{ + Elf64_Phdr *phdrs; + unsigned int i; + + if (! elf->phdrs) + if (grub_elf64_load_phdrs (elf)) + return grub_errno; + + phdrs = (Elf64_Phdr *) elf->phdrs; + for (i = 0; i < elf->ehdr.ehdr64.e_phnum; i++) + if (hook (elf, phdrs + i, hook_arg)) + break; + + return grub_errno; +} + +/* Load every loadable segment into memory. */ +grub_err_t +grub_elf64_load (grub_elf_t elf, int (*phdr_hook) (Elf64_Phdr *)) +{ + return grub_elf64_phdr_iterate (elf, grub_elf64_load_segment, + phdr_hook); +} _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel