我试图在NASM中编写一个'Hello_world' EFI应用程序,但似乎无法使其工作。当我运行应用程序时(在VirtualBox中),它不打印任何东西。它只是挂着。下面是我的代码:
Bits 64
DEFAULT REL
START:
PE:
HEADER_START:
STANDARD_HEADER:
.DOS_SIGNATURE db 'MZ' ; The DOS signature. This is apparently compulsory
.DOS_HEADERS times 60-($-STANDARD_HEADER) db 0 ; The DOS Headers. Probably not needed by UEFI
.SIGNATURE_POINTER dd .PE_SIGNATURE - START ; Pointer to the PE Signature
.DOS_STUB times 64 db 0 ; The DOS stub. Fill with zeros
.PE_SIGNATURE db 'PE', 0x00, 0x00 ; This is the pe signature. The characters 'PE' followed by 2 null bytes
.MACHINE_TYPE dw 0x8664 ; Targetting the x64 machine
.NUMBER_OF_SECTIONS dw 3 ; Number of sections. Indicates size of section table that immediately follows the headers
.CREATED_DATE_TIME dd 1657582794 ; Number of seconds since 1970 since when the file was created
.SYMBOL_TABLE_POINTER dd 0x00 ; Pointer to the symbol table. There should be no symbol table in an image so this is 0
.NUMBER_OF_SYMBOLS dd 0x00 ; Because there are no symbol tables in an image
.OPTIONAL_HEADER_SIZE dw OPTIONAL_HEADER_STOP - OPTIONAL_HEADER_START ; Size of the optional header
.CHARACTERISTICS dw 0b0010111000100010 ; These are the attributes of the file
OPTIONAL_HEADER_START:
.MAGIC_NUMBER dw 0x020B ; PE32+ (i.e. pe64) magic number
.MAJOR_LINKER_VERSION db 0 ; I'm sure this isn't needed. So set to 0
.MINOR_LINKER_VERSION db 0 ; This too
.SIZE_OF_CODE dd CODE_END - CODE ; The size of the code section
.INITIALIZED_DATA_SIZE dd DATA_END - DATA ; Size of initialized data section
.UNINITIALIZED_DATA_SIZE dd 0x00 ; Size of uninitialized data section
.ENTRY_POINT_ADDRESS dd EntryPoint - START ; Address of entry point relative to image base when the image is loaded in memory
.BASE_OF_CODE_ADDRESS dd CODE - START ; Relative address of base of code
.IMAGE_BASE dq 0x400000 ; Where in memory we would prefer the image to be loaded at
.SECTION_ALIGNMENT dd 0x1000 ; Alignment in bytes of sections when they are loaded in memory. Align to page boundry (4kb)
.FILE_ALIGNMENT dd 0x1000 ; Alignment of sections in the file. Also align to 4kb
.MAJOR_OS_VERSION dw 0x00 ; I'm not sure UEFI requires these and the following 'version woo'
.MINOR_OS_VERSION dw 0x00 ; More of these version thingies are to follow. Again, not sure UEFI needs them
.MAJOR_IMAGE_VERSION dw 0x00 ; Major version of the image
.MINOR_IMAGE_VERSION dw 0x00 ; Minor version of the image
.MAJOR_SUBSYSTEM_VERSION dw 0x00 ;
.MINOR_SUBSYSTEM_VERSION dw 0x00 ;
.WIN32_VERSION_VALUE dd 0x00 ; Reserved, must be 0
.IMAGE_SIZE dd END - START ; The size in bytes of the image when loaded in memory including all headers
.HEADERS_SIZE dd HEADER_END - HEADER_START ; Size of all the headers
.CHECKSUM dd 0x00 ; Hoping this doesn't break the application
.SUBSYSTEM dw 10 ; The subsystem. In this case we're making a UEFI application.
.DLL_CHARACTERISTICS dw 0b000011110010000 ; I honestly don't know what to put here
.STACK_RESERVE_SIZE dq 0x200000 ; Reserve 2MB for the stack... I guess...
.STACK_COMMIT_SIZE dq 0x1000 ; Commit 4kb of the stack
.HEAP_RESERVE_SIZE dq 0x200000 ; Reserve 2MB for the heap... I think... :D
.HEAP_COMMIT_SIZE dq 0x1000 ; Commit 4kb of heap
.LOADER_FLAGS dd 0x00 ; Reserved, must be zero
.NUMBER_OF_RVA_AND_SIZES dd 0x10 ; Number of entries in the data directory
DATA_DIRECTORIES:
EDATA:
.address dd 0 ; Address of export table
.size dd 0 ; Size of export table
IDATA:
.address dd 0 ; Address of import table
.size dd 0 ; Size of import table
RSRC:
.address dd 0 ; Address of resource table
.size dd 0 ; Size of resource table
PDATA:
.address dd 0 ; Address of exception table
.size dd 0 ; Size of exception table
CERT:
.address dd 0 ; Address of certificate table
.size dd 0 ; Size of certificate table
RELOC:
.address dd END - START ; Address of relocation table
.size dd 0 ; Size of relocation table
DEBUG:
.address dd 0 ; Address of debug table
.size dd 0 ; Size of debug table
ARCHITECTURE:
.address dd 0 ; Reserved. Must be 0
.size dd 0 ; Reserved. Must be 0
GLOBALPTR:
.address dd 0 ; RVA to be stored in global pointer register
.size dd 0 ; Must be 0
TLS:
.address dd 0 ; Address of TLS table
.size dd 0 ; Size of TLS table
LOADCONFIG:
.address dd 0 ; Address of Load Config table
.size dd 0 ; Size of Load Config table
BOUNDIMPORT:
.address dd 0 ; Address of bound import table
.size dd 0 ; Size of bound import table
IAT:
.address dd 0 ; Address of IAT
.size dd 0 ; Size of IAT
DELAYIMPORTDESCRIPTOR:
.address dd 0 ; Address of delay import descriptor
.size dd 0 ; Size of delay import descriptor
CLRRUNTIMEHEADER:
.address dd 0 ; Address of CLR runtime header
.size dd 0 ; Size of CLR runtime header
RESERVED:
.address dd 0 ; Reserved, must be 0
.size dd 0 ; Reserved, must be 0
OPTIONAL_HEADER_STOP:
HEADER_END:
SECTION_HEADERS:
SECTION_CODE:
.name db ".text", 0x00, 0x00, 0x00
.virtual_size dd CODE_END - CODE
.virtual_address dd CODE - START
.size_of_raw_data dd CODE_END - CODE
.pointer_to_raw_data dd CODE - START
.pointer_to_relocations dd 0 ; Set to 0 for executable images
.pointer_to_line_numbers dd 0 ; There are no COFF line numbers
.number_of_relocations dw 0 ; Set to 0 for executable images
.number_of_line_numbers dw 0 ; Should be 0 for images
.characteristics dd 0x70000020 ; Need to read up more on this
SECTION_DATA:
.name db ".data", 0x00, 0x00, 0x00
.virtual_size dd DATA_END - DATA
.virtual_address dd DATA - START
.size_of_raw_data dd DATA_END - DATA
.pointer_to_raw_data dd DATA - START
.pointer_to_relocations dd 0
.pointer_to_line_numbers dd 0
.number_of_relocations dw 0
.number_of_line_numbers dw 0
.characteristics dd 0xD0000040
SECTION_RELOC:
.name db ".reloc", 0x00, 0x00
.virtual_size dd 0
.virtual_address dd 0
.size_of_raw_data dd 0
.pointer_to_raw_data dd END - START
.pointer_to_relocations dd 0
.pointer_to_line_numbers dd 0
.number_of_relocations dw 0
.number_of_line_numbers dw 0
.characteristics dd 0xC2000040
times 4096-($-PE) db 0
CODE:
; The code begins here with the entry point
EntryPoint:
; First order of business is to store the values that were passed to us by EFI
mov [EFI_IMAGE_HANDLE], rcx
mov [EFI_SYSTEM_TABLE], rdx
; Locate OutputString of the TEXT_OUTPUT_PROTOCOL
add rdx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
mov rcx, [abs rdx] ; This is the first parameter to the call
mov rdx, [abs rdx] ; Now rdx points to simple text output protocol
add rdx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OutputString ; Now rdx points to output string
mov rax, [abs rdx] ; We'll later do `call rax`
lea rdx, [hello_message] ; The string to be printed
sub rsp, 40 ; Shadow space on the stack before the call
call rax
mov eax, EFI_SUCCESS
retn
align 4096
CODE_END:
; Data begins here
DATA:
EFI_IMAGE_HANDLE dq 0x00 ; EFI will give use this in rcx
EFI_SYSTEM_TABLE dq 0x00 ; And this in rdx
hello_message db __utf16__ `Hello_world ` ; EFI strings are UTF16 and null-terminated
align 4096
DATA_END:
END:
; Define the needed EFI constants and offsets here.
EFI_SUCCESS equ 0
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL equ 64
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_Reset equ 0
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OutputString equ 8
假设文件保存为"hello。asm',我与nasm -f bin -o hello.efi hello.asm
组装。我能做错什么吗?
p。S:我知道我可以用结构体来计算偏移量,但是我想自己计算。
最大功率S:这是我在网上找到的可用的FASM版本:http://x86asm.net/articles/uefi-programming-first-steps/index.html#Building-an-UEFI-application。我用这个在线工具(https://manalyzer.org/)检查,发现它的入口点在.data
部分。现在我很好奇它是如何以及为什么工作的。
我能够解决这个问题要感谢osdev论坛上的那些家伙。.HEADERS_SIZE
应该包括Section Headers,并且应该是.FILE_ALIGNMENT
的倍数。这意味着HEADER_END:
应该出现在times 4096-($-PE) db 0
之后,因为我是通过执行HEADER_END - HEADER_START
来计算.HEADERS_SIZE
的。
Bits 64
DEFAULT REL
START:
PE:
HEADER_START:
STANDARD_HEADER:
.DOS_SIGNATURE db 'MZ' ; The DOS signature. This is apparently compulsory
.DOS_HEADERS times 60-($-STANDARD_HEADER) db 0 ; The DOS Headers. Probably not needed by UEFI
.SIGNATURE_POINTER dd .PE_SIGNATURE - START ; Pointer to the PE Signature
.DOS_STUB times 64 db 0 ; The DOS stub. Fill with zeros
.PE_SIGNATURE db 'PE', 0x00, 0x00 ; This is the pe signature. The characters 'PE' followed by 2 null bytes
.MACHINE_TYPE dw 0x8664 ; Targetting the x64 machine
.NUMBER_OF_SECTIONS dw 3 ; Number of sections. Indicates size of section table that immediately follows the headers
.CREATED_DATE_TIME dd 1657582794 ; Number of seconds since 1970 since when the file was created
.SYMBOL_TABLE_POINTER dd 0x00 ; Pointer to the symbol table. There should be no symbol table in an image so this is 0
.NUMBER_OF_SYMBOLS dd 0x00 ; Because there are no symbol tables in an image
.OPTIONAL_HEADER_SIZE dw OPTIONAL_HEADER_STOP - OPTIONAL_HEADER_START ; Size of the optional header
.CHARACTERISTICS dw 0b0010111000100010 ; These are the attributes of the file
OPTIONAL_HEADER_START:
.MAGIC_NUMBER dw 0x020B ; PE32+ (i.e. pe64) magic number
.MAJOR_LINKER_VERSION db 0 ; I'm sure this isn't needed. So set to 0
.MINOR_LINKER_VERSION db 0 ; This too
.SIZE_OF_CODE dd CODE_END - CODE ; The size of the code section
.INITIALIZED_DATA_SIZE dd DATA_END - DATA ; Size of initialized data section
.UNINITIALIZED_DATA_SIZE dd 0x00 ; Size of uninitialized data section
.ENTRY_POINT_ADDRESS dd EntryPoint - START ; Address of entry point relative to image base when the image is loaded in memory
.BASE_OF_CODE_ADDRESS dd CODE - START ; Relative address of base of code
.IMAGE_BASE dq 0x400000 ; Where in memory we would prefer the image to be loaded at
.SECTION_ALIGNMENT dd 0x1000 ; Alignment in bytes of sections when they are loaded in memory. Align to page boundry (4kb)
.FILE_ALIGNMENT dd 0x1000 ; Alignment of sections in the file. Also align to 4kb
.MAJOR_OS_VERSION dw 0x00 ; I'm not sure UEFI requires these and the following 'version woo'
.MINOR_OS_VERSION dw 0x00 ; More of these version thingies are to follow. Again, not sure UEFI needs them
.MAJOR_IMAGE_VERSION dw 0x00 ; Major version of the image
.MINOR_IMAGE_VERSION dw 0x00 ; Minor version of the image
.MAJOR_SUBSYSTEM_VERSION dw 0x00 ;
.MINOR_SUBSYSTEM_VERSION dw 0x00 ;
.WIN32_VERSION_VALUE dd 0x00 ; Reserved, must be 0
.IMAGE_SIZE dd END - START ; The size in bytes of the image when loaded in memory including all headers
.HEADERS_SIZE dd HEADER_END - HEADER_START ; Size of all the headers
.CHECKSUM dd 0x00 ; Hoping this doesn't break the application
.SUBSYSTEM dw 10 ; The subsystem. In this case we're making a UEFI application.
.DLL_CHARACTERISTICS dw 0b000011110010000 ; I honestly don't know what to put here
.STACK_RESERVE_SIZE dq 0x200000 ; Reserve 2MB for the stack... I guess...
.STACK_COMMIT_SIZE dq 0x1000 ; Commit 4kb of the stack
.HEAP_RESERVE_SIZE dq 0x200000 ; Reserve 2MB for the heap... I think... :D
.HEAP_COMMIT_SIZE dq 0x1000 ; Commit 4kb of heap
.LOADER_FLAGS dd 0x00 ; Reserved, must be zero
.NUMBER_OF_RVA_AND_SIZES dd 0x10 ; Number of entries in the data directory
DATA_DIRECTORIES:
EDATA:
.address dd 0 ; Address of export table
.size dd 0 ; Size of export table
IDATA:
.address dd 0 ; Address of import table
.size dd 0 ; Size of import table
RSRC:
.address dd 0 ; Address of resource table
.size dd 0 ; Size of resource table
PDATA:
.address dd 0 ; Address of exception table
.size dd 0 ; Size of exception table
CERT:
.address dd 0 ; Address of certificate table
.size dd 0 ; Size of certificate table
RELOC:
.address dd END - START ; Address of relocation table
.size dd 0 ; Size of relocation table
DEBUG:
.address dd 0 ; Address of debug table
.size dd 0 ; Size of debug table
ARCHITECTURE:
.address dd 0 ; Reserved. Must be 0
.size dd 0 ; Reserved. Must be 0
GLOBALPTR:
.address dd 0 ; RVA to be stored in global pointer register
.size dd 0 ; Must be 0
TLS:
.address dd 0 ; Address of TLS table
.size dd 0 ; Size of TLS table
LOADCONFIG:
.address dd 0 ; Address of Load Config table
.size dd 0 ; Size of Load Config table
BOUNDIMPORT:
.address dd 0 ; Address of bound import table
.size dd 0 ; Size of bound import table
IAT:
.address dd 0 ; Address of IAT
.size dd 0 ; Size of IAT
DELAYIMPORTDESCRIPTOR:
.address dd 0 ; Address of delay import descriptor
.size dd 0 ; Size of delay import descriptor
CLRRUNTIMEHEADER:
.address dd 0 ; Address of CLR runtime header
.size dd 0 ; Size of CLR runtime header
RESERVED:
.address dd 0 ; Reserved, must be 0
.size dd 0 ; Reserved, must be 0
OPTIONAL_HEADER_STOP:
SECTION_HEADERS:
SECTION_CODE:
.name db ".text", 0x00, 0x00, 0x00
.virtual_size dd CODE_END - CODE
.virtual_address dd CODE - START
.size_of_raw_data dd CODE_END - CODE
.pointer_to_raw_data dd CODE - START
.pointer_to_relocations dd 0 ; Set to 0 for executable images
.pointer_to_line_numbers dd 0 ; There are no COFF line numbers
.number_of_relocations dw 0 ; Set to 0 for executable images
.number_of_line_numbers dw 0 ; Should be 0 for images
.characteristics dd 0x70000020 ; Need to read up more on this
SECTION_DATA:
.name db ".data", 0x00, 0x00, 0x00
.virtual_size dd DATA_END - DATA
.virtual_address dd DATA - START
.size_of_raw_data dd DATA_END - DATA
.pointer_to_raw_data dd DATA - START
.pointer_to_relocations dd 0
.pointer_to_line_numbers dd 0
.number_of_relocations dw 0
.number_of_line_numbers dw 0
.characteristics dd 0xD0000040
SECTION_RELOC:
.name db ".reloc", 0x00, 0x00
.virtual_size dd 0
.virtual_address dd END - START
.size_of_raw_data dd 0
.pointer_to_raw_data dd END - START
.pointer_to_relocations dd 0
.pointer_to_line_numbers dd 0
.number_of_relocations dw 0
.number_of_line_numbers dw 0
.characteristics dd 0xC2000040
times 4096-($-PE) db 0
HEADER_END:
CODE:
; The code begins here with the entry point
EntryPoint:
; First order of business is to store the values that were passed to us by EFI
mov [EFI_IMAGE_HANDLE], rcx
mov [EFI_SYSTEM_TABLE], rdx
; Locate OutputString of the TEXT_OUTPUT_PROTOCOL
add rdx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
mov rcx, [rdx] ; This is the first parameter to the call
mov rdx, [rdx] ; Now rdx points to simple text output protocol
add rdx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OutputString ; Now rdx points to output string
mov rax, [rdx] ; We'll later do `call rax`
lea rdx, [hello_message] ; The string to be printed
sub rsp, 32 ; Shadow space on the stack before the call
call rax
add rsp, 32
mov rax, EFI_SUCCESS
ret
align 4096
CODE_END:
; Data begins here
DATA:
EFI_IMAGE_HANDLE dq 0x00 ; EFI will give use this in rcx
EFI_SYSTEM_TABLE dq 0x00 ; And this in rdx
hello_message db __utf16__ `Hello_world ` ; EFI strings are UTF16 and null-terminated
align 4096
DATA_END:
END:
; Define the needed EFI constants and offsets here.
EFI_SUCCESS equ 0
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL equ 64
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_Reset equ 0
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OutputString equ 8