diff --git a/ChangeLog b/ChangeLog index b41dd6c..912a6f3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +2009-06-13 Vladimir Serbinenko + + Load BSD ELF modules + + * conf/i386-pc.rmk (bsd_mod_SOURCES): Add loader/i386/bsd32.c + and loader/i386/bsd64.c + * include/grub/i386/bsd.h (FREEBSD_MODTYPE_MODULE): Remove + (FREEBSD_MODTYPE_ELF_MODULE): New definition + (FREEBSD_MODTYPE_ELF_MODULE_OBJ): Likewise + (grub_freebsd_load_elfmodule32): New declaration + (grub_freebsd_load_elfmoduleobj64): Likewise + (grub_freebsd_load_elf_meta32): Likewise + (grub_freebsd_load_elf_meta64): Likewise + (grub_freebsd_add_meta): Likewise + (grub_freebsd_add_meta_module): Likewise + * loader/i386/bsd.c (grub_freebsd_add_meta): Make global + (grub_freebsd_add_meta_module): Likewise and move module-specific + parts to grub_cmd_freebsd and grub_cmd_freebsd_module + (grub_cmd_freebsd): Add elf-kernel specific parts + based on grub_freebsd_add_meta_module + (grub_cmd_freebsd_module): Add type parsing moved from + grub_freebsd_add_meta_module + (grub_cmd_freebsd_module_elf): New function + (cmd_freebsd_module_elf): New variable + (GRUB_MOD_INIT): Register freebsd_module_elf + * loader/i386/bsd32.c: New file + * loader/i386/bsd64.c: Likewise + * loader/i386/bsdXX.c: Likewise + * kern/elf.c (grub_elf32_load): Let hook decide which pheaders to load + (grub_elf64_load): Likewise + * include/grub/elfload.h (grub_elf32_load_hook_t): New parameter do_load + All users updated + (grub_elf64_load_hook_t): Likewise + 2009-06-12 Pavel Roskin * Makefile.in (pkgdata_SRCDIR): Remove. genmodsrc.sh and diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index abb6fd5..47427db 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -328,7 +328,7 @@ aout_mod_CFLAGS = $(COMMON_CFLAGS) aout_mod_LDFLAGS = $(COMMON_LDFLAGS) # For bsd.mod -bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S +bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd32.c loader/i386/bsd64.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S bsd_mod_CFLAGS = $(COMMON_CFLAGS) bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) bsd_mod_ASFLAGS = $(COMMON_ASFLAGS) diff --git a/include/grub/elfload.h b/include/grub/elfload.h index 5d611da..6e09e0d 100644 --- a/include/grub/elfload.h +++ b/include/grub/elfload.h @@ -37,9 +37,9 @@ struct grub_elf_file typedef struct grub_elf_file *grub_elf_t; typedef grub_err_t (*grub_elf32_load_hook_t) - (Elf32_Phdr *phdr, grub_addr_t *addr); + (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); + (Elf64_Phdr *phdr, grub_addr_t *addr, int *load); grub_elf_t grub_elf_open (const char *); grub_elf_t grub_elf_file (grub_file_t); diff --git a/include/grub/i386/bsd.h b/include/grub/i386/bsd.h index 321b31f..3130697 100644 --- a/include/grub/i386/bsd.h +++ b/include/grub/i386/bsd.h @@ -86,7 +86,8 @@ #define FREEBSD_MODTYPE_KERNEL "elf kernel" #define FREEBSD_MODTYPE_KERNEL64 "elf64 kernel" -#define FREEBSD_MODTYPE_MODULE "elf module" +#define FREEBSD_MODTYPE_ELF_MODULE "elf module" +#define FREEBSD_MODTYPE_ELF_MODULE_OBJ "elf obj module" #define FREEBSD_MODTYPE_RAW "raw" struct grub_freebsd_bootinfo @@ -229,6 +230,21 @@ struct grub_netbsd_btinfo_bootdisk void grub_unix_real_boot (grub_addr_t entry, ...) __attribute__ ((cdecl,noreturn)); +grub_err_t grub_freebsd_load_elfmodule32 (grub_file_t file, int argc, + char *argv[], grub_addr_t *kern_end); +grub_err_t grub_freebsd_load_elfmodule_obj64 (grub_file_t file, int argc, + char *argv[], + grub_addr_t *kern_end); +grub_err_t grub_freebsd_load_elf_meta32 (grub_file_t file, + grub_addr_t *kern_end); +grub_err_t grub_freebsd_load_elf_meta64 (grub_file_t file, + grub_addr_t *kern_end); + +grub_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data, + grub_uint32_t len); +grub_err_t grub_freebsd_add_meta_module (char *filename, char *type, + int argc, char **argv, + grub_addr_t addr, grub_uint32_t size); extern grub_uint8_t grub_bsd64_trampoline_start, grub_bsd64_trampoline_end; extern grub_uint32_t grub_bsd64_trampoline_selfjump; diff --git a/include/grub/multiboot2.h b/include/grub/multiboot2.h index 69d9393..1d2324a 100644 --- a/include/grub/multiboot2.h +++ b/include/grub/multiboot2.h @@ -45,10 +45,10 @@ void grub_mb2_arch_unload (struct multiboot_tag_header *tags); grub_err_t -grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, grub_addr_t *addr); +grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, grub_addr_t *addr, int *do_load); grub_err_t -grub_mb2_arch_elf64_hook (Elf64_Phdr *phdr, grub_addr_t *addr); +grub_mb2_arch_elf64_hook (Elf64_Phdr *phdr, grub_addr_t *addr, int *do_load); grub_err_t grub_mb2_arch_module_alloc (grub_size_t size, grub_addr_t *addr); diff --git a/kern/elf.c b/kern/elf.c index 82e2484..2590552 100644 --- a/kern/elf.c +++ b/kern/elf.c @@ -228,14 +228,15 @@ grub_elf32_load (grub_elf_t _elf, grub_elf32_load_hook_t _load_hook, { grub_elf32_load_hook_t load_hook = (grub_elf32_load_hook_t) hook; grub_addr_t load_addr; - - if (phdr->p_type != PT_LOAD) - return 0; + int do_load = 1; load_addr = phdr->p_paddr; - if (load_hook && load_hook (phdr, &load_addr)) + if (load_hook && load_hook (phdr, &load_addr, &do_load)) return 1; + if (! do_load) + return 0; + if (load_addr < load_base) load_base = load_addr; @@ -407,14 +408,15 @@ grub_elf64_load (grub_elf_t _elf, grub_elf64_load_hook_t _load_hook, { grub_elf64_load_hook_t load_hook = (grub_elf64_load_hook_t) hook; grub_addr_t load_addr; - - if (phdr->p_type != PT_LOAD) - return 0; + int do_load = 1; load_addr = phdr->p_paddr; - if (load_hook && load_hook (phdr, &load_addr)) + if (load_hook && load_hook (phdr, &load_addr, &do_load)) return 1; + if (! do_load) + return 0; + if (load_addr < load_base) load_base = load_addr; diff --git a/loader/i386/bsd.c b/loader/i386/bsd.c index 6214b00..b76cfb5 100644 --- a/loader/i386/bsd.c +++ b/loader/i386/bsd.c @@ -116,7 +116,7 @@ grub_bsd_get_device (grub_uint32_t * biosdev, grub_device_close (dev); } -static grub_err_t +grub_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data, grub_uint32_t len) { if (mod_buf_max < mod_buf_len + len + 8) @@ -261,36 +261,21 @@ grub_freebsd_add_mmap (void) return grub_errno; } -static grub_err_t -grub_freebsd_add_meta_module (int is_kern, int argc, char **argv, +grub_err_t +grub_freebsd_add_meta_module (char *filename, char *type, int argc, char **argv, grub_addr_t addr, grub_uint32_t size) { - char *name, *type; - - name = grub_strrchr (argv[0], '/'); + char *name; + name = grub_strrchr (filename, '/'); if (name) name++; else - name = argv[0]; + name = filename; if (grub_freebsd_add_meta (FREEBSD_MODINFO_NAME, name, grub_strlen (name) + 1)) return grub_errno; - argc--; - argv++; - - if ((argc) && (!grub_memcmp (argv[0], "type=", 5))) - { - type = &argv[0][5]; - argc--; - argv++; - } - else - type = ((is_kern) ? - ((is_64bit) ? FREEBSD_MODTYPE_KERNEL64 : FREEBSD_MODTYPE_KERNEL) - : FREEBSD_MODTYPE_RAW); - if (is_64bit) { grub_uint64_t addr64 = addr, size64 = size; @@ -341,23 +326,6 @@ grub_freebsd_add_meta_module (int is_kern, int argc, char **argv, } } - if (is_kern) - { - int len = (is_64bit) ? 8 : 4; - grub_uint64_t data = 0; - - if ((grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | - FREEBSD_MODINFOMD_HOWTO, &data, 4)) || - (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | - FREEBSD_MODINFOMD_ENVP, &data, len)) || - (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | - FREEBSD_MODINFOMD_KERNEND, &data, len))) - return grub_errno; - kern_end_mdofs = mod_buf_len - len; - - return grub_freebsd_add_mmap (); - } - return GRUB_ERR_NONE; } @@ -688,10 +656,18 @@ grub_bsd_load_aout (grub_file_t file) } static grub_err_t -grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr) +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 &= 0xFFFFFF; paddr = phdr->p_paddr; @@ -712,10 +688,18 @@ grub_bsd_elf32_hook (Elf32_Phdr * phdr, grub_addr_t * addr) } static grub_err_t -grub_bsd_elf64_hook (Elf64_Phdr * phdr, grub_addr_t * addr) +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 & 0xffffff; if ((paddr < grub_os_area_addr) @@ -838,10 +822,54 @@ grub_cmd_freebsd (grub_command_t cmd __attribute__ ((unused)), if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE) { kern_end = ALIGN_PAGE (kern_end); - if ((is_elf_kernel) && - (grub_freebsd_add_meta_module (1, argc, argv, kern_start, - kern_end - kern_start))) - return grub_errno; + if (is_elf_kernel) + { + grub_err_t err; + grub_uint64_t data = 0; + grub_file_t file; + int len = is_64bit ? 8 : 4; + + err = grub_freebsd_add_meta_module (argv[0], is_64bit + ? FREEBSD_MODTYPE_KERNEL64 + : FREEBSD_MODTYPE_KERNEL, + argc - 1, argv + 1, + kern_start, + kern_end - kern_start); + if (err) + return err; + + file = grub_gzfile_open (argv[0], 1); + if (! file) + return grub_errno; + + if (is_64bit) + err = grub_freebsd_load_elf_meta64 (file, &kern_end); + else + err = grub_freebsd_load_elf_meta32 (file, &kern_end); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_HOWTO, &data, 4); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_ENVP, &data, len); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_KERNEND, &data, len); + if (err) + return err; + + kern_end_mdofs = mod_buf_len - len; + + err = grub_freebsd_add_mmap (); + if (err) + return err; + } grub_loader_set (grub_freebsd_boot, grub_bsd_unload, 1); } @@ -969,6 +997,10 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; + grub_err_t err; + int modargc; + char **modargv; + char *type; if (kernel_type != KERNEL_TYPE_FREEBSD) return grub_error (GRUB_ERR_BAD_ARGUMENT, @@ -996,9 +1028,27 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)), } grub_file_read (file, (void *) kern_end, file->size); - if ((!grub_errno) && - (!grub_freebsd_add_meta_module (0, argc, argv, kern_end, file->size))) - kern_end = ALIGN_PAGE (kern_end + file->size); + if (grub_errno) + goto fail; + + modargc = argc - 1; + modargv = argv + 1; + + if (modargc && (! grub_memcmp (modargv[0], "type=", 5))) + { + type = &modargv[0][5]; + modargc--; + modargv++; + } + else + type = FREEBSD_MODTYPE_RAW; + + err = grub_freebsd_add_meta_module (argv[0], type, modargc, modargv, + kern_end, file->size); + if (err) + goto fail; + + kern_end = ALIGN_PAGE (kern_end + file->size); fail: if (file) @@ -1007,8 +1057,50 @@ fail: return grub_errno; } +static grub_err_t +grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err; + + if (kernel_type != KERNEL_TYPE_FREEBSD) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "only freebsd support module"); + + if (! is_elf_kernel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "only elf kernel support module"); + + /* List the current modules if no parameter. */ + if (! argc) + { + grub_freebsd_list_modules (); + return 0; + } + + file = grub_gzfile_open (argv[0], 1); + if (!file) + return grub_errno; + if (!file->size) + { + grub_file_close (file); + return grub_errno; + } + + if (is_64bit) + err = grub_freebsd_load_elfmodule_obj64 (file, argc, argv, &kern_end); + else + err = grub_freebsd_load_elfmodule32 (file, argc, argv, &kern_end); + grub_file_close (file); + + return err; +} + + static grub_command_t cmd_freebsd, cmd_openbsd, cmd_netbsd; static grub_command_t cmd_freebsd_loadenv, cmd_freebsd_module; +static grub_command_t cmd_freebsd_module_elf; GRUB_MOD_INIT (bsd) { @@ -1027,6 +1119,9 @@ GRUB_MOD_INIT (bsd) cmd_freebsd_module = grub_register_command ("freebsd_module", grub_cmd_freebsd_module, 0, "load freebsd module"); + cmd_freebsd_module_elf = + grub_register_command ("freebsd_module_elf", grub_cmd_freebsd_module_elf, + 0, "load freebsd ELF module"); my_mod = mod; } @@ -1039,6 +1134,7 @@ GRUB_MOD_FINI (bsd) grub_unregister_command (cmd_freebsd_loadenv); grub_unregister_command (cmd_freebsd_module); + grub_unregister_command (cmd_freebsd_module_elf); if (mod_buf) { diff --git a/loader/i386/bsd32.c b/loader/i386/bsd32.c new file mode 100644 index 0000000..24dab6c --- /dev/null +++ b/loader/i386/bsd32.c @@ -0,0 +1,8 @@ +#define SUFFIX(x) x ## 32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#define Elf_Sym Elf32_Sym +#define OBJSYM 0 +#include +typedef grub_uint32_t grub_freebsd_addr_t; +#include "bsdXX.c" diff --git a/loader/i386/bsd64.c b/loader/i386/bsd64.c new file mode 100644 index 0000000..f4ff8b2 --- /dev/null +++ b/loader/i386/bsd64.c @@ -0,0 +1,8 @@ +#define SUFFIX(x) x ## 64 +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Shdr Elf64_Shdr +#define Elf_Sym Elf64_Sym +#define OBJSYM 1 +#include +typedef grub_uint64_t grub_freebsd_addr_t; +#include "bsdXX.c" diff --git a/loader/i386/bsdXX.c b/loader/i386/bsdXX.c new file mode 100644 index 0000000..3f15579 --- /dev/null +++ b/loader/i386/bsdXX.c @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include +#include + +#define ALIGN_PAGE(a) ALIGN_UP (a, 4096) + +static inline grub_err_t +load (grub_file_t file, void *where, grub_off_t off, grub_size_t size) +{ + if (PTR_TO_UINT32 (where) + size > grub_os_area_addr + grub_os_area_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Not enough memory for the module"); + if (grub_file_seek (file, off) == (grub_off_t) -1) + return grub_errno; + if (grub_file_read (file, where, size) + != (grub_ssize_t) size) + { + if (grub_errno) + return grub_errno; + else + return grub_error (GRUB_ERR_BAD_OS, "file is truncated"); + } + return GRUB_ERR_NONE; +} + +static inline grub_err_t +read_headers (grub_file_t file, Elf_Ehdr *e, char **shdr) +{ + if (grub_file_seek (file, 0) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (file, (char *) e, sizeof (*e)) != sizeof (*e)) + { + if (grub_errno) + return grub_errno; + else + return grub_error (GRUB_ERR_BAD_OS, "file is too short"); + } + + 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"); + + if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS)) + return grub_error (GRUB_ERR_BAD_OS, "invalid arch dependent ELF magic"); + + *shdr = grub_malloc (e->e_shnum * e->e_shentsize); + if (! *shdr) + return grub_errno; + + if (grub_file_seek (file, e->e_shoff) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (file, *shdr, e->e_shnum * e->e_shentsize) + != e->e_shnum * e->e_shentsize) + { + if (grub_errno) + return grub_errno; + else + return grub_error (GRUB_ERR_BAD_OS, "file is truncated"); + } + + return GRUB_ERR_NONE; +} + +/* On i386 FreeBSD uses "elf module" approarch for 32-bit variant + and "elf obj module" for 64-bit variant. However it may differ on other + platforms. So I keep both versions. */ +#if OBJSYM +grub_err_t +SUFFIX (grub_freebsd_load_elfmodule_obj) (grub_file_t file, int argc, + char *argv[], grub_addr_t *kern_end) +{ + Elf_Ehdr e; + Elf_Shdr *s; + char *shdr; + grub_addr_t curload, module; + grub_err_t err; + + err = read_headers (file, &e, &shdr); + if (err) + return err; + + curload = module = ALIGN_PAGE (*kern_end); + + for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr + + e.e_shnum * e.e_shentsize); + s = (Elf_Shdr *) ((char *) s + e.e_shentsize)) + { + if (s->sh_size == 0) + continue; + + if (s->sh_addralign) + curload = ALIGN_UP (curload, s->sh_addralign); + s->sh_addr = curload; + + grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n", + (unsigned) curload, (int) s->sh_size, + (int) s->sh_addralign); + + switch (s->sh_type) + { + default: + case SHT_PROGBITS: + err = load (file, UINT_TO_PTR (curload), s->sh_offset, s->sh_size); + if (err) + return err; + break; + case SHT_NOBITS: + if (curload + s->sh_size > grub_os_area_addr + grub_os_area_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Not enough memory for the module"); + grub_memset (UINT_TO_PTR (curload), 0, s->sh_size); + break; + } + curload += s->sh_size; + } + + *kern_end = ALIGN_PAGE (curload); + + err = grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE_OBJ, + argc - 1, argv + 1, module, + curload - module); + if (! err) + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA + | FREEBSD_MODINFOMD_ELFHDR, + &e, sizeof (e)); + if (! err) + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA + | FREEBSD_MODINFOMD_SHDR, + shdr, e.e_shnum * e.e_shentsize); + + return err; +} + +#else + +grub_err_t +SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[], + grub_addr_t *kern_end) +{ + Elf_Ehdr e; + Elf_Shdr *s; + char *shdr; + grub_addr_t curload, module; + grub_err_t err; + + err = read_headers (file, &e, &shdr); + if (err) + return err; + + curload = module = ALIGN_PAGE (*kern_end); + + for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr + + e.e_shnum * e.e_shentsize); + s = (Elf_Shdr *) ((char *) s + e.e_shentsize)) + { + if (s->sh_size == 0) + continue; + + if (! (s->sh_flags & SHF_ALLOC)) + continue; + + grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n", + (unsigned) curload, (int) s->sh_size, + (int) s->sh_addralign); + + switch (s->sh_type) + { + default: + case SHT_PROGBITS: + err = load (file, UINT_TO_PTR (module + s->sh_addr), + s->sh_offset, s->sh_size); + if (err) + return err; + break; + case SHT_NOBITS: + if (module + s->sh_addr + s->sh_size + > grub_os_area_addr + grub_os_area_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Not enough memory for the module"); + grub_memset (UINT_TO_PTR (module + s->sh_addr), 0, s->sh_size); + break; + } + if (curload < module + s->sh_addr + s->sh_size) + curload = module + s->sh_addr + s->sh_size; + } + + load (file, UINT_TO_PTR (module), 0, sizeof (e)); + if (curload < module + sizeof (e)) + curload = module + sizeof (e); + + load (file, UINT_TO_PTR (module + e.e_shoff), e.e_shoff, + e.e_shnum * e.e_shentsize); + if (curload < module + e.e_shoff + e.e_shnum * e.e_shentsize) + curload = module + e.e_shoff + e.e_shnum * e.e_shentsize; + + load (file, UINT_TO_PTR (module + e.e_phoff), e.e_phoff, + e.e_phnum * e.e_phentsize); + if (curload < module + e.e_phoff + e.e_phnum * e.e_phentsize) + curload = module + e.e_phoff + e.e_phnum * e.e_phentsize; + + *kern_end = curload; + + grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE, + argc - 1, argv + 1, module, + curload - module); + return SUFFIX (grub_freebsd_load_elf_meta) (file, kern_end); +} + +#endif + +grub_err_t +SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end) +{ + grub_err_t err; + Elf_Ehdr e; + Elf_Shdr *s; + char *shdr; + unsigned symoff, stroff, symsize, strsize; + grub_addr_t curload; + grub_freebsd_addr_t symstart, symend, symentsize, dynamic; + Elf_Sym *sym; + const char *str; + unsigned i; + + err = read_headers (file, &e, &shdr); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_ELFHDR, &e, + sizeof (e)); + if (err) + return err; + + for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr + + e.e_shnum * e.e_shentsize); + s = (Elf_Shdr *) ((char *) s + e.e_shentsize)) + if (s->sh_type == SHT_SYMTAB) + break; + if (s >= (Elf_Shdr *) ((char *) shdr + + e.e_shnum * e.e_shentsize)) + return grub_error (GRUB_ERR_BAD_OS, "no symbol table"); + symoff = s->sh_offset; + symsize = s->sh_size; + symentsize = s->sh_entsize; + s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link); + stroff = s->sh_offset; + strsize = s->sh_size; + + if (*kern_end + 4 * sizeof (grub_freebsd_addr_t) + symsize + strsize + > grub_os_area_addr + grub_os_area_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Not enough memory for kernel symbols"); + + symstart = curload = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t)); + *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = symsize; + curload += sizeof (grub_freebsd_addr_t); + if (grub_file_seek (file, symoff) == (grub_off_t) -1) + return grub_errno; + sym = (Elf_Sym *) UINT_TO_PTR (curload); + if (grub_file_read (file, UINT_TO_PTR (curload), symsize) != + (grub_ssize_t) symsize) + { + if (! grub_errno) + return grub_error (GRUB_ERR_BAD_OS, "invalid elf"); + return grub_errno; + } + curload += symsize; + + *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = strsize; + curload += sizeof (grub_freebsd_addr_t); + if (grub_file_seek (file, stroff) == (grub_off_t) -1) + return grub_errno; + str = (char *) UINT_TO_PTR (curload); + if (grub_file_read (file, UINT_TO_PTR (curload), strsize) + != (grub_ssize_t) strsize) + { + if (! grub_errno) + return grub_error (GRUB_ERR_BAD_OS, "invalid elf"); + return grub_errno; + } + curload += strsize; + curload = ALIGN_UP (curload, sizeof (grub_freebsd_addr_t)); + symend = curload; + + for (i = 0; + i * symentsize < symsize; + i++, sym = (Elf_Sym *) ((char *) sym + symentsize)) + { + const char *name = str + sym->st_name; + if (grub_strcmp (name, "_DYNAMIC") == 0) + break; + } + + if (i * symentsize < symsize) + { + dynamic = sym->st_value; + grub_dprintf ("bsd", "dynamic = %llx\n", (unsigned long long) dynamic); + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_DYNAMIC, &dynamic, + sizeof (dynamic)); + if (err) + return err; + } + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_SSYM, &symstart, + sizeof (symstart)); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_ESYM, &symend, + sizeof (symend)); + if (err) + return err; + *kern_end = ALIGN_PAGE (curload); + + return GRUB_ERR_NONE; +} diff --git a/loader/i386/pc/multiboot2.c b/loader/i386/pc/multiboot2.c index dadbccd..3baf488 100644 --- a/loader/i386/pc/multiboot2.c +++ b/loader/i386/pc/multiboot2.c @@ -27,10 +27,18 @@ #include grub_err_t -grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, UNUSED grub_addr_t *addr) +grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, UNUSED grub_addr_t *addr, + int *do_load) { Elf32_Addr paddr = phdr->p_paddr; + if (phdr->p_type != PT_LOAD) + { + *do_load = 0; + return 0; + } + *do_load = 1; + if ((paddr < grub_os_area_addr) || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size)) return grub_error(GRUB_ERR_OUT_OF_RANGE,"Address 0x%x is out of range", @@ -40,10 +48,18 @@ grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, UNUSED grub_addr_t *addr) } grub_err_t -grub_mb2_arch_elf64_hook (Elf64_Phdr *phdr, UNUSED grub_addr_t *addr) +grub_mb2_arch_elf64_hook (Elf64_Phdr *phdr, UNUSED grub_addr_t *addr, + int *do_load) { Elf64_Addr paddr = phdr->p_paddr; + if (phdr->p_type != PT_LOAD) + { + *do_load = 0; + return 0; + } + *do_load = 1; + if ((paddr < grub_os_area_addr) || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size)) return grub_error (GRUB_ERR_OUT_OF_RANGE, "Address 0x%x is out of range", diff --git a/loader/ieee1275/multiboot2.c b/loader/ieee1275/multiboot2.c index d1444ce..fda62fc 100644 --- a/loader/ieee1275/multiboot2.c +++ b/loader/ieee1275/multiboot2.c @@ -36,10 +36,18 @@ typedef void (*kernel_entry_t) (unsigned long, void *, int (void *), /* Claim the memory occupied by the multiboot kernel. */ grub_err_t -grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, UNUSED grub_addr_t *addr) +grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, UNUSED grub_addr_t *addr, + int *do_load) { int rc; + if (phdr->p_type != PT_LOAD) + { + *do_load = 0; + return 0; + } + *do_load = 1; + rc = grub_claimmap (phdr->p_paddr, phdr->p_memsz); if (rc) return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Couldn't claim %x - %x", @@ -53,10 +61,18 @@ grub_mb2_arch_elf32_hook (Elf32_Phdr *phdr, UNUSED grub_addr_t *addr) /* Claim the memory occupied by the multiboot kernel. */ grub_err_t -grub_mb2_arch_elf64_hook (Elf64_Phdr *phdr, UNUSED grub_addr_t *addr) +grub_mb2_arch_elf64_hook (Elf64_Phdr *phdr, UNUSED grub_addr_t *addr, + int *do_load) { int rc; + if (phdr->p_type != PT_LOAD) + { + *do_load = 0; + return 0; + } + *do_load = 1; + rc = grub_claimmap (phdr->p_paddr, phdr->p_memsz); if (rc) return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Couldn't claim 0x%lx - 0x%lx", diff --git a/loader/powerpc/ieee1275/linux.c b/loader/powerpc/ieee1275/linux.c index 04c9b01..f183c86 100644 --- a/loader/powerpc/ieee1275/linux.c +++ b/loader/powerpc/ieee1275/linux.c @@ -132,8 +132,15 @@ grub_linux_load32 (grub_elf_t elf) /* Now load the segments into the area we claimed. */ auto grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr); - grub_err_t offset_phdr (Elf32_Phdr *phdr, grub_addr_t *addr) + 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 = (phdr->p_paddr & ~ELF32_LOADMASK) + linux_addr; @@ -175,8 +182,14 @@ grub_linux_load64 (grub_elf_t elf) /* Now load the segments into the area we claimed. */ auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr); - grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr) + 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 = (phdr->p_paddr & ~ELF64_LOADMASK) + linux_addr; diff --git a/loader/sparc64/ieee1275/linux.c b/loader/sparc64/ieee1275/linux.c index 288ee3a..d0623f3 100644 --- a/loader/sparc64/ieee1275/linux.c +++ b/loader/sparc64/ieee1275/linux.c @@ -272,8 +272,15 @@ grub_linux_load64 (grub_elf_t elf) /* Now load the segments into the area we claimed. */ auto grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr); - grub_err_t offset_phdr (Elf64_Phdr *phdr, grub_addr_t *addr) + 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;