grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: loadee relocation (Re: loader modules jumping back to kernel)


From: Robert Millan
Subject: Re: loadee relocation (Re: loader modules jumping back to kernel)
Date: Sat, 2 Aug 2008 14:11:40 +0200
User-agent: Mutt/1.5.13 (2006-08-11)

Committed.

On Sat, Aug 02, 2008 at 12:45:20AM +0200, Robert Millan wrote:
> 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."

> 2008-08-01  Robert Millan  <address@hidden>
> 
>       * 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;
>  }

> _______________________________________________
> Grub-devel mailing list
> address@hidden
> http://lists.gnu.org/mailman/listinfo/grub-devel


-- 
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."




reply via email to

[Prev in Thread] Current Thread [Next in Thread]