(本文已参与“新人创作礼”活动,一起开启掘金创作之路。)
(转载请保留-《UEFI编程实践》并行博客,作者:罗冰)
上一篇博客中,在编译AppPkg的时候,遇到了问题,编译的时候出错。错误的提示在上一篇博客中贴出来了,这里不再贴出。针对此问题,我查找了一些资料,做了若干实验,姑且以杂谈的形式记录下来。
1 EADK
为了方便使用标准的C库,EDKII中提供了开发包:EDK II Application Development Kit,简称为EADK。它最早脱胎于EFI_ToolKit,是Intel推广EFI的范例程序包。
随着UEFI的法发展,原来的架构无法统一,EFI_ToolKit的代码被分别整合到各个Package中了。其中的python和Application Development Kit就由EADK接替,并维护着。不过,从github上的日期来看,最后维护的时间也是2015了,是不是后面放弃了?
不管如何,它能很方便的用来构建Uefi程序,也能直接使用熟悉的C库函数,之前博客中的大量代码都使用了它。 它包含了三个Package:
图1 EADK
分别是AppPkg、StdLib和StdLibPrivateInternalFiles。其中,StdLibPrivateInternalFiles中的函数最好不要使用,很有可能未来会取消掉。
之前的博客中,以main()作为主函数的程序,都是基于AppPkg来进行编译的,使用了StdLib库。我很想将原来的代码在linux开发环境下进行编译,包括IA32以及X64,可惜遇到了一些阻碍。
以下是EADK的wiki和github下载地址:
github.com/tianocore/t…
github.com/tianocore/e…
2 EDK2 编译流程
在寻找AppPkg无法编译通过的原因时,我把EDk2的编译过程仔细读了一下,期望从中找到一些线索。
平常编译的时候,我一般遵循两个步骤来进行编译:
1) 设置编译环境。使用edksetup.bat或者edksetup.sh,设置工作环境以及工具路径和配置文件的路径等;
2) 编译指定的Package或者程序。使用build工具对指定package或程序进行编译,相关的参数可以通过命令行给出,也可在target.txt中设定。
编译的过程中涉及到各种文件的处理,包括DEC文件、DSC文件、INF文件等,编译的流程图如下:
图2 EDK2平台 编译流程
INF文件,Module Information File,模块描述文件。Module可以是可执行文件,也可以是库文件。具体可以参考Inf的文件说明,细节太多,就不列出内容了。
DEC文件,Package Declaration File,组件包描述文件,用于支持编译Package以及发布Modules。在此文件中,可以在[Includes]下指定头文件路径,在[LibraryClasses]下指定依赖的库以及路径。
DSC文件,Platform Description File,平台描述文件。
EDK Build Tools是EDK II通用组件之一,为了在自己的Module中使用EDK II通用组件包括EDK II Build Tools,必须要在自己的Module中编写DSC和FDF配置文件。
DSC文件用来配合FDF/DEC/INF等文件,最终生成PE32/PE32+/Coff二进制文件。
DSC文件中包括:\
- EDK II Module INF Files\
- EDK Components\
- EDK Libraries (only used by EDK Components)\
- EDK II Library Class Instance Mappings (only used by EDK II Modules)\
- EDK II PCD Entries
LibraryClasses字段表示当前Package依赖哪些库以及库描述文件路径,Components字段表示当前Package对外提供哪些库以及库描述文件路径。
FDF文件,Flash Definition File,Flash布局描述文件,类似于lds链接脚本。在编写多个Option ROM时,可以用这个文件来进行管理。
3 AppPkg 为何编译不了?
准确地说,是AppPkg在Ubutnu 16.04下,使用最新的EDK2(201908 git clone),目标架构为IA32时编译不通过。
我本来以为是build流程中,有哪些编译选项导致编译不通过。从报错的信息来看,有个库文件编译成了PE结构的。Gcc能编译PE结构的中间文件出来?
带着疑问,我翻来覆去地查看了与编译相关的选项和各个dsc、dec文件,以及/conf下的各个配置文件。怎么也找不到问题在哪。
回到报错信息:
Relocatable linking with relocations from format pe-i386 (/home/robin/src/edk2/Build/AppPkg/DEBUG_GCC5/IA32/StdLib/LibC/LibC/OUTPUT/LibC.lib(ftol2.obj)) to format elf32-i386 (/home/robin/src/edk2/Build/AppPkg/DEBUG_GCC5/IA32/AppPkg/Applications/Sockets/SetHostName/SetHostName/DEBUG/SetHostName.dll) is not supported 我仔细读了下LibC.inf,终于找到问题所在了:
我仔细读了下LibC.inf,终于找到问题所在了:
图3 罪魁祸首
Inf的spec文件中,对于[Binaries]字段有详细的说明。这一字段下将不使用平台给定的参数$(MAKE)进行编译,而是使用指定的工具进行镜像生成。
图3中标出的字段,其意义为使用MSFT工具,即微软的Vs Studio对ftol2.boj进行编译。也就是说ftol2.obj是PE结构的,使用dumpbin.exe查看:
图4 dumpbin读取ftol2.obj
这就意味着,AppPkg如果想编译IA32的,只能在Windows下,使用Vs studio编译了。
同时意味着,我之前写的那些代码,只要用到Stdlib库的,都没法编译成IA32架构的了(20200520 robin:现在有64位的模拟器了)。
不过,X64架构还是可以编译的。看来没法在EmulatorPkg的模拟环境下运行程序了(模拟环境似乎只能启动IA32的,无法启动X64),除非我抛弃使用StdLib库。
或者使用Qemu和OvmfPkg,做一个虚拟机环境来测试X64程序,也是种不错的选择。把这个作为下一篇博客的题目吧。
4 构建程序
目标架构为IA32时,看来无法使用StdLib了。因此,只剩下构建入口函数为UefiMain和ShellAppMain两类方式了。
吐槽一下,Linux下的UEFI编程比Windows下更为严格。包括结构体数组的初始化、大小写文件名,以及对未使用变量的判断,都需要重写,否则无法编译通过。
实际上之前的博客代码中,已经有了以UefiMian为入口和ShellAppMain为入口的程序。我把它们找了出来,在Ubuntu的编译环境下做了一些修改,主要是会提示警告的部分去除了。
为方便管理,我编写的代码都放在_LuoApp下:
图5 两类入口函数的示例代码
/Luo3下是UefiMain为入口的示例代码,/ShellMain下是ShellAppMain为入口的示例代码。
编译ShellMian过程:
1) 在ShellPkg.dsc的[Components]中添加一行:LuoApp/ShellMain/ShellMain.inf;
2) 进入UEFI开发目录~/src/edk2,Shell下执行. edksetup.sh;
3) 编译命令: build -m _LuoApp/ShellMain/ShellMain.inf -p ShellPkg/ShellPkg.dsc -a IA32
编译Luo3过程:
1) 在EmulatorPkg.dsc的[Components]中添加一行:_LuoApp/Luo3/Luo3.inf;
2) 进入UEFI开发目录~/src/edk2,Shell下执行. edksetup.sh;|
3) 编译命令: build -m _LuoApp/ Luo3/ Luo3.inf -p EmulatorPkg/EmulatorPkg.dsc -a IA32
编译出来的efi文件,都可以在EmulatorPkg的模拟环境下运行,以Luo3为例:
图6 演示
在编写过程中,发现读写文件的Protocol始终有问题,而同样的代码在Windows 10的UDK2018下编译以及运行时没有问题的。
这正是探索的乐趣:不断发现新的问题,不断解决,乐在其中!
**Gitee地址: gitee.com/luobing4365…
项目代码位于:/ **24 Ubuntu-UEFI EntryMain 下