Ady
2015-Oct-10 08:14 UTC
[syslinux] [PATCH] Extend Multiboot1 with support for ELF64 file format
This patch is just a (shameless) copy from bug #28 [1]. The original patch has been sitting there for a couple of years now, and it was based on Syslinux v 4.05. The patch I am posting here is just a re-base on v.6.03. Whichever inadequate formatting (tabs, trailing spaces...), or any kind of correction that was required for the original patch to be accepted, is probably still required now. I have not tested this patch in any way, at all. I do not know whether it is formatted adequately, whether it actually builds correctly, nor whether the potential resulting binaries would work as expected in any case. The only "credit" (for lack of a better term) that could be attributed to me could be bringing up the existence of this pending-patch to the official Syslinux mailing list, after 2 years of being initially posted to the official Syslinux Bugzilla tracker. Credit should go to the original poster. I am hoping this pending-for-review-patch can be evaluated, (corrected, improved) and eventually accepted and merged in the official Syslinux code. Please don't leave this patch awaiting another 2 years. TIA, Ady. [1]: http://bugzilla.syslinux.org/show_bug.cgi?id=28 Extend Multiboot1 with support for ELF64 file format diff U3 syslinux-6.03/com32/mboot/map.c syslinux-6.03_mbootELF64/com32/mboot/map.c --- syslinux-6.03/com32/mboot/map.c Mon Oct 06 19:27:44 2014 +++ syslinux-6.03_mbootELF64/com32/mboot/map.c Sat Oct 10 09:13:45 2015 @@ -106,6 +106,11 @@ Elf32_Ehdr *eh = ptr; Elf32_Phdr *ph; Elf32_Shdr *sh; + + Elf64_Ehdr *eh64 = ptr; + Elf64_Phdr *ph64; + Elf64_Shdr *sh64; + unsigned int i, mbh_offset; uint32_t bad_flags; @@ -150,6 +155,17 @@ !eh->e_phnum || eh->e_phoff + eh->e_phentsize * eh->e_phnum > len) eh = NULL; /* No valid ELF header found */ + /* Determine 64-bit images */ + if ((eh != NULL) || + len < sizeof(Elf64_Ehdr) || + memcmp(eh64->e_ident, "\x7f" "ELF\2\1\1", 6) || + (eh64->e_machine != EM_X86_64) || + eh64->e_version != EV_CURRENT || + eh64->e_ehsize < sizeof(Elf64_Ehdr) || eh64->e_ehsize >= len || + eh64->e_phentsize < sizeof(Elf64_Phdr) || + !eh64->e_phnum || eh64->e_phoff + eh64->e_phentsize * eh64->e_phnum > len) + eh64 = NULL; /* No valid ELF64 header found */ + /* Is this a Solaris kernel? */ if (!set.solaris && eh && kernel_is_solaris(eh)) opt.solaris = true; @@ -263,6 +279,112 @@ return NULL; } sh[i].sh_addr = addr; + } + } + } else if (eh64 && !(opt.aout && mbh_len && + (mbh->flags & MULTIBOOT_AOUT_KLUDGE))) { + /* Load 64-bit ELF */ + regs.eip = eh64->e_entry; /* Can be overridden further down... */ + + ph64 = (Elf64_Phdr *) (cptr + eh64->e_phoff); + + for (i = 0; i < eh64->e_phnum; i++) { + if (ph64->p_type == PT_LOAD || ph64->p_type == PT_PHDR) { + /* + * This loads at p_paddr, which matches Grub. However, if + * e_entry falls within the p_vaddr range of this PHDR, then + * adjust it to match the p_paddr range... this is how Grub + * behaves, so it's by definition correct (it doesn't have to + * make sense...) + */ + addr_t addr = ph64->p_paddr; + addr_t msize = ph64->p_memsz; + addr_t dsize = min(msize, ph64->p_filesz); + + if (eh64->e_entry >= ph64->p_vaddr + && eh64->e_entry < ph64->p_vaddr + msize) + regs.eip = eh64->e_entry + (ph64->p_paddr - ph64->p_vaddr); + + dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n", + addr, dsize, msize); + + if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) { + printf + ("Memory segment at 0x%08x (len 0x%08x) is unavailable\n", + addr, msize); + return NULL; /* Memory region unavailable */ + } + + /* Mark this region as allocated in the available map */ + if (syslinux_add_memmap(&amap, addr, msize, SMT_ALLOC)) { + error("Overlapping segments found in ELF header\n"); + return NULL; + } + + if (ph64->p_filesz) { + /* Data present region. Create a move entry for it. */ + if (syslinux_add_movelist + (&ml, addr, (addr_t) cptr + ph64->p_offset, dsize)) { + error("Failed to map PHDR data\n"); + return NULL; + } + } + if (msize > dsize) { + /* Zero-filled region. Mark as a zero region in the memory map. */ + if (syslinux_add_memmap + (&mmap, addr + dsize, msize - dsize, SMT_ZERO)) { + error("Failed to map PHDR zero region\n"); + return NULL; + } + } + if (addr + msize > mboot_high_water_mark) + mboot_high_water_mark = addr + msize; + } else { + /* Ignore this program header */ + } + + ph64 = (Elf64_Phdr *) ((char *)ph64 + eh64->e_phentsize); + } + + /* Load the ELF symbol table */ + if (eh64->e_shoff) { + addr_t addr, len; + + sh64 = (Elf64_Shdr *) ((char *)eh64 + eh64->e_shoff); + + len = eh64->e_shentsize * eh64->e_shnum; + /* + * Align this, but don't pad -- in general this means a bunch of + * smaller sections gets packed into a single page. + */ + addr = map_data(sh64, len, 4096, MAP_HIGH | MAP_NOPAD); + if (!addr) { + error("Failed to map symbol table\n"); + return NULL; + } + + mbinfo.flags |= MB_INFO_ELF_SHDR; + mbinfo.syms.e.addr = addr; + mbinfo.syms.e.num = eh64->e_shnum; + mbinfo.syms.e.size = eh64->e_shentsize; + mbinfo.syms.e.shndx = eh64->e_shstrndx; + + for (i = 0; i < eh64->e_shnum; i++) { + addr_t align; + + if (!sh64[i].sh_size) + continue; /* Empty section */ + if (sh64[i].sh_flags & SHF_ALLOC) + continue; /* SHF_ALLOC sections should have PHDRs */ + + align = sh64[i].sh_addralign ? sh64[i].sh_addralign : 0; + addr = map_data((char *)ptr + sh64[i].sh_offset, + sh64[i].sh_size, align, MAP_HIGH); + if (!addr) { + error("Failed to map symbol section\n"); + return NULL; + } + sh64[i].sh_addr = addr; } } } else if (mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE)) { diff U3 syslinux-6.03/com32/mboot/mboot.h syslinux-6.03_mbootELF64/com32/mboot/mboot.h --- syslinux-6.03/com32/mboot/mboot.h Mon Oct 06 19:27:44 2014 +++ syslinux-6.03_mbootELF64/com32/mboot/mboot.h Sat Oct 10 09:11:57 2015 @@ -46,6 +46,7 @@ #include <minmax.h> #include <sys/stat.h> #include <elf.h> +#include <sys/elf64.h> #include <console.h> #include <syslinux/loadfile.h> --
Geert Stappers
2015-Oct-10 10:54 UTC
[syslinux] Extend Multiboot1 with support for ELF64 file format
On Sat, Oct 10, 2015 at 11:14:27AM +0300, Ady via Syslinux wrote:> This patch is just a (shameless) copy from bug #28 [1]. > > The original patch has been sitting there for a couple of years now, > and it was based on Syslinux v 4.05.:-/> The patch I am posting here is just a re-base on v.6.03. Whichever > inadequate formatting (tabs, trailing spaces...), or any kind of > correction that was required for the original patch to be accepted, is > probably still required now.I will removel trailing spaces in an additional patch.> I have not tested this patch in any way, at all. I do not know whether > it is formatted adequately, whether it actually builds correctly,It applied cleanly and builds without errors.> nor whether the potential resulting binaries would work as expected > in any case.Here we will need help from people that do use mboot module mboot.c32> The only "credit" (for lack of a better term) that could be attributed > to me could be bringing up the existence of this pending-patch to the > official Syslinux mailing list, after 2 years of being initially posted > to the official Syslinux Bugzilla tracker. Credit should go to the > original poster.`git format-patch` is work in progress, it's output will have the original poster as author.> I am hoping this pending-for-review-patch can be evaluated, (corrected, > improved) and eventually accepted and merged in the official Syslinux > code. > > Please don't leave this patch awaiting another 2 years. > > TIA, > Ady. > > [1]: http://bugzilla.syslinux.org/show_bug.cgi?id=28 >Groeten Geert Stappers -- Leven en laten leven
modified: com32/mboot/map.c modified: com32/mboot/mboot.h --- com32/mboot/map.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++ com32/mboot/mboot.h | 1 + 2 files changed, 123 insertions(+) diff --git a/com32/mboot/map.c b/com32/mboot/map.c index 4b0baa2..2e8641f 100644 --- a/com32/mboot/map.c +++ b/com32/mboot/map.c @@ -106,6 +106,11 @@ struct multiboot_header *map_image(void *ptr, size_t len) Elf32_Ehdr *eh = ptr; Elf32_Phdr *ph; Elf32_Shdr *sh; + + Elf64_Ehdr *eh64 = ptr; + Elf64_Phdr *ph64; + Elf64_Shdr *sh64; + unsigned int i, mbh_offset; uint32_t bad_flags; @@ -150,6 +155,17 @@ struct multiboot_header *map_image(void *ptr, size_t len) !eh->e_phnum || eh->e_phoff + eh->e_phentsize * eh->e_phnum > len) eh = NULL; /* No valid ELF header found */ + /* Determine 64-bit images */ + if ((eh != NULL) || + len < sizeof(Elf64_Ehdr) || + memcmp(eh64->e_ident, "\x7f" "ELF\2\1\1", 6) || + (eh64->e_machine != EM_X86_64) || + eh64->e_version != EV_CURRENT || + eh64->e_ehsize < sizeof(Elf64_Ehdr) || eh64->e_ehsize >= len || + eh64->e_phentsize < sizeof(Elf64_Phdr) || + !eh64->e_phnum || eh64->e_phoff + eh64->e_phentsize * eh64->e_phnum > len) + eh64 = NULL; /* No valid ELF64 header found */ + /* Is this a Solaris kernel? */ if (!set.solaris && eh && kernel_is_solaris(eh)) opt.solaris = true; @@ -265,6 +281,112 @@ struct multiboot_header *map_image(void *ptr, size_t len) sh[i].sh_addr = addr; } } + } else if (eh64 && !(opt.aout && mbh_len && + (mbh->flags & MULTIBOOT_AOUT_KLUDGE))) { + /* Load 64-bit ELF */ + regs.eip = eh64->e_entry; /* Can be overridden further down... */ + + ph64 = (Elf64_Phdr *) (cptr + eh64->e_phoff); + + for (i = 0; i < eh64->e_phnum; i++) { + if (ph64->p_type == PT_LOAD || ph64->p_type == PT_PHDR) { + /* + * This loads at p_paddr, which matches Grub. However, if + * e_entry falls within the p_vaddr range of this PHDR, then + * adjust it to match the p_paddr range... this is how Grub + * behaves, so it's by definition correct (it doesn't have to + * make sense...) + */ + addr_t addr = ph64->p_paddr; + addr_t msize = ph64->p_memsz; + addr_t dsize = min(msize, ph64->p_filesz); + + if (eh64->e_entry >= ph64->p_vaddr + && eh64->e_entry < ph64->p_vaddr + msize) + regs.eip = eh64->e_entry + (ph64->p_paddr - ph64->p_vaddr); + + dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n", + addr, dsize, msize); + + if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) { + printf + ("Memory segment at 0x%08x (len 0x%08x) is unavailable\n", + addr, msize); + return NULL; /* Memory region unavailable */ + } + + /* Mark this region as allocated in the available map */ + if (syslinux_add_memmap(&amap, addr, msize, SMT_ALLOC)) { + error("Overlapping segments found in ELF header\n"); + return NULL; + } + + if (ph64->p_filesz) { + /* Data present region. Create a move entry for it. */ + if (syslinux_add_movelist + (&ml, addr, (addr_t) cptr + ph64->p_offset, dsize)) { + error("Failed to map PHDR data\n"); + return NULL; + } + } + if (msize > dsize) { + /* Zero-filled region. Mark as a zero region in the memory map. */ + if (syslinux_add_memmap + (&mmap, addr + dsize, msize - dsize, SMT_ZERO)) { + error("Failed to map PHDR zero region\n"); + return NULL; + } + } + if (addr + msize > mboot_high_water_mark) + mboot_high_water_mark = addr + msize; + } else { + /* Ignore this program header */ + } + + ph64 = (Elf64_Phdr *) ((char *)ph64 + eh64->e_phentsize); + } + + /* Load the ELF symbol table */ + if (eh64->e_shoff) { + addr_t addr, len; + + sh64 = (Elf64_Shdr *) ((char *)eh64 + eh64->e_shoff); + + len = eh64->e_shentsize * eh64->e_shnum; + /* + * Align this, but don't pad -- in general this means a bunch of + * smaller sections gets packed into a single page. + */ + addr = map_data(sh64, len, 4096, MAP_HIGH | MAP_NOPAD); + if (!addr) { + error("Failed to map symbol table\n"); + return NULL; + } + + mbinfo.flags |= MB_INFO_ELF_SHDR; + mbinfo.syms.e.addr = addr; + mbinfo.syms.e.num = eh64->e_shnum; + mbinfo.syms.e.size = eh64->e_shentsize; + mbinfo.syms.e.shndx = eh64->e_shstrndx; + + for (i = 0; i < eh64->e_shnum; i++) { + addr_t align; + + if (!sh64[i].sh_size) + continue; /* Empty section */ + if (sh64[i].sh_flags & SHF_ALLOC) + continue; /* SHF_ALLOC sections should have PHDRs */ + + align = sh64[i].sh_addralign ? sh64[i].sh_addralign : 0; + addr = map_data((char *)ptr + sh64[i].sh_offset, + sh64[i].sh_size, align, MAP_HIGH); + if (!addr) { + error("Failed to map symbol section\n"); + return NULL; + } + sh64[i].sh_addr = addr; + } + } } else if (mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE)) { /* * a.out kludge thing... diff --git a/com32/mboot/mboot.h b/com32/mboot/mboot.h index da6ca2f..9f9e510 100644 --- a/com32/mboot/mboot.h +++ b/com32/mboot/mboot.h @@ -46,6 +46,7 @@ #include <minmax.h> #include <sys/stat.h> #include <elf.h> +#include <sys/elf64.h> #include <console.h> #include <syslinux/loadfile.h> -- 2.0.0
Geert Stappers
2015-Oct-10 11:29 UTC
[syslinux] [PATCH 2/2] com32/mboot/map.c: removed trailing spaces
From: Geert Stappers <stappers at nero.gpm.stappers.nl> They were introduced by the patch for ELF64 support. --- com32/mboot/map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/com32/mboot/map.c b/com32/mboot/map.c index 2e8641f..1992f14 100644 --- a/com32/mboot/map.c +++ b/com32/mboot/map.c @@ -281,7 +281,7 @@ struct multiboot_header *map_image(void *ptr, size_t len) sh[i].sh_addr = addr; } } - } else if (eh64 && !(opt.aout && mbh_len && + } else if (eh64 && !(opt.aout && mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE))) { /* Load 64-bit ELF */ regs.eip = eh64->e_entry; /* Can be overridden further down... */ @@ -378,7 +378,7 @@ struct multiboot_header *map_image(void *ptr, size_t len) continue; /* SHF_ALLOC sections should have PHDRs */ align = sh64[i].sh_addralign ? sh64[i].sh_addralign : 0; - addr = map_data((char *)ptr + sh64[i].sh_offset, + addr = map_data((char *)ptr + sh64[i].sh_offset, sh64[i].sh_size, align, MAP_HIGH); if (!addr) { error("Failed to map symbol section\n"); -- 2.0.0
Apparently Analagous Threads
- [PATCH] Extend Multiboot1 with support for ELF64 file format
- com32/mboot/map.c question
- [PATCH] chain.c32: support chainloading GRUB2 core.img
- [PATCH v3 23/27] x86/modules: Adapt module loading for PIE support
- [klibc:master] README.klibc: Refresh architecture list