jirutka / efi-mkuki

EFI Unified Kernel Image Maker
MIT License
17 stars 3 forks source link

Added sections below image base #4

Open WhyNotHugo opened 1 year ago

WhyNotHugo commented 1 year ago

Given that gummiboot and other stubs rely on the now-deprecated handover protocol, I'm writing my own little stub loader.

The loader by itself "runs" and can inspect its own PE table to find PE sections (e.g.: the kernel, cmdline, initrd). However, I'm having a hard time assembling a binary which actually contains the stub plus these three.

I'm basically using the following:

efi-mkuki \
  -c /proc/cmdline \
  -o ESP/efi/boot/bootx64.efi \
  -S target/x86_64-unknown-uefi/debug/hello-uefi.efi \
  /efi/vmlinuz-edge \
  /efi/initramfs-edge

Which warns:

objcopy: ESP/efi/boot/bootx64.efi:.osrel: section below image base
objcopy: ESP/efi/boot/bootx64.efi:.cmdline: section below image base
objcopy: ESP/efi/boot/bootx64.efi:.linux: section below image base
objcopy: ESP/efi/boot/bootx64.efi:.initrd: section below image base

Indeed, it seems that the new sections are added to the begging of the file, not to the end of it:

> objdump --section-headers ESP/efi/boot/bootx64.efi                       

ESP/efi/boot/bootx64.efi:     file format pei-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .osrel        000000bc  0000000200020000  0000000200020000  00000400  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .cmdline      000000c2  0000000200030000  0000000200030000  00000600  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .linux        0061ed60  0000000202000000  0000000202000000  00000800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .initrd       02bdccec  0000000203000000  0000000203000000  0061f600  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .text         00017bf5  0000000140001000  0000000140001000  031fc400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  5 .rdata        0000423a  0000000140019000  0000000140019000  03214000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .data         00000068  000000014001e000  000000014001e000  03218400  2**4
                  CONTENTS, ALLOC, LOAD, DATA
  7 .eh_fram      00000040  000000014001f000  000000014001f000  03218600  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .reloc        00000314  0000000140020000  0000000140020000  03218800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

For reference, this is by stub before running mkuki:

> objdump --section-headers target/x86_64-unknown-uefi/debug/hello-uefi.efi 

target/x86_64-unknown-uefi/debug/hello-uefi.efi:     file format pei-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00017bf5  0000000140001000  0000000140001000  00000400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rdata        0000423a  0000000140019000  0000000140019000  00018000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .data         00000068  000000014001e000  000000014001e000  0001c400  2**4
                  CONTENTS, ALLOC, LOAD, DATA
  3 .eh_fram      00000040  000000014001f000  000000014001f000  0001c600  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .reloc        00000314  0000000140020000  0000000140020000  0001c800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

The resulting binary doesn't run. I'm pretty sure that it is not valid due to the order of the sections (e.g.: the image base should be at the beginning). However, I can't figure out how to force a different order.

For reference, using mkuki with gummiboot's stub results in a different ordering:

> objdump --section-headers EFI/Linux/alpine-edge-6.1.11-0.efi

EFI/Linux/alpine-edge-6.1.11-0.efi:     file format pei-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00005e90  0000000000003000  0000000000003000  00000400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .reloc        0000000c  0000000000009000  0000000000009000  00006400  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .data         00001e88  000000000000a000  000000000000a000  00006600  2**4
                  CONTENTS, ALLOC, LOAD, DATA
  3 .dynamic      000000f0  000000000000c000  000000000000c000  00008600  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  4 .rela         00000e58  000000000000d000  000000000000d000  00008800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynsym       000003a8  000000000000e000  000000000000e000  00009800  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .osrel        000000c7  0000000000020000  0000000000020000  00009c00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .cmdline      000000c2  0000000000030000  0000000000030000  00009e00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .linux        005d41a0  0000000002000000  0000000002000000  0000a000  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .initrd       02b44cd0  0000000003000000  0000000003000000  005de200  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

I tried using objcopy manually, but ended up having the same issue. Any ideas how to address this?

WhyNotHugo commented 1 year ago

FWIW, I also tried using objcopy directly like this:

objcopy \
  --preserve-dates \
  --enable-deterministic-archives \
  --add-section ".kernel=$KERNEL" --set-section-flags .kernel=code,readonly \
  --add-section ".cmdline=$CMDLINE" --set-section-flags .cmdline=data,readonly \
  --add-section ".initrd=$INITRD" --set-section-flags .initrd=data,readonly \
  target/x86_64-unknown-uefi/debug/hello-uefi.efi \
  ESP/efi/boot/bootx64.efi

But the result in the same.

WhyNotHugo commented 1 year ago

Looks like --change-section-vma is required (and mkuki is specifying it), but the addresses need to be relative to the ImageBase, which is simply different for my binary:

> objdump -p ESP/efi/boot/bootx64.efi | grep ImageBase
ImageBase       0000000140000000

I'm not sure if I know how to address this in mkuki though, I'll have to do a bit more experimentation first (since I'm manually calculating offsets for now) and adding, for example:

  --add-section ".kernel=$KERNEL" --set-section-flags .kernel=code,readonly --change-section-vma .kernel+0x140020314\
jas67 commented 1 month ago

@WhyNotHugo -- did you ever figure out how to resolve or work around this problem?

I was successfully using efi-mkuki until changing the kernel that I'm using from 5.15 to 6.8, and my base OS image from Ubuntu 22.04LTS to Ubuntu 24.04LTS.

I now have the object loaded below base error too.

WhyNotHugo commented 1 month ago

I couldn't figure out how to make this work with efi-mkuki; I still don't fully understand all the parts in play here.

At the time, I wrote my own custom code using a library that handled PE for this. It was a horrible pile of hacks. I can't seem to find any of it now, sorry.

jas67 commented 1 month ago

I found the problem to be /usr/lib/systemd/boot/efi/linuxx64.efi.stub that is included in Ubuntu 24.04LTS's systemd-efi-bin package. Using this stub to produce the unified kernel image results in the following warning from objcopy:

objcopy:/tmp/NEW_ROOT1224/boot/efi/EFI/ubuntu/vmlinuz.efi:.osrel:sectionbelowimagebase objcopy:/tmp/NEW_ROOT1224/boot/efi/EFI/ubuntu/vmlinuz.efi:.cmdline:sectionbelowimagebase objcopy: /tmp/NEW_ROOT1224/boot/efi/EFI/ubuntu/vmlinuz.efi:.linux: section below image base objcopy:/*mp/NEW_ROOT1224/boot/efi/EFI/ubuntu/vmlinuz.efi:.initrd:section below image base And the resulting EFI binary will not boot.

If I copied the same file from an Ubuntu 22.04LTS system (in systemd package) and used that instead, I got an EFI binary that boots.

So, at least for now, that is my work-around.