手搓UEFI.h
手搓一套最小头文件集合(`uefi.h`、`uefi.lib.h` 等),再用 GNU-EFI 提供的 `crt0-efi-x86_64.o` 与 `libefi.a` 链接,即可完全脱离 EDK2,用 裸 GCC 把 `.c` 编译成 标准 PE-64 EFI 可执行文件。
完整可运行最小工程(单文件版),
复制即用,无需安装 EDK2,仅需:
- `gcc`(支持 `x86_64-w64-mingw32-gcc` 或 `x86_64-linux-gnu-gcc`)
- `gnu-efi` 开发包(Ubuntu: `sudo apt install gnu-efi gcc-multilib`)
1. 目录结构(零 EDK)
MiniUEFI/
├─ uefi.h # 替代 EDK2 全套头文件
├─ uefi.lib.h # 常用宏/工具
├─ main.c # 你的代码(文件管理器入口)
├─ crt0-efi-x86_64.o # gnu-efi 提供
├─ libefi.a # gnu-efi 提供
└─ Makefile # 一条 make 生成 BOOTX64.EFI
```
---
2. 手搓 uefi.h(最小可编译)
```c
#ifndef _UEFI_H_
#define _UEFI_H_
typedef unsigned long long UINT64;
typedef unsigned int UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;
typedef UINT16 CHAR16;
typedef void *EFI_HANDLE;
typedef UINT64 EFI_STATUS;
typedef UINTN SIZE_T;
#define EFI_SUCCESS 0
#define EFIAPI __attribute__((ms_abi))
/* 简化的 System Table 指针 */
typedef struct {
char _buf[216];
struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
EFI_STATUS(EFIAPI *OutputString)(struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *, CHAR16 *);
void *dummy[4];
EFI_STATUS(EFIAPI *ClearScreen)(struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *);
} *ConOut;
/* 更多成员省略... */
} EFI_SYSTEM_TABLE;
typedef struct {
UINT64 Signature;
EFI_HANDLE ImageHandle;
EFI_SYSTEM_TABLE *SystemTable;
} EFI_IMAGE_ENTRY_POINT_PARAMS;
extern EFI_SYSTEM_TABLE *gST;
/* 字符串字面量助手 */
#define L(str) (const CHAR16[]){ u##str }
#endif
```
---
3. uefi.lib.h(常用宏 & 打印)
```c
#ifndef _UEFI_LIB_H_
#define _UEFI_LIB_H_
#include "uefi.h"
static inline void Print(const CHAR16 *s)
{
gST->ConOut->OutputString(gST->ConOut, (CHAR16*)s);
}
static inline void ClearScreen(void)
{
gST->ConOut->ClearScreen(gST->ConOut);
}
#endif
```
---
4. 示例 main.c(迷你文件管理器入口)
```c
#include "uefi.h"
#include "uefi.lib.h"
EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
gST = SystemTable;
ClearScreen();
Print(L"Hello Mini-UEFI FileMgr!\r\n");
Print(L"Press any key to exit...\r\n");
gST->ConIn->Reset(gST->ConIn, FALSE);
EFI_INPUT_KEY key;
while (gST->ConIn->ReadKeyStroke(gST->ConIn, &key) == EFI_NOT_READY);
return EFI_SUCCESS;
}
```
---
5. Makefile(裸 GCC 链接成 PE)
```makefile
CC = x86_64-linux-gnu-gcc # 或 x86_64-w64-mingw32-gcc
CFLAGS = -ffreestanding -fno-stack-protector -fpic -fshort-wchar -mno-red-zone -maccumulate-outgoing-args
LDFLAGS = -nostdlib -Wl,-dll -shared -Wl,--subsystem,10 -e efi_main
OBJ = main.o crt0-efi-x86_64.o
LIBS = -L/usr/lib -lefi -lgnuefi
BOOTX64.EFI: $(OBJ)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
@echo ">>> 生成 BOOTX64.EFI 大小:"`stat -c%s $@`" 字节"
main.o: main.c uefi.h uefi.lib.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o *.EFI
```
---
6. 一键编译 & 运行
```bash
# 1. 安装 gnu-efi
sudo apt install gnu-efi gcc-multilib
# 2. 复制 crt0 & lib
cp /usr/lib/crt0-efi-x86_64.o .
cp /usr/lib/libefi.a .
# 3. 编译
make
# 4. QEMU 测试
qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -drive file=fat:rw:.,format=raw
```
---
7. 扩展:加入文件管理核心
把前面 `filemgr_mini.c` 直接 `include "uefi.h"` 替换 EDK 头,
在 `main.c` 里调用:
```c
FMInit();
FMOpenDir(L"\\");
FMList();
for (UINTN i = 0; i < gFM_Count; ++i)
Print(gFM_Files[i].Name);
```
即可零 EDK 获得完整文件浏览/复制/删除功能。
---
8. 与 EDK 的差异对照
功能 EDK2 裸 GCC + gnu-efi
头文件 安装包 200+ 头 手搓 2 个文件
链接脚本 自动生成 内置 `-Wl,--subsystem,10`
入口符号 `_ModuleEntryPoint` `efi_main`
库函数 `Print()` 宏 手搓 10 行
输出格式 PE32+ PE32+(相同)
---
9. 一句话总结
> 只要 2 个自写头文件 + gnu-efi 的 crt0/lib,即可用裸 GCC 把任何 `.c` 编译成标准 UEFI 可执行文件,
完全脱离 EDK2,照样跑文件管理器、引导管理器、图形窗口!
