BIOS 编译过程:C文件到EFI文件

本文详细解析了从C源代码到EFI可执行文件的编译步骤,包括使用cl.exe生成对象文件,lib.exe创建库文件,link.exe链接库文件生成DLL,最后通过GenFw工具生成EFI文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    上一篇Blog介绍了exe的生成,从C 到 exe可以通过“cl /c”来编译到“obj”,然后再“link”到exe。如果用到其他lib的话,还需要用“/I<dir> add to include search path”来添加相应的lib文件。

    这里我们写了一个最精简的Pkg,里面有个最精简的UEFI app,用来探究C文件到EFI文件的过程。这个UEFI app 代码只有“Hello.inf”和“Hello.c”两个文件,使用了最少的lib,以方便看流程:

#include <Uefi.h>

EFI_STATUS
EFIAPI
Main (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return EFI_SUCCESS;
}

    用“build > log.txt”可以把build log记录下来分析,log里面跟这个app相关的命令有以下几条:

	"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\cl.exe" /Fod:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\.\Hello.obj /nologo /arch:IA32 /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2 /GL /FIAutoGen.h /EHs-c- /GR- /GF /Id:\udk2017_25818\AppPkgSample\Hello  /Id:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG  /Id:\udk2017_25818\MdePkg  /Id:\udk2017_25818\MdePkg\Include  /Id:\udk2017_25818\MdePkg\Include\Ia32 d:\udk2017_25818\AppPkgSample\Hello\Hello.c
	"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\cl.exe" /Fod:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\.\AutoGen.obj /nologo /arch:IA32 /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2 /GL /FIAutoGen.h /EHs-c- /GR- /GF /Id:\udk2017_25818\AppPkgSample\Hello  /Id:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG  /Id:\udk2017_25818\MdePkg  /Id:\udk2017_25818\MdePkg\Include  /Id:\udk2017_25818\MdePkg\Include\Ia32 d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\AutoGen.c
	"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\lib.exe" /NOLOGO /LTCG /OUT:d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\Hello.lib @d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\object_files.lst
	"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\link.exe" /OUT:d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\Hello.dll /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4254 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:_ModuleEntryPoint /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /MERGE:.rdata=.data  @d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\static_library_files.lst
	"GenFw" -e UEFI_APPLICATION -o d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\Hello.efi d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\Hello.dll

    这个过程就是EFI编译的过程,我们来分析每一步的意义,每一个命令接收了什么东西,产出了什么东西,都要搞清楚。

    第一步:命令"cl.exe",输入:Hello.c,输出:Hello.obj. 主要参数:/Fo 生成obj文件,/c 只编译不链接,/I 设定搜索目录。如下:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\cl.exe" /Fod:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\.\Hello.obj /nologo /arch:IA32 /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2 /GL /FIAutoGen.h /EHs-c- /GR- /GF /Id:\udk2017_25818\AppPkgSample\Hello  /Id:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG  /Id:\udk2017_25818\MdePkg  /Id:\udk2017_25818\MdePkg\Include  /Id:\udk2017_25818\MdePkg\Include\Ia32 d:\udk2017_25818\AppPkgSample\Hello\Hello.c

    第二步:命令"cl.exe",输入:AutoGen.c,输出:AutoGen.obj ,如下:

	"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\cl.exe" /Fod:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\.\AutoGen.obj /nologo /arch:IA32 /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2 /GL /FIAutoGen.h /EHs-c- /GR- /GF /Id:\udk2017_25818\AppPkgSample\Hello  /Id:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG  /Id:\udk2017_25818\MdePkg  /Id:\udk2017_25818\MdePkg\Include  /Id:\udk2017_25818\MdePkg\Include\Ia32 d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\AutoGen.c

    第三步:命令"lib.exe",输入:Hello.obj&AutoGen (见objobject_files.lst),输出:Hello.lib。如下:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\lib.exe" /NOLOGO /LTCG /OUT:d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\Hello.lib @d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\object_files.lst

    其中,object_files.lst内容如下,可见用到了第一步的Hello.obj和第二步的AutoGen.obj:

d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/AppPkgSample/Hello/Hello/OUTPUT/Hello.obj
d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/AppPkgSample/Hello/Hello/OUTPUT/AutoGen.obj

    第四步:命令"link.exe",输入:“static_library_files.lst”里面的lib文件,输出:Hello.dll。如下:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Vc\bin\link.exe" /OUT:d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\Hello.dll /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4254 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:_ModuleEntryPoint /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /MERGE:.rdata=.data  @d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\OUTPUT\static_library_files.lst

其中,static_library_files.lst内容如下,可见用到了第三步生成的Hello.lib:

d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/MdePkg/Library/BaseMemoryLib/BaseMemoryLib/OUTPUT/BaseMemoryLib.lib
d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/MdePkg/Library/BasePcdLibNull/BasePcdLibNull/OUTPUT/BasePcdLibNull.lib
d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/MdePkg/Library/BaseLib/BaseLib/OUTPUT/BaseLib.lib
d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull/OUTPUT/BaseDebugLibNull.lib
d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib/OUTPUT/UefiBootServicesTableLib.lib
d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint/OUTPUT/UefiApplicationEntryPoint.lib
d:/udk2017_25818/Build/AppPkgSample/RELEASE_VS2013x86/IA32/AppPkgSample/Hello/Hello/OUTPUT/Hello.lib
    第五步:命令"GenFw",输入:Hello.dll,输出:Hello.efi。如下:
