UEFI 笔记 002 —— PrintfLib.h + String OP
声明:个人笔记,概不负责
严谨性声明:行文随意使用 UEFI 或 EDK2。事实上,先有 EDK2 再有 UEFI;抠字眼,EDK2 只是 UEFI 的一种实现;实际上,本人没接触过 EDK2 以外的实现。
UEFI 的 PrintLib 只是 格式化字符串, 并不打印输出.
格式化小语言
https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/PrintLib.h
典型实现
https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BasePrintLib/PrintLib.c
主要函数清单
// MdePkg\Include\Library\PrintLib.h
//
// This function is similar as snprintf_s defined in C11.
UnicodeSPrint(OUT CHAR16 *, IN UINTN, IN const CHAR16 *, ...);
UnicodeSPrintAsciiFormat(OUT CHAR16 *, IN UINTN, IN const CHAR8 *, ...);
AsciiSPrint(OUT CHAR8 *, IN UINTN, IN const CHAR8 *, ...);
AsciiSPrintUnicodeFormat(OUT CHAR8 *, IN UINTN, IN const CHAR16 *, ...);
// Supporting variants
UnicodeVSPrint(OUT CHAR16 *, IN UINTN, IN const CHAR16 *, IN VA_LIST);
UnicodeBSPrint(OUT CHAR16 *, IN UINTN, IN const CHAR16 *, IN BASE_LIST);
UnicodeVSPrintAsciiFormat(OUT CHAR16 *, IN UINTN, IN const CHAR8 *, IN VA_LIST);
UnicodeBSPrintAsciiFormat(OUT CHAR16 *, IN UINTN, IN const CHAR8 *, IN BASE_LIST);
AsciiVSPrint(OUT CHAR8 *, IN UINTN, IN const CHAR8 *, IN VA_LIST);
AsciiBSPrint(OUT CHAR8 *, IN UINTN, IN const CHAR8 *, IN BASE_LIST);
AsciiVSPrintUnicodeFormat(OUT CHAR8 *, IN UINTN, IN const CHAR16 *, IN VA_LIST);
AsciiBSPrintUnicodeFormat(OUT CHAR8 *, IN UINTN, IN const CHAR16 *, IN BASE_LIST);
// Converts a decimal value to a Null-terminated Unicode string.
UnicodeValueToStringS(IN OUT CHAR16 *, IN UINTN, IN UINTN, IN INT64, IN UINTN);
AsciiValueToStringS(IN OUT CHAR8 *, IN UINTN, IN UINTN, IN INT64, IN UINTN);
// Returns the number of characters that would be produced, not including the Null-terminator.
SPrintLength(IN const CHAR16 *, IN VA_LIST);
SPrintLengthAsciiFormat(IN const CHAR8 *, IN VA_LIST);
// MdePkg\Library\BasePrintLib\PrintLib.c
// 有趣的 “隐藏技能” (使用方式 见最后)
//
// MdePkg\Library\BasePrintLib\PrintLibInternal.h
// 将 Value 转换成, 任意基数表达的字符串. (基数 16 以内不会出错)
//
// Internal function that convert a number to a string in Buffer.
BasePrintLibValueToString(IN OUT CHAR8 *Buffer, IN INT64 Value, IN UINTN Radix);
// MdePkg\Include\Library\UefiLib.h
// MdePkg\Library\UefiLib\UefiLibPrint.c
//
// 貌似有意思, ***实际有毛病的***, 加强技能
// ———— 连续使用时, 必须清除 **每个** 中间产生的 String
// 1. 第一次用 NULL 当 String 传入, 产生第一个 结果;
// 2. 之后, 把返回的结果 继续喂入, 继续 Cat // 应该 FreePool ()
// 3. 以此类推 // 应该 FreePool ()
// 4. caller 需要 FreePool () 最后返回的 String
//
// 事实上, EDK2 自己 在 2-3 这里, 错的一塌糊涂!
// 典型错误 in EDK2
// ShellPkg\Library\UefiHandleParsingLib\UefiHandleParsingLib.c
// LoadedImageProtocolDumpInformation ()
// RetVal = CatSPrint (
// RetVal,
//
// 结论:这个 CatSPrint () 的本意是连续 Cat , 流畅使用
// 但这个 脑子不清楚 的实现, 全给搞砸了
//
// Appends a formatted Unicode string to a Null-terminated Unicode string
// The caller is responsible for freeing the returned string.
//
CatSPrint(IN CHAR16 *String, IN const CHAR16 *FormatString, ...);
CatVSPrint(IN CHAR16 *String, IN const CHAR16 *FormatString, IN VA_LIST);
// ShellPkg\Include\Library\ShellLib.h
// ShellPkg\Library\UefiShellLib\UefiShellLib.c
//
// 用法与 CatSPrint () 有得一拼, 但有点 傻了吧唧的实现 , 倒是没啥毛病.
// 内部用 StrSize () 算出当前尾巴, 然后用 StrnCatS () 进行 Cat // 好傻
// 若是 *CurrentSize == 0 则, 自动分配内存
// 若是 Size 不够,就会 ReallocatePool (), 不会错用内存泄漏 // 好傻, 实际每次都会 Reallocate
// caller 只要 FreePool () 最后的 String 就行 // 好棒!
//
StrnCatGrow(IN OUT CHAR16 **Destination, IN OUT UINTN *CurrentSize, IN const CHAR16 *Source, IN UINTN Count);
ShellCopySearchAndReplace(IN CHAR16 const *, IN OUT CHAR16 *, IN UINTN, IN const CHAR16 *, IN const CHAR16 *, IN const BOOLEAN, IN const BOOLEAN);
ShellStrToUintn(IN const CHAR16 *);
ShellHexStrToUintn(IN const CHAR16 *);
ShellIsHexaDecimalDigitCharacter(IN CHAR16);
ShellIsDecimalDigitCharacter(IN CHAR16);
ShellIsHexOrDecimalNumber(IN const CHAR16 *, IN const BOOLEAN, IN const BOOLEAN);
ShellConvertStringToUint64(IN const CHAR16 *, OUT UINT64 *, IN const BOOLEAN, IN const BOOLEAN);
// MdePkg\Include\Library\BaseLib.h
StrnLenS(IN const CHAR16 *, IN UINTN)
StrnSizeS(IN const CHAR16 *, IN UINTN)
StrCpyS(OUT CHAR16 *, IN UINTN, IN const CHAR16 *)
StrnCpyS(OUT CHAR16 *, IN UINTN, IN const CHAR16 *, IN UINTN)
StrCatS(IN OUT CHAR16 *, IN UINTN, IN const CHAR16 *)
StrnCatS(IN OUT CHAR16 *, IN UINTN, IN const CHAR16 *, IN UINTN)
StrDecimalToUintnS(IN const CHAR16 *, OUT CHAR16 **EndPointer, OUT UINTN *)
StrDecimalToUint64S(IN const CHAR16 *, OUT CHAR16 **EndPointer, OUT UINT64 *)
StrHexToUintnS(IN const CHAR16 *, OUT CHAR16 **EndPointer, OUT UINTN *)
StrHexToUint64S(IN const CHAR16 *, OUT CHAR16 **EndPointer, OUT UINT64 *)
AsciiStrnLenS(IN const CHAR8 *, IN UINTN)
AsciiStrnSizeS(IN const CHAR8 *, IN UINTN)
AsciiStrCpyS(OUT CHAR8 *, IN UINTN, IN const CHAR8 *)
AsciiStrnCpyS(OUT CHAR8 *, IN UINTN, IN const CHAR8 *, IN UINTN)
AsciiStrCatS(IN OUT CHAR8 *, IN UINTN, IN const CHAR8 *)
AsciiStrnCatS(IN OUT CHAR8 *, IN UINTN, IN const CHAR8 *, IN UINTN)
AsciiStrDecimalToUintnS(IN const CHAR8 *, OUT CHAR8 **EndPointer, OUT UINTN *)
AsciiStrDecimalToUint64S(IN const CHAR8 *, OUT CHAR8 **EndPointer, OUT UINT64 *)
AsciiStrHexToUintnS(IN const CHAR8 *, OUT CHAR8 **EndPointer, OUT UINTN *)
AsciiStrHexToUint64S(IN const CHAR8 *, OUT CHAR8 **EndPointer, OUT UINT64 *)
StrLen(IN const CHAR16 *)
StrSize(IN const CHAR16 *)
StrCmp(IN const CHAR16 *, IN const CHAR16 *)
StrnCmp(IN const CHAR16 *, IN const CHAR16 *, IN UINTN)
StrStr(IN const CHAR16 *, IN const CHAR16 *)
StrDecimalToUintn(IN const CHAR16 *)
StrDecimalToUint64(IN const CHAR16 *)
StrHexToUintn(IN const CHAR16 *)
StrHexToUint64(IN const CHAR16 *)
StrToIpv6Address(IN const CHAR16 *, OUT CHAR16 **EndPointer, OUT IPv6_ADDRESS *, OUT UINT8 *PrefixLength)
StrToIpv4Address(IN const CHAR16 *, OUT CHAR16 **EndPointer, OUT IPv4_ADDRESS *, OUT UINT8 *PrefixLength)
StrToGuid(IN const CHAR16 *, OUT GUID *)
StrHexToBytes(IN const CHAR16 *, IN UINTN, OUT UINT8 *, IN UINTN)
UnicodeStrToAsciiStrS(IN const CHAR16 *, OUT CHAR8 *, IN UINTN)
UnicodeStrnToAsciiStrS(IN const CHAR16 *, IN UINTN, OUT CHAR8 *, IN UINTN, OUT UINTN *)
AsciiStrLen(IN const CHAR8 *)
AsciiStrSize(IN const CHAR8 *)
AsciiStrCmp(IN const CHAR8 *, IN const CHAR8 *)
AsciiStriCmp(IN const CHAR8 *, IN const CHAR8 *)
AsciiStrnCmp(IN const CHAR8 *, IN const CHAR8 *, IN UINTN)
AsciiStrStr(IN const CHAR8 *, IN const CHAR8 *)
AsciiStrDecimalToUintn(IN const CHAR8 *)
AsciiStrDecimalToUint64(IN const CHAR8 *)
AsciiStrHexToUintn(IN const CHAR8 *)
AsciiStrHexToUint64(IN const CHAR8 *)
AsciiStrToIpv6Address(IN const CHAR8 *, OUT CHAR8 **EndPointer, OUT IPv6_ADDRESS *, OUT UINT8 *PrefixLength)
AsciiStrToIpv4Address(IN const CHAR8 *, OUT CHAR8 **EndPointer, OUT IPv4_ADDRESS *, OUT UINT8 *PrefixLength)
AsciiStrToGuid(IN const CHAR8 *, OUT GUID *)
AsciiStrHexToBytes(IN const CHAR8 *, IN UINTN, OUT UINT8 *, IN UINTN)
AsciiStrToUnicodeStrS(IN const CHAR8 *, OUT CHAR16 *, IN UINTN)
AsciiStrnToUnicodeStrS(IN const CHAR8 *, IN UINTN, OUT CHAR16 *, IN UINTN, OUT UINTN *)
CharToUpper(IN CHAR16)
AsciiCharToUpper(IN CHAR8)
Base64Encode(IN const UINT8 *, IN UINTN, OUT CHAR8 *Destination, IN OUT UINTN *)
Base64Decode(IN const CHAR8 *Source, IN UINTN, OUT UINT8 *Destination, IN OUT UINTN *)
DecimalToBcd8(IN UINT8)
BcdToDecimal8(IN UINT8)
使用 隐藏技能:
- Copy function declaration to your C file, just build it.
- The linker will actually resolve the symbol - found the func impl from lib.
Why?
The function declaration is for compiling.
The linker will find the impl of the func.
The LibraryClass
of EDK2 building system is actually based-on C library.
Why++ ?
(1) If a function is not exposed by public header file of the LibraryClass
, we can use it by declaring it by ourselves.
(2) To prevent such dirty use for LibraryClass
, specify STATIC
on your function impl.
函数清单获取方式:安装 VSCode list-symbols 插件, 截取头文件,手工清理.
see also