UEFI开发探索92 – 调试使用了StdLib的64位程序

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

昨天有位名为“sam bing”的网友,提到用VS2015和EmulatorPkg调试UEFI程序。调试方法在之前的博客中讨论过,他是参照博客去操作的。

问题在于,他发现在工程中使用C标准库函数,也即StdLib后,在EmulatorPkg中无法编译通过,也就无法通过EmulatorPkg进行64位C标准库工程的调试了。

这个问题我也觉得奇怪,理论上不应该啊。

上午处理完项目相关的事情后,正好有点时间,就着手进行这个问题的分析了。

1 搭建开发环境

我日常都是在vUDK2018下进行开发,主要是因为它还支持Nt32Pkg,方便我随时使用模拟器进行调试。

为了分析这个问题,我得用一个相对较新的版本来测试。正好前段时间整理书稿的时候,搭建了一个开发环境,看了下版本:

robin@DESKTOP-083AISO:/mnt/c/srcEDKx/edk2$ git log
commit ca407c7246bf405da6d9b1b9d93e5e7f17b4b1f9 (HEAD, tag: edk2-stable202005)
Author: Ard Biesheuvel <ard.biesheuvel@arm.com>
Date:   Wed May 20 13:44:48 2020 +0200

ArmPkg/CompilerIntrinsicsLib: provide atomics intrinsics
…..//后略

robin@DESKTOP-083AISO:/mnt/c/srcEDKx/edk2$ ls
AppPkg          DynamicTablesPkg     License.txt      RobinPkg             StdLibPrivateInternalFiles
ArmPkg          EmbeddedPkg          Maintainers.txt  RobinPkg1            UefiCpuPkg
ArmPlatformPkg  EmulatorPkg          MdeModulePkg     SecurityPkg          UefiPayloadPkg
ArmVirtPkg      FatPkg               MdePkg           ShellPkg             UnitTestFrameworkPkg
BaseTools       FmpDevicePkg         NetworkPkg       SignedCapsulePkg     edksetup.bat
Build           IntelFsp2Pkg         OvmfPkg          SourceLevelDebugPkg  edksetup.sh
Conf            IntelFsp2WrapperPkg  PcAtChipsetPkg   StandaloneMmPkg      pip-requirements.txt
CryptoPkg       License-History.txt  ReadMe.rst       StdLib

使用的是2020年的5月版,为了方便使用,我把StdLib、AppPkg等包,以及我自己的包RobinPkg一股脑都放在了edk2的同一目录下了。

我是在WSL的Ubuntu下git开发所需要的源代码的,实际开发是在Windows环境下进行的。

2 测试

我是以之前写的EchoTcp4工程作为实验材料的,它是一个使用了StdLib的网络测试程序。具体步骤如下:

2.1 调试工程建立

建立调试工程主要包括三个工作:

  1. 编译64位的EmulatorPkg,生成模拟器供后续使用;
  2. 建立VS的调试工程,使得可以使用VS编译和调试代码;
  3. 修改EmulatorPkg.dsc文件,增加对StdLib库的支持。

编译64位EmulatorPkg相对简单,直接按如下命令编译即可:

C:\srcEDKx\edk2>build -p EmulatorPkg\EmulatorPkg.dsc -a X64

而VS2015调试用工程的建立,在之前的博客中已经有详细描述了,贴一下编译部分的设置(*.vcxproj文件):

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <NMakeBuildCommandLine>
		cd /D C:\srcEDKx\edk2
		set WORKSPACE=C:\srcEDKx\edk2
		call edksetup.bat
		call build.bat -p EmulatorPkg\EmulatorPkg.dsc -a X64 -m RobinPkg\Applications\EchoTcp4\EchoTcp4.inf -b DEBUG
   </NMakeBuildCommandLine>

从内容中可以看到”Debug|Win32”字样,说明是针对32位平台修改的编译命令因此,即便是编译64位程序,也要注意Soulution Platform中,选择Debug和x86进行编译。

然后,在EmulatorPkg.dsc中,添加需要编译的EchoTcp4工程。内容如下:

 ……
  RobinPkg/Applications/EchoTcp4/EchoTcp4.inf  #需要调试的工程
  MdeModulePkg/Application/HelloWorld/HelloWorld.inf

  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
……

并在DSC文件中添加对StdLib库的支持:

!include NetworkPkg/Network.dsc.inc
 DEFINE EMULATE  #robin 20210527
 !include StdLib/StdLib.inc  #robin 20210527
[BuildOptions]
  #
  # Disable deprecated APIs.
  #
   *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES 

  MSFT:DEBUG_*_*_CC_FLAGS = /Od /Oy-
  MSFT:NOOPT_*_*_CC_FLAGS = /Od /Oy-
......

至此,完成了调试工程的建立。

2.2 编译和调试

打开VS工程文件进行调试之前,要先试着编译下,看是否能成功。

