前面的文章“EFI 文件研究(1)”提到了入口地方有一个奇怪的现象,直接从ProcessLibraryConstructorList 函数跳到了ProcessModuleEntryPointList。百思不得其解之后咨询天杀,他提到有编译器有一种优化方式,针对“连续两个函数的调用 ,在优化后可能会将第一个函数的尾部返回优化成对第二个函数的跳转,然后由第二个函数来进行返回”。经过这样的提醒后,我在代码中查找,找到了非常类似的代码。
在\MdePkg\Library\UefiApplicationEntryPoint\ApplicationEntryPoint.c 定义了Application 的入口:
/**
Entry point to UEFI Application.
This function is the entry point for a UEFI Application. This function must call
ProcessLibraryConstructorList(), ProcessModuleEntryPointList(), and ProcessLibraryDestructorList().
The return value from ProcessModuleEntryPointList() is returned.
If _gUefiDriverRevision is not zero and SystemTable->Hdr.Revision is less than _gUefiDriverRevison,
then return EFI_INCOMPATIBLE_VERSION.
@param ImageHandle The image handle of the UEFI Application.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The UEFI Application exited normally.
@retval EFI_INCOMPATIBLE_VERSION _gUefiDriverRevision is greater than SystemTable->Hdr.Revision.
@retval Other Return value from ProcessModuleEntryPointList().
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
if (_gUefiDriverRevision != 0) {
//
// Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application.
//
if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
return EFI_INCOMPATIBLE_VERSION;
}
}
//
// Call constructor for all libraries.
//
ProcessLibraryConstructorList (ImageHandle, SystemTable);
//
// Call the module's entry point
//
Status = ProcessModuleEntryPointList (ImageHandle, SystemTable);
//
// Process destructor for all libraries.
//
ProcessLibraryDestructorList (ImageHandle, SystemTable);
//
// Return the return status code from the driver entry point
//
return Status;
}
因此,前面_gUefiDriverRevision == 0 在编译期内部代码直接会被优化掉,剩下的就是连续两次调用ProcessLibraryConstructorList 和ProcessModuleEntryPointList 函数。
为了证明这一点,我在 Inf 文件中加入关闭优化的指令 /Od:
[BuildOptions]
MSFT:*_*_X64_CC_FLAGS = /FAsc /Od
加入之后再次编译,
在 \Build\AppPkg\DEBUG_VS2015x86\X64\AppPkg\Applications\EFIStudy\EFIStudy\ApplicationEntryPoint.cod 中可以看到完整的图景:
_ModuleEntryPoint PROC ; COMDAT
; 45 : {
$LN25:
00000 48 89 5c 24 08 mov QWORD PTR [rsp+8], rbx
00005 48 89 74 24 10 mov QWORD PTR [rsp+16], rsi
0000a 57 push rdi
0000b 48 83 ec 20 sub rsp, 32 ; 00000020H
0000f 48 8b fa mov rdi, rdx
00012 48 8b f1 mov rsi, rcx
; 46 : EFI_STATUS Status;
; 47 :
; 48 : if (_gUefiDriverRevision != 0) {
; 49 : //
; 50 : // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application.
; 51 : //
; 52 : if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
; 53 : return EFI_INCOMPATIBLE_VERSION;
; 54 : }
; 55 : }
; 56 :
; 57 : //
; 58 : // Call constructor for all libraries.
; 59 : //
; 60 : ProcessLibraryConstructorList (ImageHandle, SystemTable);
00015 e8 00 00 00 00 call ProcessLibraryConstructorList
; 61 :
; 62 : //
; 63 : // Call the module's entry point
; 64 : //
; 65 : Status = ProcessModuleEntryPointList (ImageHandle, SystemTable);
0001a 48 8b d7 mov rdx, rdi
0001d 48 8b ce mov rcx, rsi
00020 e8 00 00 00 00 call ProcessModuleEntryPointList
; 66 :
; 67 : //
; 68 : // Process destructor for all libraries.
; 69 : //
; 70 : ProcessLibraryDestructorList (ImageHandle, SystemTable);
00025 48 8b d7 mov rdx, rdi
00028 48 8b ce mov rcx, rsi
0002b 48 8b d8 mov rbx, rax
0002e e8 00 00 00 00 call ProcessLibraryDestructorList
; 71 :
; 72 : //
; 73 : // Return the return status code from the driver entry point
; 74 : //
; 75 : return Status;
; 76 : }
00033 48 8b 74 24 38 mov rsi, QWORD PTR [rsp+56]
00038 48 8b c3 mov rax, rbx
0003b 48 8b 5c 24 30 mov rbx, QWORD PTR [rsp+48]
00040 48 83 c4 20 add rsp, 32 ; 00000020H
00044 5f pop rdi
00045 c3 ret 0
_ModuleEntryPoint ENDP
这里可以清楚的看到分别调用了2个函数,并且和我们上面找到的位置代码是一致的。
从上面的试验可以得知:
- _ModuleEntryPoint ()是每个 Application 的起点,如果有需要可以在其中添加代码;
- 编译器有时候会将连续的2个函数调用优化。