Search notes:

arch/x86/boot/bzImage

arch/x86/boot/bzImage is a disk-image file that is built with the following command:
arch/x86/boot/tools/build arch/x86/boot/setup.bin arch/x86/boot/vmlinux.bin arch/x86/boot/zoffset.h arch/x86/boot/bzImage 
bzImage is a (partly) compressed version of the kernel image. The bz of bzImage stands for big zimage. The content of bzImage can be extracted with scripts/extract-vmlinux.

Masquerading as a PE/COFF image

A kernel zImage or bzImage can masquerade as a PE/COFF image on order to convince EFI firmware loaders to load it as an EFI executable.
A PE/COFF image starts with MZ. The following command verifies that this is the case with bzImage:
$ echo $(head -c 2 arch/x86/boot/bzImage)
MZ
At position 0x38 (=56 in decimal), we find the value of the macro LINUX_PE_MAGIC:
$ xxd -s 56 -l 4 -p  arch/x86/boot/bzImage
cd238281
TODO: It seems that CONFIG_EFI_STUB must be enabled in order for bzImage to be a PE/COFF file.

Copying bzImage to the ESP

arch/x86/boot/bzImage is intended to be copied to the EFI System Partition (ESP) and renamed .efi. (Without this extension, the EFI firmware loader refuses to execute it).

Pasing arguments to bzImage.efi

In the EFI shell, it's possible to pass arguments to the kernel via bzImage.efi like so:
fs0:> bzImage.efi console=ttyS0 root=/dev/sda4
The only argument that is consumed by bzImage.efi (and not passed to the kernel) is initrd.

Calling efi_pe_entry

When UEFI has loaded bzImage, it calls the function whose address is stored in AddressOfEntryPoint which, in a x86 architecture, is efi_pe_entry (defined in drivers/firmware/efi/libstub/x86-stub.c).

Decompressing the kernel

If the kernel is not relocatable (CONFIG_RELOCATABLE=n) then bzImage will decompress itself to above the address specified with LOAD_PHYSICAL_ADDR, otherwise, it will be be run from the address where it has been loaded by the boot loader.

TODO

Extract some fields from bzImage

# file=arch/x86/boot/setup.bin
file=arch/x86/boot/bzImage


read_chars() {
   pos=$1
   len=$2
   echo $(dd if=$file bs=1 skip=$(( $pos )) count=$len 2>/dev/null) # expected HdRS
}

read_hex_bytes() {
   pos=$1
   len=$2

   echo $(od --skip-bytes=$(( $pos )) --read-bytes=$len --format=x$len  --address-radix=n $file)
}

echo "MS Dos Header"
echo "  MZ:                        $(read_chars      16#000 2)"

# include/linux/pe.h: 
#   LINUX_PE_MAGIC appears at offset 0x38 into the MS-DOS header of EFI bootable
#   Linux kernel images that target the architecture as specified by the PE/COFF
#   header machine type field.
#
echo "  LINUX_PE_MAGIC:              $(read_hex_bytes 16#38 4 )" # expected 818223cd

