UEFI开发探索38 – Ubuntu下编译AppPkg杂谈

上一篇博客中,在编译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下载地址:

https://github.com/tianocore/tianocore.github.io/wiki/EDKII-EADK
https://github.com/tianocore/edk2-libc

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文件中包括:
1) EDK II Module INF Files
2) EDK Components
3) EDK Libraries (only used by EDK Components)
4) EDK II Library Class Instance Mappings (only used by EDK II Modules)
5) 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架构的了。

不过,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下编译以及运行时没有问题的。

这正是探索的乐趣:不断发现新的问题,不断解决,乐在其中!

(原文地址: http://yiiyee.cn/blog/author/luobing/ )

百度云链接:https://pan.baidu.com/s/1gccSosw8_UAGTI5gZPnLCA
提取码:dx23
文件在 24 Ubuntu-UEFI EntryMain 下

202 total views, 1 views today

发表评论

电子邮件地址不会被公开。