Step to UEFI (22) ----- Application的入口分析

研究一下:UEFI APP 在编译的时候会加入什么头。

使用上一次的示例程序 ClrTest。稍微修改一下,去掉清屏的调用以便我们能看清结果:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/UefiApplicationEntryPoint.h>

extern EFI_BOOT_SERVICES             	 *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 		 *gRT;

//
// Entry point function 
//
EFI_STATUS
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   Print(L"www.lab-z.com\n");
   return EFI_SUCCESS;
}

编译命令是 build -p AppPkg\AppPkg.dsc

首先查看生成的 Makefile

在 \Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\Makefile

下面的语句指定了入口函数

DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP
/ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL
/ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO
/BASE:0 /DRIVER /DEBUG

同样的 Makefile中,给出入口函数

IMAGE_ENTRY_POINT = _ModuleEntryPoint

顺便看一眼 \Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\OUTPUT\ClsTest.map

__ModuleEntryPoint 00000260 f UefiApplicationEntryPoint:ApplicationEntryPoint.obj

就是说 ModuleEntryPoint 是从 ApplicationEntryPoint.obj 中链接进来的

这个函数的头文件在 \MdePkg\Include\Library\UefiApplicationEntryPoint.h

再进一步查找 \MdePkg\Library\UefiApplicationEntryPoint\UefiApplicationEntryPoint.inf 其中给出了对应的函数体的位置

[Sources]
ApplicationEntryPoint.c

打开看看 \MdePkg\Library\UefiApplicationEntryPoint\ApplicationEntryPoint.c

EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
...............
//
// 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);

其中调用了4个函数,下面分别按图索骥

  1. ProcessLibraryConstructorList 函数,他在

\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\DEBUG\AutoGen.c

追进去,看一下

VOID
EFIAPI
ProcessLibraryConstructorList (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);
ASSERT_EFI_ERROR (Status);
Status = UefiRuntimeServicesTableLibConstructor (ImageHandle,SystemTable);
ASSERT_EFI_ERROR (Status);
Status = UefiLibConstructor (ImageHandle, SystemTable);
ASSERT_EFI_ERROR (Status);
}

 

1.1 追一下UefiBootServicesTableLibConstructor  发现它在\MdePkg\Library\UefiBootServicesTableLib\UefiBootServicesTableLib.c

EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Cache the Image Handle
//
gImageHandle = ImageHandle;  //看到这里也能明白之前文章中Extern 的gImageHandle哪里来了
ASSERT (gImageHandle != NULL);
//
// Cache pointer to the EFI System Table
//
gST = SystemTable;
ASSERT (gST != NULL);

//
// Cache pointer to the EFI Boot Services Table
//
gBS = SystemTable->BootServices;
ASSERT (gBS != NULL);

return EFI_SUCCESS;
}

 

1.2   再看看UefiRuntimeServicesTableLibConstructor 在 \MdePkg\Library\UefiRuntimeServicesTableLib\UefiRuntimeServicesTableLib.c

EFI_STATUS
EFIAPI
UefiRuntimeServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Cache pointer to the EFI Runtime Services Table
  //
  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);
  return EFI_SUCCESS;
}

 

1.3   UefiLibConstructor  在\MdePkg\Library\UefiLib\UefiLib.c

EFI_STATUS
EFIAPI
UefiLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return EFI_SUCCESS;
}

 

2. ProcessModuleEntryPointList在

\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\DEBUG\AutoGen.c

EFI_STATUS
EFIAPI
ProcessModuleEntryPointList (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return UefiMain (ImageHandle, SystemTable); //至此,马上进入到了我们写的函数中
}

 

3.继续追ProcessLibraryDestructorList 这里应该是收尾的一些工作了

\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\DEBUG\AutoGen.c

VOID
EFIAPI
ProcessLibraryDestructorList (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

}

 

上面很罗嗦的说了很多,总结一下可以用下面的图表示调用过程

AppEntry

 

当然,说了很多如果没有实验不能保证正确性

验证的办法是在上述提到的过程里面插入下面的语句

SystemTable->ConOut->OutputString(SystemTable->ConOut,L"LABZ Test X.X\n");

如果输出结果能够正常输出全部插入的,那么表示找到的点是正确的。

结果

appentry2

============================================================

2024年10月9日

还可以在 UefiApplicationEntryPoint.inf文件中加入如下编译指令:


[BuildOptions]
  MSFT:*_*_X64_CC_FLAGS  = /FAsc /Od
  

之后,可以在对应的 Application 的 Build 目录下看到生成的 .COD 文件中出现的代码。