"GenFw" -e UEFI_APPLICATION -o d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\Hello.efi d:\udk2017_25818\Build\AppPkgSample\RELEASE_VS2013x86\IA32\AppPkgSample\Hello\Hello\DEBUG\Hello.dll
<think>好的,我现在要帮用户了解如何将嵌入式Linux系统编译EFI文件。首先,我需要确认用户的需求是什么。EFI文件通常与UEFI启动相关,所以用户可能是在尝试将他们的嵌入式Linux系统配置为通过UEFI启动,而不是传统的BIOS。这可能涉及到内核的编译、引导加载器的设置,以及生成符合UEFI标准的可执行文件。 接下来,我应该回忆一下UEFI启动的基本流程。UEFI系统通常需要一个FAT格式的分区,其中包含EFI可执行文件,这些文件通常位于EFI/BOOT目录下,比如bootx64.efi。因此,用户可能需要将他们的引导加载器(如GRUB)编译EFI格式,或者直接使用支持EFI stub的内核。 然后,我需要考虑嵌入式Linux系统的组成部分。通常包括引导加载器、内核、根文件系统等。编译EFI文件可能主要涉及内核和引导加载器的部分。例如,Linux内核从某个版本开始支持EFI stub,允许内核自身作为EFI可执行文件直接由UEFI固件加载,而无需单独的引导加载器。 接下来,步骤可能包括配置内核启用EFI支持,编译内核生成EFI可执行文件,然后处理initramfs和根文件系统。此外,可能需要使用工具如objcopy将内核转换为EFI格式,或者使用grub-mkstandalone来生成包含配置的GRUB EFI文件。 用户可能需要知道具体的编译步骤,比如如何配置内核选项,如何生成initramfs,如何将内核和initramfs打包成EFI可执行文件。同时,还要考虑不同架构的支持,比如x86_64或ARM64,对应的EFI文件路径可能不同。 另外,需要提醒用户关于UEFI启动时的分区设置,确保EFI系统分区(ESP)正确格式化,并且相关文件放置在正确的位置。可能还需要配置UEFI启动项,或者使用efibootmgr工具来管理启动条目。 潜在的问题可能包括内核配置中未启用必要的EFI选项,生成的EFI文件无法被UEFI识别,或者引导过程中找不到根文件系统。需要指导用户检查内核配置,确认EFI相关的选项如CONFIG_EFI_STUB、CONFIG_EFI_PARTITION等是否启用。此外,可能还需要处理模块依赖和initramfs的正确生成,确保必要的驱动和文件系统模块被包含。 用户可能还需要了解如何测试生成的EFI文件,比如在QEMU虚拟机上模拟UEFI环境,或者在实际硬件上通过USB驱动器启动。这时候可能需要使用OVMF(UEFI固件用于QEMU)来进行测试。 最后,应该总结整个流程,强调关键步骤和常见问题,帮助用户系统地完成编译和配置过程。可能需要分步骤说明,从准备工具链、内核配置、编译、生成EFI文件,到部署和测试,确保每个环节都清晰明确。</think>将嵌入式Linux系统编译EFI可执行文件是一个涉及多步骤的系统级工程。以下是关键步骤和原理说明: --- ### 一、基础概念 1. **EFI启动原理** UEFI固件会直接加载`FAT32`格式的**EFI系统分区(ESP)**中的`.efi`文件,无需传统BIOS的MBR引导机制。 2. **嵌入式Linux组件** 需整合以下部分: - **EFI引导程序** (如GRUB、systemd-boot) - **Linux内核** (需启用EFI stub) - **Initramfs** (可选,用于早期用户态) - **根文件系统** (如SquashFS、ext4) --- ### 二、内核配置(关键步骤) ```bash # 进入内核源码目录 make menuconfig ``` 需启用的核心选项: ``` Processor type and features ---> [*] EFI runtime service support [*] EFI stub support Device Drivers ---> Firmware Drivers ---> [*] EFI Variable Support via sysfs Executable file formats ---> [*] Kernel support for ELF binaries [*] Kernel support for MISC binaries ``` --- ### 三、生成EFI可执行文件 #### 方法1:直接编译内核为EFI ```bash # 编译内核时生成EFI文件 make -j$(nproc) bzImage cp arch/x86/boot/bzImage linux.efi ``` #### 方法2:使用objcopy转换 ```bash objcopy \ --add-section .osrel=/etc/os-release \ --add-section .cmdline=cmdline.txt \ --add-section .linux="vmlinuz" \ --add-section .initrd="initrd.img" \ /usr/lib/systemd/boot/efi/linuxx64.efi.stub \ output.efi ``` --- ### 四、引导加载器集成(以GRUB为例) 1. **编译GRUB为EFI** ```bash ./configure --target=x86_64 --with-platform=efi make ./grub-mkstandalone -O x86_64-efi -o grubx64.efi ``` 2. **配置文件** 在ESP分区创建`/EFI/BOOT/grub.cfg`: ```grub menuentry "Embedded Linux" { linux /vmlinuz root=/dev/sda2 initrd /initrd.img } ``` --- ### 五、部署结构示例 ``` /EFI/BOOT/ ├── bootx64.efi # GRUB或systemd-boot ├── grub.cfg ├── vmlinuz # 内核 └── initrd.img # 初始内存盘 ``` --- ### 六、验证与调试 1. **QEMU测试** ```bash qemu-system-x86_64 -bios OVMF.fd -hda fat:rw:/path/to/esp ``` 2. **日志查看** 通过`dmesg | grep -i efi`检查内核是否成功识别EFI环境。 --- ### 七、常见问题 1. **无法加载驱动** 在initramfs中确保包含`efivarfs`和`efivars`模块。 2. **Secure Boot限制** 需对EFI文件进行签名或禁用Secure Boot。 3. **架构不匹配** ARM64架构需使用`aa64`前缀文件,如`grubaa64.efi`。 --- 通过以上步骤,您可以将嵌入式Linux系统整合为符合UEFI标准的启动方案。实际部署时需根据硬件平台调整架构参数和驱动配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值