From efd159c0660c89f90089c7570c17c58c68df6dbe Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 2 Mar 2013 16:45:57 +0100 Subject: [PATCH 184/364] Make elfload not use hooks. Opt for flags and iterators instead. --- ChangeLog | 4 + grub-core/kern/elf.c | 455 +++------------------------ grub-core/kern/elfXX.c | 144 +++++++++ grub-core/loader/i386/bsd.c | 137 +++----- grub-core/loader/i386/coreboot/chainloader.c | 69 ++-- grub-core/loader/mips/linux.c | 37 +-- grub-core/loader/powerpc/ieee1275/linux.c | 34 +- grub-core/loader/sparc64/ieee1275/linux.c | 18 +- include/grub/elfload.h | 35 ++- 9 files changed, 301 insertions(+), 632 deletions(-) create mode 100644 grub-core/kern/elfXX.c diff --git a/ChangeLog b/ChangeLog index d55dd8f..7c80ed6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2013-03-02 Vladimir Serbinenko + Make elfload not use hooks. Opt for flags and iterators instead. + +2013-03-02 Vladimir Serbinenko + * grub-core/lib/ia64/longjmp.S: Fix the name of longjmp function. * grub-core/lib/ia64/setjmp.S: Fix the name of setjmp function. diff --git a/grub-core/kern/elf.c b/grub-core/kern/elf.c index f52ca21..5f99c43 100644 --- a/grub-core/kern/elf.c +++ b/grub-core/kern/elf.c @@ -51,6 +51,7 @@ grub_elf_close (grub_elf_t elf) grub_file_t file = elf->file; grub_free (elf->phdrs); + grub_free (elf->filename); grub_free (elf); if (file) @@ -85,9 +86,14 @@ grub_elf_file (grub_file_t file, const char *filename) if (grub_elf_check_header (elf)) goto fail; + elf->filename = grub_strdup (filename); + if (!elf->filename) + goto fail; + return elf; fail: + grub_free (elf->filename); grub_free (elf->phdrs); grub_free (elf); return 0; @@ -112,420 +118,41 @@ grub_elf_open (const char *name) /* 32-bit */ - -int -grub_elf_is_elf32 (grub_elf_t elf) -{ - return elf->ehdr.ehdr32.e_ident[EI_CLASS] == ELFCLASS32; -} - -static grub_err_t -grub_elf32_load_phdrs (grub_elf_t elf, const char *filename) -{ - grub_ssize_t phdrs_size; - - phdrs_size = elf->ehdr.ehdr32.e_phnum * elf->ehdr.ehdr32.e_phentsize; - - grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n", - (unsigned long long) elf->ehdr.ehdr32.e_phoff, - (unsigned long) 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)) - { - if (!grub_errno) - grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), - filename); - return grub_errno; - } - - return GRUB_ERR_NONE; -} - -grub_err_t -grub_elf32_phdr_iterate (grub_elf_t elf, - const char *filename, - grub_elf32_phdr_iterate_hook_t hook, void *hook_arg) -{ - Elf32_Phdr *phdrs; - unsigned int i; - - if (! elf->phdrs) - if (grub_elf32_load_phdrs (elf, filename)) - return grub_errno; - phdrs = elf->phdrs; - - for (i = 0; i < elf->ehdr.ehdr32.e_phnum; i++) - { - Elf32_Phdr *phdr = phdrs + i; - grub_dprintf ("elf", - "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx " - "filesz %lx\n", - i, phdr->p_type, - (unsigned long) phdr->p_paddr, - (unsigned long) phdr->p_memsz, - (unsigned long) phdr->p_filesz); - if (hook (elf, phdr, hook_arg)) - break; - } - - return grub_errno; -} - -struct grub_elf32_size_ctx -{ - Elf32_Addr segments_start, segments_end; - int nr_phdrs; - grub_uint32_t curr_align; -}; - -/* Run through the program headers to calculate the total memory size we - * should claim. */ -static int -grub_elf32_calcsize (grub_elf_t _elf __attribute__ ((unused)), - Elf32_Phdr *phdr, void *data) -{ - struct grub_elf32_size_ctx *ctx = data; - - /* Only consider loadable segments. */ - if (phdr->p_type != PT_LOAD) - return 0; - ctx->nr_phdrs++; - if (phdr->p_paddr < ctx->segments_start) - ctx->segments_start = phdr->p_paddr; - if (phdr->p_paddr + phdr->p_memsz > ctx->segments_end) - ctx->segments_end = phdr->p_paddr + phdr->p_memsz; - if (ctx->curr_align < phdr->p_align) - ctx->curr_align = phdr->p_align; - return 0; -} - -/* Calculate the amount of memory spanned by the segments. */ -grub_size_t -grub_elf32_size (grub_elf_t elf, const char *filename, - Elf32_Addr *base, grub_uint32_t *max_align) -{ - struct grub_elf32_size_ctx ctx = { - .segments_start = (Elf32_Addr) -1, - .segments_end = 0, - .nr_phdrs = 0, - .curr_align = 1 - }; - - grub_elf32_phdr_iterate (elf, filename, grub_elf32_calcsize, &ctx); - - if (base) - *base = 0; - - if (ctx.nr_phdrs == 0) - { - grub_error (GRUB_ERR_BAD_OS, "no program headers present"); - return 0; - } - - if (ctx.segments_end < ctx.segments_start) - { - /* Very bad addresses. */ - grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses"); - return 0; - } - - if (base) - *base = ctx.segments_start; - if (max_align) - *max_align = ctx.curr_align; - return ctx.segments_end - ctx.segments_start; -} - -struct grub_elf32_load_ctx -{ - const char *filename; - grub_elf32_load_hook_t load_hook; - grub_addr_t load_base; - grub_size_t load_size; -}; - -static int -grub_elf32_load_segment (grub_elf_t elf, Elf32_Phdr *phdr, void *data) -{ - struct grub_elf32_load_ctx *ctx = data; - grub_addr_t load_addr; - int do_load = 1; - - load_addr = phdr->p_paddr; - if (ctx->load_hook && ctx->load_hook (phdr, &load_addr, &do_load)) - return 1; - - if (! do_load) - return 0; - - if (load_addr < ctx->load_base) - ctx->load_base = load_addr; - - grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n", - (unsigned long long) load_addr, - (unsigned long long) phdr->p_memsz); - - if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1) - return grub_errno; - - if (phdr->p_filesz) - { - grub_ssize_t read; - read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz); - if (read != (grub_ssize_t) phdr->p_filesz) - { - /* XXX How can we free memory from `ctx->load_hook'? */ - if (!grub_errno) - grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), - ctx->filename); - return grub_errno; - } - } - - if (phdr->p_filesz < phdr->p_memsz) - grub_memset ((void *) (long) (load_addr + phdr->p_filesz), - 0, phdr->p_memsz - phdr->p_filesz); - - ctx->load_size += phdr->p_memsz; - - return 0; -} - -/* Load every loadable segment into memory specified by `_load_hook'. */ -grub_err_t -grub_elf32_load (grub_elf_t elf, const char *filename, - grub_elf32_load_hook_t load_hook, - grub_addr_t *base, grub_size_t *size) -{ - struct grub_elf32_load_ctx ctx = { - .filename = filename, - .load_hook = load_hook, - .load_base = (grub_addr_t) -1ULL, - .load_size = 0 - }; - grub_err_t err; - - err = grub_elf32_phdr_iterate (elf, filename, grub_elf32_load_segment, &ctx); - - if (base) - *base = ctx.load_base; - if (size) - *size = ctx.load_size; - - return err; -} +#define ehdrXX ehdr32 +#define ELFCLASSXX ELFCLASS32 +#define ElfXX_Addr Elf32_Addr +#define grub_elfXX_size grub_elf32_size +#define grub_elfXX_load grub_elf32_load +#define FOR_ELFXX_PHDRS FOR_ELF32_PHDRS +#define grub_elf_is_elfXX grub_elf_is_elf32 +#define grub_elfXX_load_phdrs grub_elf32_load_phdrs +#define ElfXX_Phdr Elf32_Phdr +#define grub_uintXX_t grub_uint32_t + +#include "elfXX.c" + +#undef ehdrXX +#undef ELFCLASSXX +#undef ElfXX_Addr +#undef grub_elfXX_size +#undef grub_elfXX_load +#undef FOR_ELFXX_PHDRS +#undef grub_elf_is_elfXX +#undef grub_elfXX_load_phdrs +#undef ElfXX_Phdr +#undef grub_uintXX_t /* 64-bit */ - -int -grub_elf_is_elf64 (grub_elf_t elf) -{ - return elf->ehdr.ehdr64.e_ident[EI_CLASS] == ELFCLASS64; -} - -static grub_err_t -grub_elf64_load_phdrs (grub_elf_t elf, const char *filename) -{ - grub_ssize_t phdrs_size; - - phdrs_size = elf->ehdr.ehdr64.e_phnum * elf->ehdr.ehdr64.e_phentsize; - - grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n", - (unsigned long long) elf->ehdr.ehdr64.e_phoff, - (unsigned long) 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)) - { - if (!grub_errno) - grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), - filename); - return grub_errno; - } - - return GRUB_ERR_NONE; -} - -grub_err_t -grub_elf64_phdr_iterate (grub_elf_t elf, - const char *filename, - grub_elf64_phdr_iterate_hook_t hook, void *hook_arg) -{ - Elf64_Phdr *phdrs; - unsigned int i; - - if (! elf->phdrs) - if (grub_elf64_load_phdrs (elf, filename)) - return grub_errno; - phdrs = elf->phdrs; - - for (i = 0; i < elf->ehdr.ehdr64.e_phnum; i++) - { - Elf64_Phdr *phdr = phdrs + i; - grub_dprintf ("elf", - "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx " - "filesz %lx\n", - i, phdr->p_type, - (unsigned long) phdr->p_paddr, - (unsigned long) phdr->p_memsz, - (unsigned long) phdr->p_filesz); - if (hook (elf, phdr, hook_arg)) - break; - } - - return grub_errno; -} - -struct grub_elf64_size_ctx -{ - Elf64_Addr segments_start, segments_end; - int nr_phdrs; - grub_uint64_t curr_align; -}; - -/* Run through the program headers to calculate the total memory size we - * should claim. */ -static int -grub_elf64_calcsize (grub_elf_t _elf __attribute__ ((unused)), - Elf64_Phdr *phdr, void *data) -{ - struct grub_elf64_size_ctx *ctx = data; - - /* Only consider loadable segments. */ - if (phdr->p_type != PT_LOAD) - return 0; - ctx->nr_phdrs++; - if (phdr->p_paddr < ctx->segments_start) - ctx->segments_start = phdr->p_paddr; - if (phdr->p_paddr + phdr->p_memsz > ctx->segments_end) - ctx->segments_end = phdr->p_paddr + phdr->p_memsz; - if (ctx->curr_align < phdr->p_align) - ctx->curr_align = phdr->p_align; - return 0; -} - -/* Calculate the amount of memory spanned by the segments. */ -grub_size_t -grub_elf64_size (grub_elf_t elf, const char *filename, - Elf64_Addr *base, grub_uint64_t *max_align) -{ - struct grub_elf64_size_ctx ctx = { - .segments_start = (Elf64_Addr) -1, - .segments_end = 0, - .nr_phdrs = 0, - .curr_align = 1 - }; - - grub_elf64_phdr_iterate (elf, filename, grub_elf64_calcsize, &ctx); - - if (base) - *base = 0; - - if (ctx.nr_phdrs == 0) - { - grub_error (GRUB_ERR_BAD_OS, "no program headers present"); - return 0; - } - - if (ctx.segments_end < ctx.segments_start) - { - /* Very bad addresses. */ - grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses"); - return 0; - } - - if (base) - *base = ctx.segments_start; - if (max_align) - *max_align = ctx.curr_align; - return ctx.segments_end - ctx.segments_start; -} - -struct grub_elf64_load_ctx -{ - const char *filename; - grub_elf64_load_hook_t load_hook; - grub_addr_t load_base; - grub_size_t load_size; -}; - -static int -grub_elf64_load_segment (grub_elf_t elf, Elf64_Phdr *phdr, void *data) -{ - struct grub_elf64_load_ctx *ctx = data; - grub_addr_t load_addr; - int do_load = 1; - - load_addr = phdr->p_paddr; - if (ctx->load_hook && ctx->load_hook (phdr, &load_addr, &do_load)) - return 1; - - if (! do_load) - return 0; - - if (load_addr < ctx->load_base) - ctx->load_base = load_addr; - - grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n", - (unsigned long long) load_addr, - (unsigned long long) phdr->p_memsz); - - if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1) - return grub_errno; - - if (phdr->p_filesz) - { - grub_ssize_t read; - read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz); - if (read != (grub_ssize_t) phdr->p_filesz) - { - /* XXX How can we free memory from `ctx->load_hook'? */ - if (!grub_errno) - grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), - ctx->filename); - return grub_errno; - } - } - - if (phdr->p_filesz < phdr->p_memsz) - grub_memset ((void *) (long) (load_addr + phdr->p_filesz), - 0, phdr->p_memsz - phdr->p_filesz); - - ctx->load_size += phdr->p_memsz; - - return 0; -} - -/* Load every loadable segment into memory specified by `_load_hook'. */ -grub_err_t -grub_elf64_load (grub_elf_t elf, const char *filename, - grub_elf64_load_hook_t load_hook, - grub_addr_t *base, grub_size_t *size) -{ - struct grub_elf64_load_ctx ctx = { - .filename = filename, - .load_hook = load_hook, - .load_base = (grub_addr_t) -1ULL, - .load_size = 0 - }; - grub_err_t err; - - err = grub_elf64_phdr_iterate (elf, filename, grub_elf64_load_segment, &ctx); - - if (base) - *base = ctx.load_base; - if (size) - *size = ctx.load_size; - - return err; -} +#define ehdrXX ehdr64 +#define ELFCLASSXX ELFCLASS64 +#define ElfXX_Addr Elf64_Addr +#define grub_elfXX_size grub_elf64_size +#define grub_elfXX_load grub_elf64_load +#define FOR_ELFXX_PHDRS FOR_ELF64_PHDRS +#define grub_elf_is_elfXX grub_elf_is_elf64 +#define grub_elfXX_load_phdrs grub_elf64_load_phdrs +#define ElfXX_Phdr Elf64_Phdr +#define grub_uintXX_t grub_uint64_t + +#include "elfXX.c" diff --git a/grub-core/kern/elfXX.c b/grub-core/kern/elfXX.c new file mode 100644 index 0000000..b35e235 --- /dev/null +++ b/grub-core/kern/elfXX.c @@ -0,0 +1,144 @@ +int +grub_elf_is_elfXX (grub_elf_t elf) +{ + return elf->ehdr.ehdrXX.e_ident[EI_CLASS] == ELFCLASSXX; +} + +grub_err_t +grub_elfXX_load_phdrs (grub_elf_t elf) +{ + grub_ssize_t phdrs_size; + + if (elf->phdrs) + return GRUB_ERR_NONE; + + phdrs_size = elf->ehdr.ehdrXX.e_phnum * elf->ehdr.ehdrXX.e_phentsize; + + grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n", + (unsigned long long) elf->ehdr.ehdrXX.e_phoff, + (unsigned long) phdrs_size); + + elf->phdrs = grub_malloc (phdrs_size); + if (! elf->phdrs) + return grub_errno; + + if ((grub_file_seek (elf->file, elf->ehdr.ehdrXX.e_phoff) == (grub_off_t) -1) + || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size)) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + elf->filename); + return grub_errno; + } + + return GRUB_ERR_NONE; +} + +/* Calculate the amount of memory spanned by the segments. */ +grub_size_t +grub_elfXX_size (grub_elf_t elf, + ElfXX_Addr *base, grub_uintXX_t *max_align) +{ + ElfXX_Addr segments_start = (ElfXX_Addr) -1; + ElfXX_Addr segments_end = 0; + int nr_phdrs = 0; + grub_uint32_t curr_align = 1; + ElfXX_Phdr *phdr; + + /* Run through the program headers to calculate the total memory size we + * should claim. */ + FOR_ELFXX_PHDRS (elf, phdr) + { + /* Only consider loadable segments. */ + if (phdr->p_type != PT_LOAD) + continue; + nr_phdrs++; + if (phdr->p_paddr < segments_start) + segments_start = phdr->p_paddr; + if (phdr->p_paddr + phdr->p_memsz > segments_end) + segments_end = phdr->p_paddr + phdr->p_memsz; + if (curr_align < phdr->p_align) + curr_align = phdr->p_align; + } + + if (base) + *base = 0; + + if (nr_phdrs == 0) + { + grub_error (GRUB_ERR_BAD_OS, "no program headers present"); + return 0; + } + + if (segments_end < segments_start) + { + /* Very bad addresses. */ + grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses"); + return 0; + } + + if (base) + *base = segments_start; + if (max_align) + *max_align = curr_align; + return segments_end - segments_start; +} + +grub_err_t +grub_elfXX_load (grub_elf_t elf, const char *filename, + void *load_offset, enum grub_elf_load_flags load_flags, + grub_addr_t *base, grub_size_t *size) +{ + grub_addr_t load_base = (grub_addr_t) -1ULL; + grub_size_t load_size = 0; + ElfXX_Phdr *phdr; + + FOR_ELFXX_PHDRS(elf, phdr) + { + grub_addr_t load_addr; + + if (phdr->p_type != PT_LOAD && !((load_flags & GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC) && phdr->p_type == PT_DYNAMIC)) + continue; + + load_addr = (grub_addr_t) phdr->p_paddr; + if (load_flags & GRUB_ELF_LOAD_FLAGS_28BITS) + load_addr &= 0xFFFFFFF; + load_addr += (grub_addr_t) load_offset; + + if (load_addr < load_base) + load_base = load_addr; + + grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n", + (unsigned long long) load_addr, + (unsigned long long) phdr->p_memsz); + + if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1) + return grub_errno; + + if (phdr->p_filesz) + { + grub_ssize_t read; + read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz); + if (read != (grub_ssize_t) phdr->p_filesz) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + filename); + return grub_errno; + } + } + + if (phdr->p_filesz < phdr->p_memsz) + grub_memset ((void *) (long) (load_addr + phdr->p_filesz), + 0, phdr->p_memsz - phdr->p_filesz); + + load_size += phdr->p_memsz; + } + + if (base) + *base = load_base; + if (size) + *size = load_size; + + return grub_errno; +} diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c index 9b86158..6199609 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -1311,89 +1311,6 @@ grub_bsd_load_aout (grub_file_t file, const char *filename) bss_size); } -static int -grub_bsd_elf32_size_hook (grub_elf_t elf __attribute__ ((unused)), - Elf32_Phdr *phdr, void *arg __attribute__ ((unused))) -{ - Elf32_Addr paddr; - - if (phdr->p_type != PT_LOAD - && phdr->p_type != PT_DYNAMIC) - return 0; - - paddr = phdr->p_paddr & 0xFFFFFFF; - - if (paddr < kern_start) - kern_start = paddr; - - if (paddr + phdr->p_memsz > kern_end) - kern_end = paddr + phdr->p_memsz; - - return 0; -} - -static grub_err_t -grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr, int *do_load) -{ - Elf32_Addr paddr; - - if (phdr->p_type != PT_LOAD - && phdr->p_type != PT_DYNAMIC) - { - *do_load = 0; - return 0; - } - - *do_load = 1; - phdr->p_paddr &= 0xFFFFFFF; - paddr = phdr->p_paddr; - - *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src); - - return GRUB_ERR_NONE; -} - -static int -grub_bsd_elf64_size_hook (grub_elf_t elf __attribute__ ((unused)), - Elf64_Phdr *phdr, void *arg __attribute__ ((unused))) -{ - Elf64_Addr paddr; - - if (phdr->p_type != PT_LOAD - && phdr->p_type != PT_DYNAMIC) - return 0; - - paddr = phdr->p_paddr & 0xfffffff; - - if (paddr < kern_start) - kern_start = paddr; - - if (paddr + phdr->p_memsz > kern_end) - kern_end = paddr + phdr->p_memsz; - - return 0; -} - -static grub_err_t -grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr, int *do_load) -{ - Elf64_Addr paddr; - - if (phdr->p_type != PT_LOAD - && phdr->p_type != PT_DYNAMIC) - { - *do_load = 0; - return 0; - } - - *do_load = 1; - paddr = phdr->p_paddr & 0xfffffff; - - *addr = (grub_addr_t) (paddr - kern_start + (grub_uint8_t *) kern_chunk_src); - - return GRUB_ERR_NONE; -} - static grub_err_t grub_bsd_load_elf (grub_elf_t elf, const char *filename) { @@ -1405,12 +1322,29 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) if (grub_elf_is_elf32 (elf)) { grub_relocator_chunk_t ch; + Elf32_Phdr *phdr; entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFFF; - err = grub_elf32_phdr_iterate (elf, filename, - grub_bsd_elf32_size_hook, NULL); - if (err) - return err; + + FOR_ELF32_PHDRS (elf, phdr) + { + Elf32_Addr paddr; + + if (phdr->p_type != PT_LOAD + && phdr->p_type != PT_DYNAMIC) + continue; + + paddr = phdr->p_paddr & 0xFFFFFFF; + + if (paddr < kern_start) + kern_start = paddr; + + if (paddr + phdr->p_memsz > kern_end) + kern_end = paddr + phdr->p_memsz; + } + + if (grub_errno) + return grub_errno; err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, kern_end - kern_start); if (err) @@ -1418,7 +1352,7 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) kern_chunk_src = get_virtual_current_address (ch); - err = grub_elf32_load (elf, filename, grub_bsd_elf32_hook, 0, 0); + err = grub_elf32_load (elf, filename, (grub_uint8_t *) kern_chunk_src - kern_start, GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC | GRUB_ELF_LOAD_FLAGS_28BITS, 0, 0); if (err) return err; if (kernel_type != KERNEL_TYPE_OPENBSD) @@ -1428,6 +1362,8 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) } else if (grub_elf_is_elf64 (elf)) { + Elf64_Phdr *phdr; + is_64bit = 1; if (! grub_cpuid_has_longmode) @@ -1445,10 +1381,25 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) entry_hi = 0; } - err = grub_elf64_phdr_iterate (elf, filename, - grub_bsd_elf64_size_hook, NULL); - if (err) - return err; + FOR_ELF64_PHDRS (elf, phdr) + { + Elf64_Addr paddr; + + if (phdr->p_type != PT_LOAD + && phdr->p_type != PT_DYNAMIC) + continue; + + paddr = phdr->p_paddr & 0xFFFFFFF; + + if (paddr < kern_start) + kern_start = paddr; + + if (paddr + phdr->p_memsz > kern_end) + kern_end = paddr + phdr->p_memsz; + } + + if (grub_errno) + return grub_errno; grub_dprintf ("bsd", "kern_start = %lx, kern_end = %lx\n", (unsigned long) kern_start, (unsigned long) kern_end); @@ -1463,7 +1414,7 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) } err = grub_elf64_load (elf, filename, - grub_bsd_elf64_hook, 0, 0); + (grub_uint8_t *) kern_chunk_src - kern_start, GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC | GRUB_ELF_LOAD_FLAGS_28BITS, 0, 0); if (err) return err; if (kernel_type != KERNEL_TYPE_OPENBSD) diff --git a/grub-core/loader/i386/coreboot/chainloader.c b/grub-core/loader/i386/coreboot/chainloader.c index df4e276..505aa0b 100644 --- a/grub-core/loader/i386/coreboot/chainloader.c +++ b/grub-core/loader/i386/coreboot/chainloader.c @@ -56,36 +56,13 @@ grub_chain_unload (void) } static grub_err_t -grub_chain_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr, int *do_load) -{ - grub_err_t err; - grub_relocator_chunk_t ch; - - if (phdr->p_type != PT_LOAD) - { - *do_load = 0; - return 0; - } - - *do_load = 1; - err = grub_relocator_alloc_chunk_addr (relocator, &ch, - phdr->p_paddr, phdr->p_memsz); - if (err) - return err; - - *addr = (grub_addr_t) get_virtual_current_address (ch); - - return GRUB_ERR_NONE; -} - - -static grub_err_t grub_cmd_chain (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_err_t err; grub_file_t file; grub_elf_t elf; + Elf32_Phdr *phdr; if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -118,13 +95,47 @@ grub_cmd_chain (grub_command_t cmd __attribute__ ((unused)), grub_elf_close (elf); } - entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF; - - err = grub_elf32_load (elf, argv[0], grub_chain_elf32_hook, 0, 0); + entry = elf->ehdr.ehdr32.e_entry; + + FOR_ELF32_PHDRS(elf, phdr) + { + grub_uint8_t *load_addr; + grub_relocator_chunk_t ch; + + if (phdr->p_type != PT_LOAD) + continue; + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, + phdr->p_paddr, phdr->p_memsz); + if (err) + break; + + load_addr = get_virtual_current_address (ch); + + if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1) + return grub_errno; + + if (phdr->p_filesz) + { + grub_ssize_t read; + read = grub_file_read (elf->file, load_addr, phdr->p_filesz); + if (read != (grub_ssize_t) phdr->p_filesz) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + argv[0]); + break; + } + } + + if (phdr->p_filesz < phdr->p_memsz) + grub_memset ((load_addr + phdr->p_filesz), + 0, phdr->p_memsz - phdr->p_filesz); + } grub_elf_close (elf); - if (err) - return err; + if (grub_errno) + return grub_errno; grub_loader_set (grub_chain_boot, grub_chain_unload, 0); return GRUB_ERR_NONE; diff --git a/grub-core/loader/mips/linux.c b/grub-core/loader/mips/linux.c index f2cf7cc..653f8a2 100644 --- a/grub-core/loader/mips/linux.c +++ b/grub-core/loader/mips/linux.c @@ -144,7 +144,7 @@ grub_linux_load32 (grub_elf_t elf, const char *filename, /* Linux's entry point incorrectly contains a virtual address. */ entry_addr = elf->ehdr.ehdr32.e_entry; - linux_size = grub_elf32_size (elf, filename, &base, 0); + linux_size = grub_elf32_size (elf, &base, 0); if (linux_size == 0) return grub_errno; target_addr = base; @@ -171,22 +171,7 @@ grub_linux_load32 (grub_elf_t elf, const char *filename, *extra_mem = playground + extraoff; /* Now load the segments into the area we claimed. */ - auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load); - grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load) - { - if (phdr->p_type != PT_LOAD) - { - *do_load = 0; - return 0; - } - *do_load = 1; - - /* Linux's program headers incorrectly contain virtual addresses. - * Translate those to physical, and offset to the area we claimed. */ - *addr = (grub_addr_t) (phdr->p_paddr - base + playground); - return 0; - } - return grub_elf32_load (elf, filename, offset_phdr, 0, 0); + return grub_elf32_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); } static grub_err_t @@ -200,7 +185,7 @@ grub_linux_load64 (grub_elf_t elf, const char *filename, /* Linux's entry point incorrectly contains a virtual address. */ entry_addr = elf->ehdr.ehdr64.e_entry; - linux_size = grub_elf64_size (elf, filename, &base, 0); + linux_size = grub_elf64_size (elf, &base, 0); if (linux_size == 0) return grub_errno; target_addr = base; @@ -227,21 +212,7 @@ grub_linux_load64 (grub_elf_t elf, const char *filename, *extra_mem = playground + extraoff; /* Now load the segments into the area we claimed. */ - auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load); - grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load) - { - if (phdr->p_type != PT_LOAD) - { - *do_load = 0; - return 0; - } - *do_load = 1; - /* Linux's program headers incorrectly contain virtual addresses. - * Translate those to physical, and offset to the area we claimed. */ - *addr = (grub_addr_t) (phdr->p_paddr - base + playground); - return 0; - } - return grub_elf64_load (elf, filename, offset_phdr, 0, 0); + return grub_elf64_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); } static grub_err_t diff --git a/grub-core/loader/powerpc/ieee1275/linux.c b/grub-core/loader/powerpc/ieee1275/linux.c index c977941..9055399 100644 --- a/grub-core/loader/powerpc/ieee1275/linux.c +++ b/grub-core/loader/powerpc/ieee1275/linux.c @@ -179,7 +179,7 @@ grub_linux_load32 (grub_elf_t elf, const char *filename) grub_uint32_t offset; Elf32_Addr entry; - linux_size = grub_elf32_size (elf, filename, &base_addr, &align); + linux_size = grub_elf32_size (elf, &base_addr, &align); if (linux_size == 0) return grub_errno; /* Pad it; the kernel scribbles over memory beyond its load address. */ @@ -203,20 +203,7 @@ grub_linux_load32 (grub_elf_t elf, const char *filename) linux_addr = seg_addr; /* Now load the segments into the area we claimed. */ - auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load); - grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load) - { - if (phdr->p_type != PT_LOAD) - { - *do_load = 0; - return 0; - } - *do_load = 1; - - *addr = (phdr->p_paddr - base_addr) + seg_addr; - return 0; - } - return grub_elf32_load (elf, filename, offset_phdr, 0, 0); + return grub_elf32_load (elf, filename, (void *) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); } static grub_err_t @@ -228,7 +215,7 @@ grub_linux_load64 (grub_elf_t elf, const char *filename) grub_uint64_t offset; Elf64_Addr entry; - linux_size = grub_elf64_size (elf, filename, &base_addr, &align); + linux_size = grub_elf64_size (elf, &base_addr, &align); if (linux_size == 0) return grub_errno; /* Pad it; the kernel scribbles over memory beyond its load address. */ @@ -250,20 +237,7 @@ grub_linux_load64 (grub_elf_t elf, const char *filename) linux_addr = seg_addr; /* Now load the segments into the area we claimed. */ - auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load); - grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load) - { - if (phdr->p_type != PT_LOAD) - { - *do_load = 0; - return 0; - } - *do_load = 1; - - *addr = (phdr->p_paddr - base_addr) + seg_addr; - return 0; - } - return grub_elf64_load (elf, filename, offset_phdr, 0, 0); + return grub_elf64_load (elf, filename, (void *) (grub_addr_t) (seg_addr - base_addr), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); } static grub_err_t diff --git a/grub-core/loader/sparc64/ieee1275/linux.c b/grub-core/loader/sparc64/ieee1275/linux.c index c85fcfd..d203377 100644 --- a/grub-core/loader/sparc64/ieee1275/linux.c +++ b/grub-core/loader/sparc64/ieee1275/linux.c @@ -261,7 +261,7 @@ grub_linux_load64 (grub_elf_t elf, const char *filename) linux_entry = elf->ehdr.ehdr64.e_entry; linux_addr = 0x40004000; off = 0x4000; - linux_size = grub_elf64_size (elf, filename, 0, 0); + linux_size = grub_elf64_size (elf, 0, 0); if (linux_size == 0) return grub_errno; @@ -286,21 +286,7 @@ grub_linux_load64 (grub_elf_t elf, const char *filename) base = linux_entry - off; /* Now load the segments into the area we claimed. */ - auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load); - grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load) - { - if (phdr->p_type != PT_LOAD) - { - *do_load = 0; - return 0; - } - *do_load = 1; - - /* Adjust the program load address to linux_addr. */ - *addr = (phdr->p_paddr - base) + (linux_addr - off); - return 0; - } - return grub_elf64_load (elf, filename, offset_phdr, 0, 0); + return grub_elf64_load (elf, filename, (void *) (linux_addr - off - base), GRUB_ELF_LOAD_FLAGS_NONE, 0, 0); } static grub_err_t diff --git a/include/grub/elfload.h b/include/grub/elfload.h index d1a8d54..f854d0b 100644 --- a/include/grub/elfload.h +++ b/include/grub/elfload.h @@ -33,14 +33,10 @@ struct grub_elf_file Elf32_Ehdr ehdr32; } ehdr; void *phdrs; + char *filename; }; typedef struct grub_elf_file *grub_elf_t; -typedef grub_err_t (*grub_elf32_load_hook_t) - (Elf32_Phdr *phdr, grub_addr_t *addr, int *load); -typedef grub_err_t (*grub_elf64_load_hook_t) - (Elf64_Phdr *phdr, grub_addr_t *addr, int *load); - typedef int (*grub_elf32_phdr_iterate_hook_t) (grub_elf_t elf, Elf32_Phdr *phdr, void *arg); typedef int (*grub_elf64_phdr_iterate_hook_t) @@ -52,26 +48,31 @@ grub_err_t grub_elf_close (grub_elf_t); int grub_elf_is_elf32 (grub_elf_t); grub_size_t grub_elf32_size (grub_elf_t, - const char *filename, Elf32_Addr *, grub_uint32_t *); +enum grub_elf_load_flags + { + GRUB_ELF_LOAD_FLAGS_NONE = 0, + GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC = 1, + GRUB_ELF_LOAD_FLAGS_28BITS = 2, + }; grub_err_t grub_elf32_load (grub_elf_t, const char *filename, - grub_elf32_load_hook_t, grub_addr_t *, + void *load_offset, enum grub_elf_load_flags flags, grub_addr_t *, grub_size_t *); int grub_elf_is_elf64 (grub_elf_t); grub_size_t grub_elf64_size (grub_elf_t, - const char *filename, Elf64_Addr *, grub_uint64_t *); grub_err_t grub_elf64_load (grub_elf_t, const char *filename, - grub_elf64_load_hook_t, grub_addr_t *, + void *load_offset, enum grub_elf_load_flags flags, grub_addr_t *, grub_size_t *); -grub_err_t -grub_elf32_phdr_iterate (grub_elf_t elf, - const char *filename, - grub_elf32_phdr_iterate_hook_t hook, void *hook_arg); -grub_err_t -grub_elf64_phdr_iterate (grub_elf_t elf, - const char *filename, - grub_elf64_phdr_iterate_hook_t hook, void *hook_arg); +grub_err_t grub_elf32_load_phdrs (grub_elf_t elf); +grub_err_t grub_elf64_load_phdrs (grub_elf_t elf); + +#define FOR_ELF32_PHDRS(elf, phdr) \ + for (grub_elf32_load_phdrs (elf), phdr = elf->phdrs; \ + phdr && phdr < (Elf32_Phdr *) elf->phdrs + elf->ehdr.ehdr32.e_phnum; phdr++) +#define FOR_ELF64_PHDRS(elf, phdr) \ + for (grub_elf64_load_phdrs (elf), phdr = elf->phdrs; \ + phdr && phdr < (Elf64_Phdr *) elf->phdrs + elf->ehdr.ehdr64.e_phnum; phdr++) #endif /* ! GRUB_ELFLOAD_HEADER */ -- 1.8.1.4