C:\srcEDKx\edk2>build -p EmulatorPkg\EmulatorPkg.dsc -m RobinPkg\Applications\EchoTcp4\EchoTcp4.inf -a X64
......//略
cl : Command line warning D9025 : overriding '/O1' with '/Od'
DevGenisis.c
Building ... c:\srcedkx\edk2\StdLib\LibC\Locale\Locale.inf [X64]
StubFunctions.c
strtoimax.c
Searching.c
DevSearch.c
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\x86_amd64\cl.exe"' : return code '0x2'
Stop.

build.py...
 : error 7000: Failed to execute command
        C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\nmake.exe /nologo tbuild [c:\srcedkx\edk2\Build\EmulatorX64\DEBUG_VS2015x86\X64\StdLib\LibC\Wchar\Wchar]
build.py...
 : error 7000: Failed to execute command
        C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\nmake.exe /nologo tbuild [c:\srcedkx\edk2\Build\EmulatorX64\DEBUG_VS2015x86\X64\StdLib\LibC\Uefi\Devices\daUtility]
build.py...
 : error 7000: Failed to execute command
        C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\nmake.exe /nologo tbuild [c:\srcedkx\edk2\Build\EmulatorX64\DEBUG_VS2015x86\X64\StdLib\LibC\Uefi\Uefi]
build.py...
 : error 7000: Failed to execute command
        C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\nmake.exe /nologo tbuild [c:\srcedkx\edk2\Build\EmulatorX64\DEBUG_VS2015x86\X64\StdLib\LibC\String\String]
build.py...
 : error 7000: Failed to execute command
        C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\nmake.exe /nologo tbuild [c:\srcedkx\edk2\Build\EmulatorX64\DEBUG_VS2015x86\X64\StdLib\LibC\StdLib\StdLib]
build.py...
 : error F002: Failed to build module
        c:\srcedkx\edk2\StdLib\LibC\Wchar\Wchar.inf [X64, VS2015x86, DEBUG]
- Failed -
Build end time: 13:11:31, May.27 2021
Build total time: 00:00:08

总而言之,一堆的错误,无法编译StdLib的库,和咨询我的网友遇到的问题一样。

3 解决问题

遇到这种现象,我很疑惑,特别奇怪的是,编译选项提示从/O1变成了/OD。

整个EDK2的编译逻辑很复杂,短时期内去完全搞清,不大现实。

在网上看了下几个论坛,看到大神Tim Lewis(《Harnessing the UEFI》作者,Insyde的CTO)的一篇回复:

https://edk2.groups.io/g/discuss/topic/winhost_exe_from_emulatorpkg/32596316?p=,,,20,0,0,0::recentpostdate%2Fsticky,,,20,1,0,32596316

说到了类似的问题。

他怀疑是编译依赖的问题,而且按他的方法,可以编译通过。不过,他把EmulatorPkg.dsc中定义StdLib的部分,移到了最后,才能编译通过。

我并没有这么做,只是把***_CC_FLAGS的定义注释掉了,如下:

!include NetworkPkg/Network.dsc.inc
DEFINE EMULATE  #robin 20210527
!include StdLib/StdLib.inc  #robin 20210527
[BuildOptions]
  #
  # Disable deprecated APIs.
  #
  # *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES # robin 20210527

  MSFT:DEBUG_*_*_CC_FLAGS = /Od /Oy-
  MSFT:NOOPT_*_*_CC_FLAGS = /Od /Oy-
  GCC:DEBUG_CLANGPDB_*_CC_FLAGS =-O0 -Wno-unused-command-line-argument -Wno-incompatible-pointer-types -Wno-enum-conversion -Wno-incompatible-pointer-types -Wno-sometimes-uninitialized -Wno-constant-conversion -Wno-main-return-type

  MSFT:*_*_*_DLINK_FLAGS     = /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE
  MSFT:DEBUG_*_*_DLINK_FLAGS = /EXPORT:InitializeDriver=$(IMAGE_ENTRY_POINT) /BASE:0x10000
  MSFT:NOOPT_*_*_DLINK_FLAGS = /EXPORT:InitializeDriver=$(IMAGE_ENTRY_POINT) /BASE:0x10000

!if $(WIN_HOST_BUILD) == TRUE
  #
  # CLANGPDB tool chain depends on WIN_HOST_BUILD flag to generate the windows application.
  #
  GCC:*_CLANGPDB_*_DLINK_FLAGS     = /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE
  GCC:DEBUG_CLANGPDB_*_DLINK_FLAGS = /EXPORT:InitializeDriver=$(IMAGE_ENTRY_POINT) /BASE:0x10000
  GCC:NOOPT_CLANGPDB_*_DLINK_FLAGS = /EXPORT:InitializeDriver=$(IMAGE_ENTRY_POINT) /BASE:0x10000
!endif

再次运行本篇2.2节的编译命令,编译通过!

看来是这个宏定义导致的问题。

搜寻了下EDK2的整体文件,很多地方都用到了它。从含义上看,是禁止了某些接口。也有函数注释中提到,为了安全原因,某些函数会被禁止使用。

总之,现在已经可以调试了。打开调试工程,按F5启动调试,在需要的语句处下断点,就可以轻松开始调试的旅程了:

图1 调试使用了StdLib的应用

3,693 total views, 1 views today

发表评论

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