BIOS/UEFI基础——StdLib的编译和使用
StdLib简要说明
UEFI的源代码中有一个Pacakge实现了UEFI下的标准C库,如下所示:
里面有很多C语言的基础函数,比如有stdio.h:
使用StdLib库可以方便对Linux等的代码的快速移植,也方便从其他C语言领域到UEFI开发的转移。
本文接下去介绍如何在UEFI下使用StdLib。
代码以https://gitee.com/jiangwei0512/vUDK2017中的为例。
StdLib的编译
StdLib有独立的Package,可以独立编译。
本文使用的编译器是VS2015。
编译的步骤如下:
1. 运行edksetup.bat:
D:\Codes\vUDK2017>edksetup.bat
PATH = D:\Codes\vUDK2017\BaseTools\Bin\Win32;C:\Program Files (x86)\Razer Chroma SDK\bin;C:\Program Files\Razer Chroma SDK\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Git\cmd;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;D:\Program Files\TortoiseGit\bin;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin;;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps;C:\Python37\Scripts\;C:\Python37\;C:\Users\HOME\AppData\Local\Microsoft\WindowsApps;D:\Program Files\Microsoft VS Code\bin;C:\Python27;C:\Python36;C:\Nasm;D:\Program Files\qemu;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin;
WORKSPACE = D:\Codes\vUDK2017
EDK_TOOLS_PATH = D:\Codes\vUDK2017\BaseTools
BASE_TOOLS_PATH = D:\Codes\vUDK2017\BaseTools
EDK_TOOLS_BIN = D:\Codes\vUDK2017\BaseTools\Bin\Win32
CONF_PATH = D:\Codes\vUDK2017\Conf
!!! WARNING !!! No CYGWIN_HOME set, gcc build may not be used !!!
D:\Codes\vUDK2017>
2. 执行build命令:
D:\Codes\vUDK2017>build -a X64 -p StdLib\StdLib.dsc -t VS2015x86
但是发现执行命令时报错了:
Processing meta-data ... done!
Building ... d:\codes\vudk2017\StdLib\LibC\LibC.inf [X64]
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\x86_amd64\cl.exe" /Fod:\codes\vudk2017\Build\StdLib\DEBUG_VS2015x86\X64\StdLib\LibC\LibC\OUTPUT\Main\errno.obj /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2s /GL /Gy /FIAutoGen.h /EHs-c- /GR- /GF /Zi /Gm /X /Zc:wchar_t /D UEFI_C_SOURCE /Wv:11 /Id:\codes\vudk2017\StdLib\LibC\Main\X64 /Id:\codes\vudk2017\StdLib\LibC\Main /Id:\codes\vudk2017\StdLib\LibC /Id:\codes\vudk2017\Build\StdLib\DEBUG_VS2015x86\X64\StdLib\LibC\LibC\DEBUG /Id:\codes\vudk2017\StdLib /Id:\codes\vudk2017\StdLib\Include /Id:\codes\vudk2017\StdLib\Include\X64 /Id:\codes\vudk2017\StdLibPrivateInternalFiles /Id:\codes\vudk2017\StdLibPrivateInternalFiles\Include /Id:\codes\vudk2017\StdLibPrivateInternalFiles\Include\X64 /Id:\codes\vudk2017\MdePkg /Id:\codes\vudk2017\MdePkg\Include /Id:\codes\vudk2017\MdePkg\Include\X64 /Id:\codes\vudk2017\ShellPkg /Id:\codes\vudk2017\ShellPkg\Include d:\codes\vudk2017\StdLib\LibC\Main\errno.c
errno.c
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\x86_amd64\cl.exe" /Fod:\codes\vudk2017\Build\StdLib\DEBUG_VS2015x86\X64\StdLib\LibC\LibC\OUTPUT\Main\assert.obj /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2s /GL /Gy /FIAutoGen.h /EHs-c- /GR- /GF /Zi /Gm /X /Zc:wchar_t /D UEFI_C_SOURCE /Wv:11 /Id:\codes\vudk2017\StdLib\LibC\Main\X64 /Id:\codes\vudk2017\StdLib\LibC\Main /Id:\codes\vudk2017\StdLib\LibC /Id:\codes\vudk2017\Build\StdLib\DEBUG_VS2015x86\X64\StdLib\LibC\LibC\DEBUG /Id:\codes\vudk2017\StdLib /Id:\codes\vudk2017\StdLib\Include /Id:\codes\vudk2017\StdLib\Include\X64 /Id:\codes\vudk2017\StdLibPrivateInternalFiles /Id:\codes\vudk2017\StdLibPrivateInternalFiles\Include /Id:\codes\vudk2017\StdLibPrivateInternalFiles\Include\X64 /Id:\codes\vudk2017\MdePkg /Id:\codes\vudk2017\MdePkg\Include /Id:\codes\vudk2017\MdePkg\Include\X64 /Id:\codes\vudk2017\ShellPkg /Id:\codes\vudk2017\ShellPkg\Include d:\codes\vudk2017\StdLib\LibC\Main\assert.c
assert.c
d:\codes\vudk2017\StdLib\Include\sys/EfiCdefs.h(342): error C2220: 警告被视为错误 - 没有生成“object”文件
d:\codes\vudk2017\StdLib\Include\sys/EfiCdefs.h(342): warning C4117: 保留“__STDC_HOSTED__”宏名,忽略“#define”
NMAKE : fatal error U1077: “"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\x86_amd64\cl.exe"”: 返回代码“0x2”
Stop.
从说明中可以看出,报错的原因是宏重定义了,这是一个warning(编号是4117),由于EDK编译时默认设置了警告视为错误,所以这里就报错了。
我们可以查看出错代码:
// Keep compiler quiet about casting from smaller to larger types
#pragma warning ( disable : 4306 )
#define __STDC__ 1
#define __STDC_VERSION__ 199409L
#define __STDC_HOSTED__ 1
所以意思是这里的__STDC_HOSTED__有重定义,所以这里可以做如下的修改:
// Keep compiler quiet about casting from smaller to larger types
#pragma warning ( disable : 4306 )
#define __STDC__ 1
#define __STDC_VERSION__ 199409L
#ifndef __STDC_HOSTED__
#define __STDC_HOSTED__ 1
#endif
这样的话编译就OK了。
这种方法理论上是OK的,但是有一个疑问,即默认情况下__STDC_HOSTED__的值是1吗?如果不是1的话,显然跟我们需要定义的值不一致。
这边没有找到这个宏到底是在哪个文件,不过可以在网上搜到相关的说明(https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=vs-2015):
Standard predefined macros
The compiler supports these predefined macros specified by the ISO C99 and ISO C++17 standards.
-
__cplusplus Defined as an integer literal value when the translation unit is compiled as C++. Otherwise, undefined.
-
__DATE__ The compilation date of the current source file. The date is a constant length string literal of the form Mmm dd yyyy. The month name Mmm is the same as the abbreviated month name in dates generated by the C Runtime Library asctime function. The first character of date dd is a space if the value is less than 10. This macro is always defined.
-
__FILE__ The name of the current source file. __FILE__ expands to a character string literal. To ensure that the full path to the file is displayed, use /FC (Full Path of Source Code File in Diagnostics). This macro is always defined.
-
__LINE__ Defined as the integer line number in the current source file. The value of the __LINE__ macro can be changed by using a
#line
directive. This macro is always defined. -
__STDC__ Defined as 1 only when compiled as C and if the /Za compiler option is specified. Otherwise, undefined.
-
__STDC_HOSTED__ Defined as 1 if the implementation is a hosted implementation, one that supports the entire required standard library. Otherwise, defined as 0.
-
__STDCPP_THREADS__ Defined as 1 if and only if a program can have more than one thread of execution, and compiled as C++. Otherwise, undefined.
-
__TIME__ The time of translation of the preprocessed translation unit. The time is a character string literal of the form hh:mm:ss, the same as the time returned by the C Runtime Library asctime function. This macro is always defined.
可见这个宏的值不是0,就是1,所以上述的代码修改是没有问题的。
我们也可以在VS2015下实际编写一个程序来确定:
#include "stdafx.h"
int main()
{
printf("hello world %d\n", __STDC_HOSTED__);
while (1);
return 0;
}
执行的结果如下:
当然上述的问题还有其它的解决方法:
1. 在代码中使用#pragma来屏蔽这个警告:
// Keep compiler quiet about casting from smaller to larger types
#pragma warning ( disable : 4306 )
#pragma warning ( disable : 4117 )
#define __STDC__ 1
#define __STDC_VERSION__ 199409L
#define __STDC_HOSTED__ 1
2. 在DSC中添加宏控制来屏蔽这个警告:
[Defines]
PLATFORM_NAME = StdLib
PLATFORM_GUID = 6135e67b-d813-4e4a-93c3-945d6af41858
PLATFORM_VERSION = 0.01
DSC_SPECIFICATION = 0x00010006
OUTPUT_DIRECTORY = Build/StdLib
SUPPORTED_ARCHITECTURES = IA32|X64|ARM|AARCH64
BUILD_TARGETS = DEBUG|RELEASE|NOOPT
SKUID_IDENTIFIER = DEFAULT
[BuildOptions]
MSFT:*_*_*_CC_FLAGS = /wd4117
新加了宏之后,可以看到编译时有体现:
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\x86_amd64\cl.exe" /Fod:\codes\vudk2017\Build\StdLib\DEBUG_VS2015x86\X64\StdLib\SocketDxe\SocketDxe\OUTPUT\.\AutoGen.obj /nologo /c /WX /GS- /W4 /Gs32768 /D UNICODE /O1b2s /GL /Gy /FIAutoGen.h /EHs-c- /GR- /GF /Zi /Gm /wd4117 /X /Zc:wchar_t /D UEFI_C_SOURCE /Wv:11 /Id:\codes\vudk2017\StdLib\SocketDxe /Id:\codes\vudk2017\Build\StdLib\DEBUG_VS2015x86\X64\StdLib\SocketDxe\SocketDxe\DEBUG /Id:\codes\vudk2017\MdePkg /Id:\codes\vudk2017\MdePkg\Include /Id:\codes\vudk2017\MdePkg\Include\X64 /Id:\codes\vudk2017\MdeModulePkg /Id:\codes\vudk2017\MdeModulePkg\Include /Id:\codes\vudk2017\StdLib /Id:\codes\vudk2017\StdLib\Include /Id:\codes\vudk2017\StdLib\Include\X64 d:\codes\vudk2017\Build\StdLib\DEBUG_VS2015x86\X64\StdLib\SocketDxe\SocketDxe\DEBUG\AutoGen.c
AutoGen.c
以上的三种方式都能够解决编译的问题。
StdLib的使用
一般StdLib单独编译没有多大的意义,重要的是将它编译到其它的实用模块中。
比如有AppPkg:
在AppPkg.dsc中可以看到对StdLib的包含:
##############################################################################
#
# Include Boilerplate text required for building with the Standard Libraries.
#
##############################################################################
!include StdLib/StdLib.inc
对于StdLib的使用似乎也没有什么好多讲的,就是包含头文件之类的。
后续如果使用到再深入。
以上。