问题描述
我想在 Flat Assembler 制作的 EFI 应用程序中通过 UEFI 输出字符串。 这是我的代码
format pe64 dll efi
entry efimain
section '.text' code readable executable
efimain:
sub rsp,20h
mov [EfiHandle],rcx
mov [SystemTable],rdx
mov [ReturnAddr],rsp
lea rdx,[_hello]
mov rcx,[SystemTable]
mov rcx,[rcx + 60]
mov rcx,[rcx + 8]
call rcx
jmp $
section '.data' data readable writeable
EfiHandle dq 0
SystemTable dq 0
ReturnAddr dq 0
ConOutAddr dq 0
_hello du 'Hello from UEFI'
基于最新的 UEFI 规范,SystemTable 在 x64 架构上的 RDX 中移交。除了基于规范的系统表被描述为
typedef struct {
EFI_TABLE_HEADER Hdr;
CHAR16 *FirmwareVendor;
UINT32 FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_BOOT_SERVICES *BootServices;
UINTN NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
表头定义为
typedef struct {
UINT64 Signature;
UINT32 Revision;
UINT32 HeaderSize;
UINT32 CRC32;
UINT32 Reserved;
} EFI_TABLE_HEADER;
EFI_HANDLE 定义为 VOID *,假设它们表示 x64 架构上的 8 字节指针。
我知道创建结构会更简单,但我想直接进行数学运算以找到正确的偏移量。根据我从 SystemTable Offset + 60(Header + SystemTable 的其他部分)获取 ConOut 指针的计算。然后 OutPutString 方法在输出协议中,定义为:
typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
EFI_TEXT_RESET Reset;
EFI_TEXT_STRING OutputString;
EFI_TEXT_TEST_STRING TestString;
EFI_TEXT_QUERY_MODE QueryMode;
EFI_TEXT_SET_MODE SetMode;
EFI_TEXT_SET_ATTRIBUTE SetAttribute;
EFI_TEXT_CLEAR_SCREEN ClearScreen;
EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition;
EFI_TEXT_ENABLE_CURSOR EnableCursor;
SIMPLE_TEXT_OUTPUT_MODE *Mode;
} EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
所以在通过 mov rcx,[rcx + 60]
移动输出协议的偏移量后,我需要跳转到我想要调用的 OutputString,所以我需要再次跳过 8 个字节 mov rcx,[rcx + 8]
。
由于我对 Assembler 不是很熟悉,我很确定问题一定在于找到了 OutputString 方法的正确 OffSet,但我无法发现它。
解决方法
Margaret 向我指出了解决方案:
-
是的,我错过了参数传递部分,第一个参数是指向输出协议的指针(因此必须在 RCX 中),第二个值是 RDX 中的字符串。所以在 rax 中将指针移动到 OutputString 确实有效
-
偏移量仍然不正确。我刚刚再次阅读了规范,它说
除非另有说明,否则所有数据类型都是自然对齐的。 结构在等于最大内部的边界上对齐 结构和内部数据的数据被隐式填充到 实现自然对齐。
因此,作为 UINT32 的 FirmwareRevision 的大小在内存中实际上也是 8 个字节(填充)。因此 ConOut 的偏移量为 0x40(或 64 字节)。
通过这些更改,我终于将字符串打印到屏幕上。