addr_pe_header=$(read_hex_bytes 16#3c 4)
echo "  Addr of PE Header (e_lfanew) $addr_pe_header" # Likely to be 82.

echo
echo "PE File Header (coff_header)"  
echo "  Signature (PE_MAGIC)         $(read_hex_bytes 16#$addr_pe_header 4)" # expected PE\0\0 (see include/linux/pe.h)

echo
echo "  Machine:                     $(read_hex_bytes $(( 16#$addr_pe_header + 16#04 )) 2)" # expected IMAGE_FILE_MACHINE_AMD64 (= 8664) or IMAGE_FILE_MACHINE_I386
echo "  NumberOfSections:            $(read_hex_bytes $(( 16#$addr_pe_header + 16#06 )) 2)" # 4 sections: .setup, .reloc, .compat (if CONFIG_EFI_MIXED) and .text and ?
echo
echo "  TimeDateStamp:               $(read_hex_bytes $(( 16#$addr_pe_header + 16#08 )) 4)" 
echo "  PointerToSymbolTable:        $(read_hex_bytes $(( 16#$addr_pe_header + 16#0c )) 4)" 
echo "  NumberOfSymbols:             $(read_hex_bytes $(( 16#$addr_pe_header + 16#10 )) 4)" 

size_opt_hdr=$(read_hex_bytes $(( 16#$addr_pe_header + 16#14 )) 2)
echo "  SizeOfOptionalHeader:        $size_opt_hdr"

echo "  Characteristics:             $(read_hex_bytes $(( 16#$addr_pe_header + 16#16 )) 2)" # 0206 = IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DEBUG_STRIPPED | IMAGE_FILE_LINE_NUMS_STRIPPED

echo
addr_opt_hdr=$(( 16#$addr_pe_header + 16#18 ))
echo "addr_opt_hdr = $addr_opt_hdr"
echo "PE Optional Header"
echo "  Magic:                       $(read_hex_bytes     $addr_opt_hdr            2)" # 0x020b = PE_OPT_MAGIC_PE32PLUS
echo "  MajorLinkerVersion:          $(read_hex_bytes $(( $addr_opt_hdr + 16#02 )) 1)" # expected 0x02

echo "  MajorLinkerVersion:          $(read_hex_bytes $(( $addr_opt_hdr + 16#03 )) 1)" # expected 0x14

echo
echo "  SizeOfCode:                  $(read_hex_bytes $(( $addr_opt_hdr + 16#04 )) 4)" # ?
echo "  SizeOfUninitializedData:     $(read_hex_bytes $(( $addr_opt_hdr + 16#08 )) 4)" # ?
echo "  SizeOfinitializedData:       $(read_hex_bytes $(( $addr_opt_hdr + 16#0c )) 4)" # ?

echo
echo "  AddressOfEntryPoint:         $(read_hex_bytes $(( $addr_opt_hdr + 16#10 )) 4)" # Relative to ImageBase ?
echo "  BaseOfCode:                  $(read_hex_bytes $(( $addr_opt_hdr + 16#14 )) 4)" # Expected 0x0200 See header.S
echo "  ImageBase:                   $(read_hex_bytes $(( $addr_opt_hdr + 16#18 )) 8)" # Expected 1000000 = LOAD_PHYSICAL_ADDR + 0xffff) & ~0xffff  ( See arch/x86/include/asm/boot.h ) -- The decompression buffer will start at ImageBase

echo
echo "  SectionAlignment:            $(read_hex_bytes $(( $addr_opt_hdr + 16#20 )) 4)" # Expected 20
echo "  FileAlignment:               $(read_hex_bytes $(( $addr_opt_hdr + 16#24 )) 4)" # Expected 20

echo
echo "  MajorOperatingSystemVersion: $(read_hex_bytes $(( $addr_opt_hdr + 16#28 )) 2)"
echo "  MinorOperatingSystemVersion: $(read_hex_bytes $(( $addr_opt_hdr + 16#2a )) 2)"

echo
echo "  MinorOperatingSystemVersion: $(read_hex_bytes $(( $addr_opt_hdr + 16#2c )) 2)" # expected: 3 = LINUX_EFISTUB_MAJOR_VERSION
echo "  MinorOperatingSystemVersion: $(read_hex_bytes $(( $addr_opt_hdr + 16#2e )) 2)" # expected: 0 = LINUX_EFISTUB_MINOR_VERSION

echo

start_section=$(( $addr_opt_hdr+ 16#$size_opt_hdr ))
echo "START_SECTION = $start_section"

echo ".setup                         $(read_chars     $start_section  6)"

echo "  size:                        $(read_hex_bytes $(( $start_section + 16#08 )) 4)"
echo "  startup_{32,64} (vma)        $(read_hex_bytes $(( $start_section + 16#0c )) 4)" # Physical address?
echo "  size init data on disk       $(read_hex_bytes $(( $start_section + 16#10 )) 4)"
echo "  startup_{32,64}: (file off.) $(read_hex_bytes $(( $start_section + 16#14 )) 4)" # Virtual address?
echo
echo "  PointerToRelocations         $(read_hex_bytes $(( $start_section + 16#18 )) 4)"
echo "  PointerToLineNumbers:        $(read_hex_bytes $(( $start_section + 16#1c )) 4)"
echo "  NumberOfRelocations:         $(read_hex_bytes $(( $start_section + 16#20 )) 2)"
echo "  NumberOfLines:               $(read_hex_bytes $(( $start_section + 16#22 )) 2)"
echo
echo "  Characteristics:             $(read_hex_bytes $(( $start_section + 16#24 )) 4)" # IMAGE_SCN_CNT_CODE	| IMAGE_SCN_MEM_READ| IMAGE_SCN_MEM_EXECUTE	| IMAGE_SCN_ALIGN_16BYTE

echo
start_section=$(( $start_section + 16#28 ))

echo ".reloc                         $(read_chars     $start_section  6)"
echo "  startup_{32,64} (vma)        $(read_hex_bytes $(( $start_section + 16#0c )) 4)" # Physical address?
echo "  size init data on disk       $(read_hex_bytes $(( $start_section + 16#10 )) 4)"
echo "  startup_{32,64}:             $(read_hex_bytes $(( $start_section + 16#14 )) 4)" # Virtual address?
echo
echo "  PointerToRelocations         $(read_hex_bytes $(( $start_section + 16#18 )) 4)"
echo "  PointerToLineNumbers:        $(read_hex_bytes $(( $start_section + 16#1c )) 4)"
echo "  NumberOfRelocations:         $(read_hex_bytes $(( $start_section + 16#20 )) 2)"
echo "  NumberOfLines:               $(read_hex_bytes $(( $start_section + 16#22 )) 2)"
echo
echo "  Characteristics:             $(read_hex_bytes $(( $start_section + 16#24 )) 4)"


echo
start_section=$(( $start_section + 16#28 ))
echo ".compat                        $(read_chars     $start_section  7)"

echo "  size:                        $(read_hex_bytes $(( $start_section + 16#08 )) 4)"
echo "  startup_{32,64} (vma)        $(read_hex_bytes $(( $start_section + 16#0c )) 4)" # Physical address?
echo "  size init data on disk       $(read_hex_bytes $(( $start_section + 16#10 )) 4)"
echo "  startup_{32,64}:             $(read_hex_bytes $(( $start_section + 16#14 )) 4)" # Virtual address?
echo
echo "  PointerToRelocations         $(read_hex_bytes $(( $start_section + 16#18 )) 4)"
echo "  PointerToLineNumbers:        $(read_hex_bytes $(( $start_section + 16#1c )) 4)"
echo "  NumberOfRelocations:         $(read_hex_bytes $(( $start_section + 16#20 )) 2)"
echo "  NumberOfLines:               $(read_hex_bytes $(( $start_section + 16#22 )) 2)"
echo
echo "  Characteristics:             $(read_hex_bytes $(( $start_section + 16#24 )) 4)"

echo
start_section=$(( $start_section + 16#28 ))
echo ".text                          $(read_chars     $start_section  5)"

echo "  size:                        $(read_hex_bytes $(( $start_section + 16#08 )) 4)"
echo "  text start:                  $(read_hex_bytes $(( $start_section + 16#0c )) 4)" # Text start Physical address?
echo "  size init data on disk       $(read_hex_bytes $(( $start_section + 16#10 )) 4)"
echo "  startup_{32,64}:             $(read_hex_bytes $(( $start_section + 16#14 )) 4)" # Virtual address?
echo
echo "  PointerToRelocations         $(read_hex_bytes $(( $start_section + 16#18 )) 4)"
echo "  PointerToLineNumbers:        $(read_hex_bytes $(( $start_section + 16#1c )) 4)"
echo "  NumberOfRelocations:         $(read_hex_bytes $(( $start_section + 16#20 )) 2)"
echo "  NumberOfLines:               $(read_hex_bytes $(( $start_section + 16#22 )) 2)"
echo
echo "  Characteristics:             $(read_hex_bytes $(( $start_section + 16#24 )) 4)"

echo
echo "----"
echo
echo "  syssize:                     $(read_hex_bytes 16#1f4 4)" # see build.c            Size of kernel (arch/x86/boot/vmlinux.bin) + 4 bytes for crc), up-aligned to 32 bytes (CONFIG_EFI_STUB) in paragraph units (16 bytes) |  put_unaligned_le32(sys_size, &buf[0x1f4]);
echo "  rootdev:                     $(read_hex_bytes 16#1fc 2)" # see build. 0 = DEFAULT_ROOT_DEV =  (DEFAULT_MAJOR_ROOT << 8 | DEFAULT_MINOR_ROOT) = 0<<8 | 0

echo
echo ".reloc                         $(read_chars     16#162 6)"
echo ".text:                         $(read_chars     16#1b2 5)"

echo "  setup_sects (-1)             $(read_hex_bytes 16#1f1 1)"  # see build.c            buf[0x1f1] = setup_sectors-1;
# echo "  setup_sects                $(read_hex_bytes 16#1f5 1)"
# echo "  root_flags:                  $(read_hex_bytes 16#1f6 2)"
echo "  rootdev:                     $(read_hex_bytes 16#1fc 2)"

echo "  boot_flag:                   $(read_hex_bytes 16#1fe 2)" # expected aa55 (See «Boot block hasn't got boot flag» in arch/x86/boot/tools/build.c)

echo "  jump:                        $(read_hex_bytes 16#200 1)" # expected eb (Assembler JMP instruction)
echo "  header:                      $(read_chars     16#202 4)" # expected HdRS
echo "  version:                     $(read_hex_bytes 16#206 4)" # 20f (or so)

echo
echo "  init_sz:                     $(read_hex_bytes 16#260 4)" # Kernel initialization size (the larger value of the size of the uncompressed or compressed kernel, see INIT_SIZE in arxch/x86/boot/header.S)
echo "  handover_offset:             $(read_hex_bytes 16#264 4)" #
echo "  kernel_info_offset:          $(read_hex_bytes 16#268 4)" #


size_setup_bin=$(stat -c%s arch/x86/boot/setup.bin)
echo "  .compat:                     $(read_hex_bytes      $size_setup_bin 4)" #
#echo "  .compat:                     $(read_chars      14748  4)" #

See also

Documentation/admin-guide/efi-stub.rst says that
make bzImage
arch/x86/boot/tools/build.c
The config option CONFIG_EFI_STUB allows a bzImage to be loaded directly by EFI firmware without the use of a bootloader.

Index