From: Robert Millan <rmh@aybabtu.com>
To: The development of GRUB 2 <grub-devel@gnu.org>
Cc: "Yoshinori K. Okuji" <okuji@enbug.org>
Subject: Re: loadee relocation (Re: loader modules jumping back to kernel)
Date: Sat, 2 Aug 2008 00:45:20 +0200 [thread overview]
Message-ID: <20080801224520.GA12392@thorin> (raw)
In-Reply-To: <20080801161606.GA31439@thorin>
[-- Attachment #1: Type: text/plain, Size: 1326 bytes --]
On Fri, Aug 01, 2008 at 06:16:06PM +0200, Robert Millan wrote:
> On Fri, Aug 01, 2008 at 01:45:30AM +0200, Robert Millan wrote:
> >
> > - What to do about physical_entry_addr now? My patch currently discards
> > it, which I suppose is not what we want.
>
> Fixed after some discussion with Bean on IRC. This version of the patch
> should handle physical_entry_addr fine.
Then again, I still got spurious crashes when trying my code with:
ftp://ftp.netbsd.org/pub/NetBSD-daily/netbsd-4/200807310002Z/i386/binary/kernel/netbsd-GENERIC.gz
In case someone is curious, the problems that made me spend all day debugging
are:
grub_multiboot_payload_entry_offset was defined with a 64-bit type but
allocated with ".long 0" in loader.S, resulting in the first 4 bytes of
grub_multiboot_real_boot being fucked up occasionally.
%edi was off-by-one in the backward relocator, which was not usually a
problem for invaders (what harm can one byte do?) but broke netbsd.
Lessons learned: gdb is your friend, and is definitely worth the hassle
of setting up for use in QEMU/GRUB.
--
Robert Millan
The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
how) you may access your data; but nobody's threatening your freedom: we
still allow you to remove your data and not access it at all."
[-- Attachment #2: relocate_payload.diff --]
[-- Type: text/x-diff, Size: 7555 bytes --]
2008-08-01 Robert Millan <rmh@aybabtu.com>
* loader/i386/pc/multiboot.c (playground, forward_relocator)
(backward_relocator): New variables. Used to allocate and relocate
the payload, respectively.
(grub_multiboot_load_elf32): Load into heap instead of requested
address, install the appropiate relocator code in each bound of
the payload, and set the entry point such that
grub_multiboot_real_boot() will jump to one of them.
* kern/i386/loader.S (grub_multiboot_payload_size)
(grub_multiboot_payload_orig, grub_multiboot_payload_dest)
(grub_multiboot_payload_entry_offset): New variables.
(grub_multiboot_real_boot): Set cpu context to what the relocator
expects, and jump to the relocator instead of the payload.
* include/grub/i386/pc/loader.h (grub_multiboot_payload_size)
(grub_multiboot_payload_orig, grub_multiboot_payload_dest)
(grub_multiboot_payload_entry_offset): Export.
Index: kern/i386/loader.S
===================================================================
--- kern/i386/loader.S (revision 1758)
+++ kern/i386/loader.S (working copy)
@@ -123,6 +123,15 @@
* This starts the multiboot kernel.
*/
+VARIABLE(grub_multiboot_payload_size)
+ .long 0
+VARIABLE(grub_multiboot_payload_orig)
+ .long 0
+VARIABLE(grub_multiboot_payload_dest)
+ .long 0
+VARIABLE(grub_multiboot_payload_entry_offset)
+ .long 0
+
FUNCTION(grub_multiboot_real_boot)
/* Push the entry address on the stack. */
pushl %eax
@@ -136,11 +145,16 @@
/* Interrupts should be disabled. */
cli
- /* Move the magic value into eax and jump to the kernel. */
- movl $MULTIBOOT_MAGIC2,%eax
- popl %ecx
- jmp *%ecx
-
+ /* Where do we copy what from. */
+ movl EXT_C(grub_multiboot_payload_size), %ecx
+ movl EXT_C(grub_multiboot_payload_orig), %esi
+ movl EXT_C(grub_multiboot_payload_dest), %edi
+ movl EXT_C(grub_multiboot_payload_entry_offset), %eax
+
+ /* Jump to the relocator. */
+ popl %edx
+ jmp *%edx
+
/*
* This starts the multiboot 2 kernel.
*/
Index: include/grub/i386/pc/loader.h
===================================================================
--- include/grub/i386/pc/loader.h (revision 1758)
+++ include/grub/i386/pc/loader.h (working copy)
@@ -25,4 +25,9 @@
/* This is an asm part of the chainloader. */
void EXPORT_FUNC(grub_chainloader_real_boot) (int drive, void *part_addr) __attribute__ ((noreturn));
+extern grub_addr_t EXPORT_VAR(grub_multiboot_payload_orig);
+extern grub_addr_t EXPORT_VAR(grub_multiboot_payload_dest);
+extern grub_size_t EXPORT_VAR(grub_multiboot_payload_size);
+extern grub_uint32_t EXPORT_VAR(grub_multiboot_payload_entry_offset);
+
#endif /* ! GRUB_LOADER_MACHINE_HEADER */
Index: loader/i386/pc/multiboot.c
===================================================================
--- loader/i386/pc/multiboot.c (revision 1758)
+++ loader/i386/pc/multiboot.c (working copy)
@@ -50,6 +50,33 @@
static struct grub_multiboot_info *mbi;
static grub_addr_t entry;
+static char *playground = NULL;
+
+static grub_uint8_t forward_relocator[] =
+{
+ 0xfc, /* cld */
+ 0x89, 0xf2, /* movl %esi, %edx */
+ 0xf3, 0xa4, /* rep movsb */
+ 0x01, 0xc2, /* addl %eax, %edx */
+ 0xb8, 0x02, 0xb0, 0xad, 0x2b, /* movl $MULTIBOOT_MAGIC2, %eax */
+ 0xff, 0xe2, /* jmp *%edx */
+};
+
+static grub_uint8_t backward_relocator[] =
+{
+ 0xfd, /* std */
+ 0x01, 0xce, /* addl %ecx, %esi */
+ 0x01, 0xcf, /* addl %ecx, %edi */
+ /* backward movsb is implicitly off-by-one. compensate that. */
+ 0x41, /* incl %ecx */
+ 0xf3, 0xa4, /* rep movsb */
+ /* same problem again. */
+ 0x47, /* incl %edi */
+ 0x01, 0xc7, /* addl %eax, %edi */
+ 0xb8, 0x02, 0xb0, 0xad, 0x2b, /* movl $MULTIBOOT_MAGIC2, %eax */
+ 0xff, 0xe7, /* jmp *%edi */
+};
+
static grub_err_t
grub_multiboot_boot (void)
{
@@ -99,6 +126,7 @@
Elf32_Ehdr *ehdr = (Elf32_Ehdr *) buffer;
char *phdr_base;
grub_addr_t physical_entry_addr = 0;
+ int lowest_segment = 0, highest_segment = 0;
int i;
if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
@@ -114,50 +142,62 @@
if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
- entry = ehdr->e_entry;
-
phdr_base = (char *) buffer + ehdr->e_phoff;
#define phdr(i) ((Elf32_Phdr *) (phdr_base + (i) * ehdr->e_phentsize))
+ for (i = 0; i < ehdr->e_phnum; i++)
+ if (phdr(i)->p_type == PT_LOAD)
+ {
+ if (phdr(i)->p_paddr < phdr(lowest_segment)->p_paddr)
+ lowest_segment = i;
+ if (phdr(i)->p_paddr > phdr(highest_segment)->p_paddr)
+ highest_segment = i;
+ }
+ grub_multiboot_payload_size = (phdr(highest_segment)->p_paddr + phdr(highest_segment)->p_memsz) - phdr(lowest_segment)->p_paddr;
+ grub_multiboot_payload_dest = phdr(lowest_segment)->p_paddr;
+
+ if (playground)
+ grub_free (playground);
+ playground = grub_malloc (sizeof (forward_relocator) + grub_multiboot_payload_size + sizeof (backward_relocator));
+ if (! playground)
+ return grub_errno;
+
+ grub_multiboot_payload_orig = playground + sizeof (forward_relocator);
+
+ grub_memmove (playground, forward_relocator, sizeof (forward_relocator));
+ grub_memmove (grub_multiboot_payload_orig + grub_multiboot_payload_size, backward_relocator, sizeof (backward_relocator));
+
/* Load every loadable segment in memory. */
for (i = 0; i < ehdr->e_phnum; i++)
{
if (phdr(i)->p_type == PT_LOAD)
{
- /* The segment should fit in the area reserved for the OS. */
- if (phdr(i)->p_paddr < grub_os_area_addr)
- return grub_error (GRUB_ERR_BAD_OS,
- "segment doesn't fit in memory reserved for the OS (0x%lx < 0x%lx)",
- phdr(i)->p_paddr, grub_os_area_addr);
- if (phdr(i)->p_paddr + phdr(i)->p_memsz > grub_os_area_addr + grub_os_area_size)
- return grub_error (GRUB_ERR_BAD_OS,
- "segment doesn't fit in memory reserved for the OS (0x%lx > 0x%lx)",
- phdr(i)->p_paddr + phdr(i)->p_memsz,
- grub_os_area_addr + grub_os_area_size);
+ char *load_this_module_at = grub_multiboot_payload_orig + (phdr(i)->p_paddr - phdr(0)->p_paddr);
- if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset)
+ if (grub_file_seek (file, (grub_off_t) phdr(i)->p_offset)
== (grub_off_t) -1)
return grub_error (GRUB_ERR_BAD_OS,
"invalid offset in program header");
- if (grub_file_read (file, (void *) phdr(i)->p_paddr, phdr(i)->p_filesz)
+ if (grub_file_read (file, load_this_module_at, phdr(i)->p_filesz)
!= (grub_ssize_t) phdr(i)->p_filesz)
return grub_error (GRUB_ERR_BAD_OS,
"couldn't read segment from file");
if (phdr(i)->p_filesz < phdr(i)->p_memsz)
- grub_memset ((char *) phdr(i)->p_paddr + phdr(i)->p_filesz, 0,
+ grub_memset (load_this_module_at + phdr(i)->p_filesz, 0,
phdr(i)->p_memsz - phdr(i)->p_filesz);
-
- if ((entry >= phdr(i)->p_vaddr) &&
- (entry < phdr(i)->p_vaddr + phdr(i)->p_memsz))
- physical_entry_addr = entry + phdr(i)->p_paddr - phdr(i)->p_vaddr;
}
}
+
+ grub_multiboot_payload_entry_offset = ehdr->e_entry - phdr(lowest_segment)->p_vaddr;
+
#undef phdr
- if (physical_entry_addr)
- entry = physical_entry_addr;
+ if (grub_multiboot_payload_dest >= grub_multiboot_payload_orig)
+ entry = (grub_addr_t) playground;
+ else
+ entry = (grub_addr_t) grub_multiboot_payload_orig + grub_multiboot_payload_size;
return grub_errno;
}
next prev parent reply other threads:[~2008-08-01 22:46 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-07-19 23:06 loader modules jumping back to kernel Robert Millan
2008-07-20 3:26 ` Bean
2008-07-20 9:09 ` Yoshinori K. Okuji
2008-07-27 21:39 ` Robert Millan
2008-07-30 19:15 ` loadee relocation (Re: loader modules jumping back to kernel) Robert Millan
2008-07-31 23:45 ` Robert Millan
2008-08-01 16:16 ` Robert Millan
2008-08-01 22:45 ` Robert Millan [this message]
2008-08-02 12:11 ` Robert Millan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20080801224520.GA12392@thorin \
--to=rmh@aybabtu.com \
--cc=grub-devel@gnu.org \
--cc=okuji@enbug.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.