On last post I got Windows Boot Manager to load GRUB (or what seems like it) on QEMU. Since then I was trying to cross-compile a newer version of GRUB for ARM and then load this newer version, instead of the original EFI partition I used when writing that post. That was harder than I first thought.

I didn’t get it to work. For now I’ll stick with that original GRUB imagine, knowing that the though of it will bug for a while - I got really curious to know how that image was made. I thought of recording whatever I found so far, in case I decide to continue investigating this in the future, or someone else is trying the same thing.

UEFI loaders are PE files. So I first started to compare the PE headers from the RoL’s GRUB image (from the last post) with the one I built:

RoL’s image headers

DOS Header
    Magic number:                    0x5a4d (MZ)
    Bytes in last page:              144
    Pages in file:                   3
    Relocations:                     0
    Size of header in paragraphs:    4
    Minimum extra paragraphs:        0
    Maximum extra paragraphs:        65535
    Initial (relative) SS value:     0
    Initial SP value:                0xb8
    Initial IP value:                0
    Initial (relative) CS value:     0
    Address of relocation table:     0x40
    Overlay number:                  0
    OEM identifier:                  0
    OEM information:                 0
    PE header offset:                0xd8
COFF/File header
    Machine:                         0x1c4 IMAGE_FILE_MACHINE_ARMV7
    Number of sections:              7
    Date/time stamp:                 1471559823 (Thu, 18 Aug 2016 22:37:03 UTC)
    Symbol Table offset:             0
    Number of symbols:               0
    Size of optional header:         0xe0
    Characteristics:                 0x122
    Characteristics names
                                         IMAGE_FILE_EXECUTABLE_IMAGE
                                         IMAGE_FILE_LARGE_ADDRESS_AWARE
                                         IMAGE_FILE_32BIT_MACHINE
Optional/Image header
    Magic number:                    0x10b (PE32)
    Linker major version:            11
    Linker minor version:            0
    Size of .text section:           0x3a00
    Size of .data section:           0x2e00
    Size of .bss section:            0
    Entrypoint:                      0x1691
    Address of .text section:        0x1000
    Address of .data section:        0x6000
    ImageBase:                       0x400000
    Alignment of sections:           0x1000
    Alignment factor:                0x200
    Major version of required OS:    0
    Minor version of required OS:    0
    Major version of image:          0
    Minor version of image:          0
    Major version of subsystem:      1
    Minor version of subsystem:      0
    Size of image:                   0xc000
    Size of headers:                 0x400
    Checksum:                        0xd560
    Subsystem required:              0x10 (IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
    DLL characteristics:             0
    DLL characteristics names
    Size of stack to reserve:        0x100000
    Size of stack to commit:         0x1000
    Size of heap space to reserve:   0x100000
    Size of heap space to commit:    0x1000

My GRUB image headers

DOS Header
    Magic number:                    0x5a4d (MZ)
    Bytes in last page:              144
    Pages in file:                   3
    Relocations:                     0
    Size of header in paragraphs:    4
    Minimum extra paragraphs:        0
    Maximum extra paragraphs:        65535
    Initial (relative) SS value:     0
    Initial SP value:                0xb8
    Initial IP value:                0
    Initial (relative) CS value:     0
    Address of relocation table:     0x40
    Overlay number:                  0
    OEM identifier:                  0
    OEM information:                 0
    PE header offset:                0x80
COFF/File header
    Machine:                         0x1c2 IMAGE_FILE_MACHINE_THUMB
    Number of sections:              4
    Date/time stamp:                 1420070400 (Thu, 01 Jan 2015 00:00:00 UTC)
    Symbol Table offset:             0
    Number of symbols:               0
    Size of optional header:         0xe0
    Characteristics:                 0x30e
    Characteristics names
                                         IMAGE_FILE_EXECUTABLE_IMAGE
                                         IMAGE_FILE_LINE_NUMS_STRIPPED
                                         IMAGE_FILE_LOCAL_SYMS_STRIPPED
                                         IMAGE_FILE_32BIT_MACHINE
                                         IMAGE_FILE_DEBUG_STRIPPED
Optional/Image header
    Magic number:                    0x10b (PE32)
    Linker major version:            0
    Linker minor version:            0
    Size of .text section:           0xb800
    Size of .data section:           0xa99a00
    Size of .bss section:            0
    Entrypoint:                      0x400
    Address of .text section:        0x400
    Address of .data section:        0xbc00
    ImageBase:                       0
    Alignment of sections:           0x200
    Alignment factor:                0x200
    Major version of required OS:    0
    Minor version of required OS:    0
    Major version of image:          0
    Minor version of image:          0
    Major version of subsystem:      0
    Minor version of subsystem:      0
    Size of image:                   0xaa5e00
    Size of headers:                 0x400
    Checksum:                        0
    Subsystem required:              0xa (IMAGE_SUBSYSTEM_EFI_APPLICATION)
    DLL characteristics:             0
    DLL characteristics names
    Size of stack to reserve:        0x10000
    Size of stack to commit:         0x10000
    Size of heap space to reserve:   0x10000
    Size of heap space to commit:    0x10000

I used the PEV toolkit to get those headers.

Some things are quite obvious. I’ll go through them. To begin with, the machine header are different. Both are ARM, but one is ARMv7 and the other is Thumb. I couldn’t really compile GRUB such to match ARMv7, even passing -march=armv7 to GCC. I am probably doing something wrong.

It is curious though. When you compile GRUB, you get a set of tools like grub-mkstandalone. Those tools are compile to your BUILD platform, that is, the platform you are building on (x86 in my case). This makes sense, because you will use these tools to then generate the final GRUB image. This image is generate, as far as I understood, from the modules that are build as part of GRUB. However, those modules are ELF files, according to /bin/file:

acpi.mod: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped

So I imagine that grub-mkstandalone actually concatenates all the modules required and transform them in a PE file when building the final imagine. So would be it the responsible to set the PE machine header to ARMV7 and my cross-compilation doesn’t really matter? I have no clue :(

Here’s how I cross-compile GRUB, should you need to do something similar:

./autoconfig
./configure --with-platform=efi --prefix=/usr --target=arm-none-eabi
make CFLAGS="-march=armv7 -Wno-unused-value" 
make DESTDIR=/tmp/grub install
cd /tmp/grub
/tmp/grub/usr/bin/grub-mkstandalone -o boot.img -O arm-efi -d /tmp/grub/usr/lib/grub/arm-efi

Long story short, I am not sure how much a problem that is, after all. If I bypass Windows Boot Manager and makes UEFI load my GRUB image directly, it works. So I don’t really know how much Windows Boot Manager cares about this header.

The other two red flags were the headers Checksum and Subsystem required. This forum post was the closed I came to a solution. It talks about how the Subsystem required needs to be set to IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION. I tried manually changing that header to match it, along side with fixing the checksum (using this code that seemed to give valid checksum when I compared with valid PE files I have). None of that helped.

Another interesting point discussed on that forum thread and that also caught my eye was the fact that the entry point for both PE files differed. Not quite sure how much that could be because of the different instruction set (if at all), but that may indicate that the RoL’s GRUB was generated in a very different way than I am doing, and fiddling with headers might not be enough.

Anyways, that’s how far I got. If you have any points on the topic, do let me know!