【UEFI实战】BIOS编译过程中报错“无法解析的外部符号memcpy”
环境
Win10系统。使用VS2022 Community进行编译。
现象
编译报错,报错信息如下:
NMAKE : fatal error U1077: "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\bin\Hostx86\x64\link.exe" /OUT:E:\Gitee\edk2-beni\edk2\Build\OvmfX64\DEBUG_VS2022\X64\ShellPkg\Application\Shell\Shell\DEBUG\Shell.dll.wtemp /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4281 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:_ModuleEntryPoint /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG /ALIGN:4096 /DLL /WX /IGNORE:4210 /WHOLEARCHIVE @E:\Gitee\edk2-beni\edk2\Build\OvmfX64\DEBUG_VS2022\X64\ShellPkg\Application\Shell\Shell\OUTPUT\static_library_files.lst: ش롰0x460
定位
从最后的报错信息看不出来原因,但是再往上翻可以看到:
UefiShellDebug1CommandsLib.lib(BufferImage.obj) : error LNK2001: 无法解析的外部符号 memcpy
UefiShellDebug1CommandsLib
这个模块中并没有使用memcpy
,所以初看这个报错会让人摸不着头脑,相似的还有memset
的报错。
原因在于/LTCG (Link-time code generation) | Microsoft Learn这个功能,在edk2\Conf\tools_def.txt中有相关的配置:
# Linker warning 4210 is disabled globally, because it checks if static initializers are present and the VCRuntime is
# linked. We do not link the VCRuntime (except for HOST_APPLICATIONs) so this warning can be generated erroneously
# whenever there are static initializers, because we will always fail the VCRuntime linking checkDEBUG_VS2022_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4281 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG /ALIGN:4096 /DLL /WX /IGNORE:4210
RELEASE_VS2022_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4281 /IGNORE:4254 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /MERGE:.rdata=.data /ALIGN:4096 /DLL /WX /IGNORE:4210
NOOPT_VS2022_X64_DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4281 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG /ALIGN:4096 /DLL /WX /IGNORE:4210
这是VS提供的一种优化代码的手段,但是如果存在这个配置,VS就可能对某些代码进行优化,最明显的就是那些数据搬运或清零的代码,所以就会使用到memcpy
和memset
。
在关于/LTCG
的说明中可以通过/GL
来关闭:
OFF
(可选)禁用链接时代码生成。 链接器会将使用/GL
编译的所有模块都视为在没有该选项的情况下进行编译,并且任何 MSIL 模块都会导致链接失败。
不过实际上并没有效果,为了解决这个问题,可以通过另外的一个配置/Od
,它用来关闭程序中的所有优化并加快编译速度,没有了优化也就没有了修改成memcpy
这样的操作。
解决
可以通过在dsc(不建议放在这里,因为它会导致全局的无优化)或者inf中增加BuildOptions
来添加该配置:
[BuildOptions]MSFT:*_*_*_CC_FLAGS = /Od
在前面提到的edk2\Conf\tools_def.txt文件中其实也有类似的配置:
NOOPT_VS2022_X64_CC_FLAGS = /nologo /c /WX /GS /W4 /Gs32768 /D UNICODE /Gy /FIAutoGen.h /EHs-c- /GR- /GF /Z7 /Od /volatileMetadata-
但它只针对特定的编译过程,这里的NOOPT
表示的应该就是“No Optimization",没有优化。
后续1
如果想要使用优化,理论上应该有让EDK模块包含外部库的方式,但是目前没有找到。尝试在安装VS2022的时候包含不同的组件(事实上在C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\lib\x64中已经有相关的库了),不过并没有实际的效果。
这个后续还需要研究。
后续2
另外在我们自己写BIOS代码的时候需要注意以下的一些情况,以避免上述的报错:
- 不要将struct等大块的内存作为传参,或者赋值。
- 不要使用大块的数组,且使用类似如下的初始化:
UINT8 Array[100] = {0};