Files
Sam Demeulemeester 5b867a1506 Fix PE/COFF W^X violation & set NX flag to fix recent AMI UEFI refusing to load Memtest86+ (#604)
Some modern UEFI refuse to load PE image when a section have both IMAGE_SCN_MEM_WRITE and IMAGE_SCN_MEM_EXECUTE flag set. The .text section was using both because relocation need to write in data area.

Split the .text PE section in two: .text with read-execute only for code and rodata, and .data with read-write for dynamic, data and got. Also set IMAGE_DLLCHARACTERISTICS_NX_COMPAT flag accordingly (we only need it for startup and not when Memtest86+ is running later).

For x86 and LoongArch headers:
- NumberOfSections go from 3 to 4
- Remove IMAGE_SCN_MEM_WRITE from .text section
- Add new .data section with RW flag, no execute
- Fix SizeOfInitializedData to include .data and .sbat
- Set IMAGE_DLLCHARACTERISTICS_NX_COMPAT flag
- BaseOfData now point to .data instead of .sbat (PE32)
- .dynamic moved after relocation tables (memtest_shared.lds)
- page-aligned _etext_ro symbol used as read-only/writable boundary (memtest_shared.lds)
2026-04-03 15:44:34 +02:00

967 lines
22 KiB
ArmAsm

// SPDX-License-Identifier: GPL-2.0
//
// header.S supports booting directly from the 1) BIOS (FDD), 2) UEFI or 3) via
// an intermediate bootloader that supports the Linux boot protocol. All-in-one.
//
// 1) If booted directly from the BIOS, ".bootsect" gets loaded at address 0x7C00
// with DL register set to the boot drive number: 0x00 - FDD, 0x80 - first HDD.
// Unfortunately, when executing "MZ" signature as x86 instructions, the original
// value of DX becomes instantly and irrevocably lost, but this is fine as we only
// support booting from FDD anyway. Once started, the code immediately prints
// boot_msg - "Loading ". Next, it sets DX to 0x00 (as assumed by the code) and
// creates a new Floppy Disk Drive parameter table. Then, it tries to load
// ".setup" section immediately after itself (address 0x7E00) that contains
// setup code, Once successful, it prints memtest version and starts loading
// the main program code at address 0x10000. After each successful read,
// a dot - "." is printed. If read fails, error message gets printed and
// the read operation is restarted. At the end, 07E0h:0000h is called which
// immediately jumps into do_setup that handles switches CPU into protected mode
// and calls kernel entry point at 0x10000.
//
// 2) To support loading from the UEFI, it provides the MS-DOS & PE/COFF headers.
// The PE/COFF headers point to offset 0x10000 + 0x1E0 where efi_boot from
// startup32.S / startup64.S that handles EFI handover and the rest of booting.
//
// 3) When using an intermediate bootloader, it provides the first few bytes of
// the Linux boot header (at the end of the ".bootsect" section), with the
// remainder of the header and the setup code being provided in the ".setup".
// section.
//
// Copyright (C) 2020-2024 Martin Whitaker.
//
// Derived from Linux 5.6 arch/x86/boot/header.S:
//
// Copyright (C) 1991, 1992 Linus Torvalds
//
// Based on bootsect.S and setup.S
// modified by more people than can be counted
//
// Rewritten as a common file by H. Peter Anvin (Apr 2007)
#define __ASSEMBLY__
#include "boot.h"
#include "build_version.h"
#include "peimage.h"
# The EFI loader loads the header at ImageBase, so we have to locate the main program
# after that. This means we can't load the main program at HIGH_LOAD_ADDR. Pick a load
# address well away from HIGH_LOAD_ADDR, to avoid overlap when relocating the code.
#define IMAGE_BASE 0x200000
#define BOOT_PARAMS_START (SETUP_SECS * 512)
#define BOOT_PARAMS_END (BOOT_PARAMS_START + 4096)
.section ".bootsect", "ax", @progbits
.code16
.globl boot
boot:
# "MZ", the MS-DOS header signature.
# Fortunately it translates to mostly harmless x86 instructions.
.byte 0x4d # dec %bp
.byte 0x5a # pop %dx
ljmp $BOOT_SEG, $init
init:
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw $BOOT_STACK_TOP, %bp
movw %bp, %sp
sti
cld
leaw boot_msg, %bp
movw $(boot_msg_end - boot_msg), %cx
call print_string
jmp init_fdd
print_string:
# Print string from %es:%bp of length %cx via BIOS routine int 10h: 1301h
push %cx
movb $0x03, %ah # read cursor pos
xorb %bh, %bh # page 0
int $0x10
pop %cx
movb $0x07, %bl # (page 0 set above) attribute 7 (normal)
movw $0x1301, %ax # write string, move cursor
int $0x10
ret
boot_msg:
.ascii "Loading "
boot_msg_end:
.org 0x3c
# The PE header pointer.
.long pe_header
init_fdd:
# Many BIOS's default disk parameter tables will not recognize
# multi-sector reads beyond the maximum sector number specified
# in the default diskette parameter tables - this may mean 7
# sectors in some cases.
#
# Since single sector reads are slow and out of the question,
# we must take care of this by creating new parameter tables
# (for the first disk) in RAM. We will set the maximum sector
# count to 18 - the most we will encounter on an HD 1.44.
#
# High doesn't hurt. Low does.
#
# Segments are as follows:
# ds=es=ss=cs = BOOT_SEG,
# fs = 0, gs = parameter table segment
xorw %dx, %dx
pushw $0
popw %fs
movw $0x78, %bx # fs:bx is parameter table address
lgs %fs:(%bx),%si # gs:si is source
movw %dx, %di # es:di is destination
movw $6, %cx # copy 12 bytes
cld
rep movsw %gs:(%si), (%di)
movw %dx, %di
movb $18, 4(%di) # patch sector count
movw %di, %fs:(%bx)
movw %es, %fs:2(%bx)
movw %cs, %ax
movw %ax, %fs
movw %ax, %gs
xorb %ah, %ah # reset FDC
xorb %dl, %dl
int $0x13
# Load the setup sectors directly after the boot block.
# Note that 'es' is already set up.
load_setup:
xorw %dx, %dx # drive 0, head 0
movw $0x0002, %cx # sector 2, track 0
movw $0x0200, %bx # address = 512, in BOOT_SEG
movw $(0x0200 + SETUP_SECS), %ax # service 2, nr of sectors
# (assume all on head 0, track 0)
int $0x13 # read it
jnc load_setup_done # ok - continue
pushw %ax # dump error code
call print_nl
movw %sp, %bp
call print_hex
popw %ax
xorb %dl, %dl # reset FDC
xorb %ah, %ah
int $0x13
jmp load_setup
load_setup_done:
# Get disk drive parameters, specifically number of sectors/track.
# It seems that there is no BIOS call to get the number of sectors.
# Guess 18 sectors if sector 18 can be read, 15 if sector 15 can be
# read. Otherwise guess 9.
xorw %dx, %dx # drive 0, head 0
movw $0x0012, %cx # sector 18, track 0
movw $BOOT_STACK, %bx # use the bottom of the stack (es = cs)
movw $0x0201, %ax # service 2, 1 sector
int $0x13
jnc got_sectors
movb $0x0f, %cl # sector 15
movw $0x0201, %ax # service 2, 1 sector
int $0x13
jnc got_sectors
movb $0x09, %cl
got_sectors:
movw %cx, %cs:sectors
movw $BOOT_SEG, %ax
movw %ax, %es
leaw mt86plus_version, %bp
movw $(mt86plus_version_end - mt86plus_version), %cx
call print_string
# Load the main test program.
movw $MAIN_SEG, %ax
movw %ax, %es
call read_it
call kill_motor
call turn_off_cursor
call print_nl
# Fix up the Linux boot header to indicate we've loaded into low memory.
movl $LOW_LOAD_ADDR, code32_start
# After that (everything loaded), we jump to the setup code loaded
# directly after the boot block.
ljmp $SETUP_SEG, $0
# This subroutine loads the system at address 0x10000, making sure no 64KB
# boundaries are crossed. We try to load it as fast as possible, loading
# whole tracks whenever we can.
#
# in: es - starting address segment (normally 0x1000)
#
sread: .word 1 + SETUP_SECS # sectors read of current track
head: .word 0 # current head
track: .word 0 # current track
read_it:
movw %es, %ax
testw $0x0fff, %ax
die:
jne die # es must be at 64kB boundary
xorw %bx,%bx # bx is starting address within segment
rp_read:
movw %es, %ax
subw $MAIN_SEG, %ax # have we loaded all yet?
cmpw sys_size, %ax
jbe ok1_read
ret
ok1_read:
movw %cs:sectors, %ax
subw sread, %ax
movw %ax, %cx
shlw $9, %cx
addw %bx, %cx
jnc ok2_read
je ok2_read
xorw %ax, %ax
subw %bx, %ax
shrw $9, %ax
ok2_read:
call read_track
movw %ax, %cx
add sread, %ax
cmpw %cs:sectors, %ax
jne ok3_read
movw $1, %ax
subw head, %ax
jne ok4_read
incw track
ok4_read:
movw %ax, head
xorw %ax, %ax
ok3_read:
movw %ax, sread
shlw $9, %cx
addw %cx, %bx
jnc rp_read
movw %es, %ax
addb $0x10, %ah
movw %ax, %es
xorw %bx, %bx
jmp rp_read
read_track:
pusha
pusha
movw $0xe2e, %ax # loading... message 2e = .
movw $7, %bx
int $0x10
popa
movw track, %dx
movw sread, %cx
incw %cx
movb %dl, %ch
movw head, %dx
movb %dl, %dh
andw $0x0100, %dx
movb $2, %ah
pushw %dx # save for error dump
pushw %cx
pushw %bx
pushw %ax
int $0x13
jc bad_rt
addw $8, %sp
popa
ret
bad_rt:
pushw %ax # save error code
call print_all # ah = error, al = read
xorb %ah, %ah
xorb %dl, %dl
int $0x13
addw $10, %sp
popa
jmp read_track
# This subroutine is for debugging purposes. It will print out all of the
# registers. The assumption is that this is called from a routine, with a
# stack frame like:
# dx
# cx
# bx
# ax
# err
# ret <- sp
print_all:
movw $5, %cx # error code + 4 registers
movw %sp, %bp
print_loop:
pushw %cx # save count left
call print_nl # nl for readability
cmpb 5, %cl # see if register name is needed
jae no_reg
movw $(0xe05 + 'A' - 1), %ax
subb %cl, %al
int $0x10
movb $'X', %al
int $0x10
movb $':', %al
int $0x10
no_reg:
addw $2, %bp # next register
call print_hex # print it
popw %cx
loop print_loop
ret
print_nl:
movw $0xe0d, %ax # CR
int $0x10
movb $0x0a, %al # LF
int $0x10
ret
# This subroutine is for debugging purposes, and prints the word pointed to
# by ss:bp in hexadecimal.
print_hex:
movw $4, %cx # 4 hex digits
movw (%bp), %dx # load word into dx
print_digit:
rolw $4, %dx # rotate so that lowest 4 bits are used
movb $0xe, %ah
movb %dl, %al # mask off so we have only next nibble
andb $0xf, %al
addb $'0', %al # convert to 0-based digit
cmpb $'9', %al # check for overflow
jbe good_digit
addb $('A' - '0' - 10), %al
good_digit:
int $0x10
loop print_digit
ret
# This subroutine turns off the floppy drive motor, so that we enter the
# kernel in a known state, and don't have to worry about it later.
kill_motor:
pushw %dx
movw $0x3f2, %dx
xorb %al, %al
outb %al, %dx
popw %dx
ret
# This subroutine turns off the text display cursor.
turn_off_cursor:
movb $0x01, %ah
movb $0x00, %bh
movw $0x2000, %cx
int $0x10
ret
# Local variables.
sectors:
.word 0
.org 497
# Emulate the Linux boot header, to allow loading by intermediate boot loaders.
# Indicate that the main program code should be loaded in high memory.
# The code from ".bootsect" will fix up the values if we are booted directly
# from the BIOS.
setup_sects:
.byte SETUP_SECS
root_flags:
.word 0
sys_size:
.long _sys_size
ram_size:
.word 0
vid_mode:
.word 0
root_dev:
.word 0
boot_flag:
.word 0xAA55
.section ".setup", "ax", @progbits
.globl setup
setup:
jmp do_setup
.globl header
header:
.ascii "HdrS"
version:
.word 0x020c
realmode_swtch:
.long 0
start_sys_seg:
.word 0x1000
kernel_version:
.word mt86plus_version-512
type_of_loader:
.byte 0
loadflags:
.byte 0x1 # LOADED_HIGH
setup_move_size:
.word 0
.globl code32_start
code32_start:
.long HIGH_LOAD_ADDR
ramdisk_image:
.long 0
ramdisk_size:
.long 0
bootsect_kludge:
.long 0
heap_end_ptr:
.word 0
ext_loader_ver:
.byte 0
ext_loader_type:
.byte 0
cmd_line_ptr:
.long 0
initrd_addr_max:
.long 0xffffffff
kernel_alignment:
.long 4096
relocatable_kernel:
.byte 0
min_alignment:
.byte 12
xload_flags:
#ifdef __x86_64__
.word 0x9 # XLF_KERNEL_64,XLF_EFI_HANDOVER_64
#else
.word 0x4 # XLF_EFI_HANDOVER_32
#endif
cmd_line_size:
.long 255
hardware_subarch:
.long 0
hardware_subarch_data:
.quad 0
payload_offset:
.long 0
payload_length:
.long 0
setup_data:
.quad 0
pref_address:
.quad HIGH_LOAD_ADDR
init_size:
.long _init_size
handover_offset:
.long 0x10
// The code below collects the memory map information from the BIOS, disables
// APM, enables A20, and performs the switch from real mode to protected mode
// before jumping to the main program entry point.
//
// The memory map information is stored in the 4KB block of memory immediately
// following the setup code. The layout of the information matches the Linux
// boot_params struct. A pointer to this block is passed to the main program,
// for compatiblity with the Linux 32-bit boot protocol.
do_setup:
# Reload the segment registers, except for the stack.
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
# Get the memory map and disable APM.
call get_mem_info
call disable_apm
# Disable interrupts.
cli
movb $0x80, %al # disable NMI
outb %al, $0x70
# Enable A20.
# Try to switch using the fast A20 gate.
movw $0x92, %dx
inb %dx, %al
# Skip if it's unimplemented (read returns 0xff).
cmpb $0xff, %al
jz 0f
orb $0x02, %al # set the ALT_A20_GATE bit
andb $0xfe, %al # clear the INIT_NOW bit
outb %al, %dx
0:
# Use the keyboard controller method anyway.
call empty_8042
movb $0xd1, %al # send write command
outb %al, $0x64
call empty_8042
movb $0xdf, %al # A20 on
outb %al, $0x60
call empty_8042
# Set up a minimal GDT and IDT.
xorl %eax, %eax
movw %cs, %ax
shll $4, %eax
addl %eax, gdt_descr - setup + 2
lgdt gdt_descr - setup
lidt idt_descr - setup
# Load a pointer to the boot_params block into ESI.
xorl %esi, %esi
movw %cs, %si
shll $4, %esi
addl $BOOT_PARAMS_START, %esi
# Fix up the jump address.
movl (code32_start - setup), %eax
movl %eax, (jump - setup + 2)
# Copy code32_start to the boot_params struct.
movl %eax, (BOOT_PARAMS_START + 0x214)
# Copy cmd_line_ptr and cmd_line_size to the boot_params struct.
movl (cmd_line_ptr - setup), %eax
movl %eax, (BOOT_PARAMS_START + 0x228)
movl (cmd_line_size - setup), %eax
movl %eax, (BOOT_PARAMS_START + 0x238)
# Switch to protected mode.
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
jmp flush
flush:
# Reload the segment registers and jump to the main test program.
movw $KERNEL_DS, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movw %ax, %fs
movw %ax, %gs
jump:
data32 ljmp $KERNEL_CS, $0
# This subroutine queries the BIOS to determine the system memory map
# and stores the results in the boot_params structure that we pass to
# the startup code.
#define SMAP 0x534d4150
get_mem_info:
push %ds
push %es
# Set DS and ES to point to the start of the boot_params structure.
movw %ds, %ax
addw $(BOOT_PARAMS_START >> 4), %ax
movw %ax, %ds
movw %ax, %es
# Zero the entire boot_params structure.
movw $0x0000, %di
movw $0x0400, %cx
xorl %eax, %eax
cld
rep stosl
# First try method E820. E820 returns memory classified into a whole
# bunch of different types, and allows memory holes and everything.
mem_e820:
movw $E820_MAP, %di # destination pointer
xorl %ebx, %ebx # continuation counter
loop_e820:
movl $0x0000e820, %eax # e820, upper word zeroed
movl $SMAP, %edx # ASCII 'SMAP'
movl $20, %ecx # size of the e820 record
int $0x15 # make the call
jc done_e820 # bail out if it fails
cmpl $SMAP, %eax # check the return is 'SMAP'
jne done_e820 # bail out if it fails
incb (E820_ENTRIES)
addw $E820_ENTRY_SIZE, %di
movb (E820_ENTRIES), %al # check for table full
cmpb $E820_MAP_SIZE, %al
je done_e820
cmpl $0, %ebx # any more entries?
jne loop_e820
done_e820:
cmpb $0, (E820_ENTRIES)
jnz get_mem_done
# Next try method E801.
mem_e801:
stc # Fix to work around buggy BIOSs
xorw %cx,%cx # which don't clear/set carry on
xorw %dx,%dx # pass/error of e801h memory size
# call or merely pass cx,dx through
# without changing them.
movw $0xe801, %ax
int $0x15
jc mem_88
cmpw $0x0, %cx # Kludge to handle BIOSes which
jne 0f # report their extended memory in
cmpw $0x0, %dx # AX/BX rather than CX/DX. The spec
jne 0f # I have read seems to indicate that
movw %ax, %cx # AX/BX are more reasonable anyway.
movw %bx, %dx
0:
jmp fake_e820
# Finally try method 88.
mem_88:
movb $0x88, %ah
int $0x15
movw %ax, %cx
movw $0, %dx
fake_e820:
# Write entry for memory below 1MB.
movl $0x0, E820_ADDR(%di)
movl $0xa0000, E820_SIZE(%di)
movl $1, E820_TYPE(%di)
incb (E820_ENTRIES)
addw $E820_ENTRY_SIZE, %di
# Write entry for memory between 1MB and 16MB.
andl $0xffff, %ecx # convert to 32-bits
jz 0f
shll $10, %ecx # convert to bytes
movl $0x100000, E820_ADDR(%di)
movl %ecx, E820_SIZE(%di)
movl $1, E820_TYPE(%di)
incb (E820_ENTRIES)
addw $E820_ENTRY_SIZE, %di
0:
# Write entry for memory above 16MB.
andl $0xffff, %edx # convert to 32-bits
jz 1f
shll $16, %edx # convert to bytes
movl $0x1000000, E820_ADDR(%di)
movl %edx, E820_SIZE(%di)
movl $1, E820_TYPE(%di)
incb (E820_ENTRIES)
addw $E820_ENTRY_SIZE, %di
1:
get_mem_done:
pop %es
pop %ds
ret
# This subroutine disables APM if it is present.
disable_apm:
movw $0x5300, %ax # APM BIOS installation check
xorw %bx, %bx
int $0x15
jc disable_apm_done # error -> no APM BIOS
cmpw $0x504d, %bx # check for "PM" signature
jne disable_apm_done # no signature -> no APM BIOS
movw $0x5304, %ax # Disconnect first just in case
xorw %bx, %bx
int $0x15 # ignore return code
movw $0x5301, %ax # Real Mode connect
xorw %bx, %bx
int $0x15
jc disable_apm_done # error
movw $0x5308, %ax # Disable APM
mov $0xffff, %bx
xorw %cx, %cx
int $0x15
disable_apm_done:
ret
# This subroutine checks that the keyboard command queue is empty (after
# emptying the output buffers). No timeout is used - if this hangs there
# is something wrong with the machine, and we probably couldn't proceed
# anyway.
empty_8042:
call delay
inb $0x64, %al # 8042 status port
cmpb $0xff, %al # skip if not implemented
jz empty_8042_ret
testb $1, %al # anything in the output buffer?
jz no_output
call delay
inb $0x60, %al # read it
jmp empty_8042
no_output:
testb $2, %al # is input buffer full?
jnz empty_8042 # yes - loop
empty_8042_ret:
ret
# This subroutine provides a short delay.
delay:
.word 0x00eb # jmp $+2
ret
# A minimal GDT and IDT.
.align 4
gdt:
.quad 0x0000000000000000 # NULL descriptor
.quad 0x0000000000000000 # not used
.quad 0x00c09a0000007fff # 128MB 32-bit code at 0x000000
.quad 0x00c0920000007fff # 128MB 32-bit code at 0x000000
gdt_end:
.word 0 # for alignment
gdt_descr:
.word gdt_end - gdt - 1 # gdt limit
.long gdt - setup # gdt base - relocated at run time
.word 0 # for alignment
idt_descr:
.word 0 # idt limit=0
.long 0 # idt base=0
mt86plus_version:
.ascii "Memtest86+ v" , MT_VERSION
mt86plus_version_end:
.byte 0
pe_header:
.ascii "PE"
.word 0
coff_header:
#ifdef __x86_64__
.word IMAGE_FILE_MACHINE_X64 # Machine (x86-64)
#else
.word IMAGE_FILE_MACHINE_I386 # Machine (i386)
#endif
.word 4 # NumberOfSections
.long 0 # TimeDateStamp
.long 0 # PointerToSymbolTable
.long 0 # NumberOfSymbols
.word section_table - optional_header # SizeOfOptionalHeader
#ifdef __x86_64__
.word IMAGE_FILE_DEBUG_STRIPPED \
| IMAGE_FILE_LOCAL_SYMS_STRIPPED \
| IMAGE_FILE_LINE_NUMS_STRIPPED \
| IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics
#else
.word IMAGE_FILE_32BIT_MACHINE \
| IMAGE_FILE_DEBUG_STRIPPED \
| IMAGE_FILE_LOCAL_SYMS_STRIPPED \
| IMAGE_FILE_LINE_NUMS_STRIPPED \
| IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics.
#endif
optional_header:
#ifdef __x86_64__
.word IMAGE_NT_OPTIONAL_HDR64_MAGIC # PE32+ format
#else
.word IMAGE_NT_OPTIONAL_HDR32_MAGIC # PE32 format
#endif
.byte 0x02 # MajorLinkerVersion
.byte 0x14 # MinorLinkerVersion
.long _virt_text_size # SizeOfCode
.long _virt_init_size # SizeOfInitializedData
.long 0 # SizeOfUninitializedData
.long _virt_text_start + 0x1e0 # AddressOfEntryPoint
.long _virt_text_start # BaseOfCode
#ifndef __x86_64__
.long _virt_data_start # BaseOfData
#endif
extra_header_fields:
#ifdef __x86_64__
.quad IMAGE_BASE # ImageBase
#else
.long IMAGE_BASE # ImageBase
#endif
.long 4096 # SectionAlignment
.long 512 # FileAlignment
.word 0 # MajorOperatingSystemVersion
.word 0 # MinorOperatingSystemVersion
.word 0 # MajorImageVersion
.word 0 # MinorImageVersion
.word 0 # MajorSubsystemVersion
.word 0 # MinorSubsystemVersion
.long 0 # Win32VersionValue
.long _virt_img_size # SizeOfImage
.long _file_head_size # SizeOfHeaders
.long 0 # CheckSum
.word 10 # Subsystem (EFI application)
.word IMAGE_DLLCHARACTERISTICS_NX_COMPAT # DllCharacteristics
#ifdef __x86_64__
.quad 0 # SizeOfStackReserve
.quad 0 # SizeOfStackCommit
.quad 0 # SizeOfHeapReserve
.quad 0 # SizeOfHeapCommit
#else
.long 0 # SizeOfStackReserve
.long 0 # SizeOfStackCommit
.long 0 # SizeOfHeapReserve
.long 0 # SizeOfHeapCommit
#endif
.long 0 # LoaderFlags
.long IMAGE_DIRECTORY_ENTRY_DEBUG # NumberOfRvaAndSizes
.long 0 # DataDirectory.Export.VirtualAddress
.long 0 # DataDirectory.Export.Size
.long 0 # DataDirectory.Import.VirtualAddress
.long 0 # DataDirectory.Import.Size
.long 0 # DataDirectory.Resource.VirtualAddress
.long 0 # DataDirectory.Resource.Size
.long 0 # DataDirectory.Exception.VirtualAddress
.long 0 # DataDirectory.Exception.Size
.long 0 # DataDirectory.Certs.VirtualAddress
.long 0 # DataDirectory.Certs.Size
.long _virt_reloc_start # DataDirectory.BaseReloc.VirtualAddress
.long _real_reloc_size # DataDirectory.BaseReloc.Size
# Section table
section_table:
.ascii ".text"
.byte 0
.byte 0
.byte 0
.long _virt_text_size # VirtualSize
.long _virt_text_start # VirtualAddress
.long _file_text_size # SizeOfRawData
.long _file_text_start # PointerToRawData
.long 0 # PointerToRelocations
.long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers
.long IMAGE_SCN_MEM_READ \
| IMAGE_SCN_MEM_EXECUTE \
| IMAGE_SCN_CNT_CODE # Characteristics (section flags)
.ascii ".data"
.byte 0
.byte 0
.byte 0
.long _virt_data_size # VirtualSize
.long _virt_data_start # VirtualAddress
.long _file_data_size # SizeOfRawData
.long _file_data_start # PointerToRawData
.long 0 # PointerToRelocations
.long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers
.long IMAGE_SCN_MEM_READ \
| IMAGE_SCN_MEM_WRITE \
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
.ascii ".reloc"
.byte 0
.byte 0
.long _virt_reloc_size # VirtualSize
.long _virt_reloc_start # VirtualAddress
.long _file_reloc_size # SizeOfRawData
.long _file_reloc_start # PointerToRawData
.long 0 # PointerToRelocations
.long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers
.long IMAGE_SCN_MEM_READ \
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
.ascii ".sbat"
.byte 0
.byte 0
.byte 0
.long _virt_sbat_size # VirtualSize
.long _virt_sbat_start # VirtualAddress
.long _file_sbat_size # SizeOfRawData
.long _file_sbat_start # PointerToRawData
.long 0 # PointerToRelocations
.long 0 # PointerToLineNumbers
.word 0 # NumberOfRelocations
.word 0 # NumberOfLineNumbers
.long IMAGE_SCN_MEM_READ \
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
# Pad to the declared size.
.org (SETUP_SECS*512)
.section ".reloc"
.long 0 // Page RVA
.long 10 // Block Size (2*4+2)
.word (IMAGE_REL_BASED_ABSOLUTE << 12) + 0 // reloc 0 -> 0
.section ".sbat", "a", @progbits
.incbin "../boot/sbat.csv"