summaryrefslogtreecommitdiff
path: root/com32/mboot
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2009-05-03 18:35:46 -0700
committerH. Peter Anvin <hpa@zytor.com>2009-05-03 18:35:46 -0700
commitde1ecabd369a2105f7822f4f8be10fb1937b9f86 (patch)
tree25ddc00ab45ff8bd0f54c67c6f87cc0d242eff85 /com32/mboot
parent71e120ecacc2e124ccda5ba5ada48ad860e10dd5 (diff)
downloadsyslinux-de1ecabd369a2105f7822f4f8be10fb1937b9f86.tar.gz
mboot: handle ELF Multiboot kernel where paddr != vaddr
The way Grub handles ELF Multiboot kernels where paddr != vaddr is to load at the paddr, but to also adjust the entry point (e_entry) so that the previous vaddr becomes a paddr. Since the Multiboot spec is pretty much "Grub wins", follow this behavior. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'com32/mboot')
-rw-r--r--com32/mboot/map.c15
1 files changed, 11 insertions, 4 deletions
diff --git a/com32/mboot/map.c b/com32/mboot/map.c
index a84fc234..15e3a28a 100644
--- a/com32/mboot/map.c
+++ b/com32/mboot/map.c
@@ -161,19 +161,26 @@ int map_image(void *ptr, size_t len)
* useless, but at least Solaris apparently depends on this behavior.
*/
if (eh && !(opt.aout && mbh_len && (mbh->flags & MULTIBOOT_AOUT_KLUDGE))) {
- regs.eip = eh->e_entry;
+ regs.eip = eh->e_entry; /* Can be overridden further down... */
ph = (Elf32_Phdr *)(cptr+eh->e_phoff);
for (i = 0; i < eh->e_phnum; i++) {
if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
- /* This loads at p_paddr, which is arguably the correct semantics.
- The SysV spec says that SysV loads at p_vaddr (and thus Linux does,
- too); that is, however, a major brainfuckage in the spec. */
+ /*
+ * 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 = ph->p_paddr;
addr_t msize = ph->p_memsz;
addr_t dsize = min(msize, ph->p_filesz);
+ if (eh->e_entry >= ph->p_vaddr && eh->e_entry < ph->p_vaddr + msize)
+ regs.eip = eh->e_entry + (ph->p_paddr - ph->p_vaddr);
+
dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
addr, dsize, msize);