前面介绍了静态条件下的分析,下面研究一下EFI 文件加载在内存中的情况。

用一个图来说明情况:

左侧是文件,右侧是加载到内存后的情况,可以看到对于头是照搬到内存中。对于Section 的话,看起来就比较麻烦,一般情况下内存的对齐要求会比PE文件要求的大。比如:PE 中按照 16Bytes对齐,在内存中可能要求按照 64Bytes对齐,相比是因为 PE文件希望紧凑一些,内存的数据希望读取更快所以要做成这样的。

用一个GenCRC32.exe为例:

加载到内存中的是按照 0x1000对齐,数据在文件中存放是按照 0x200对齐。

继续查看 .text Section 可以看到 RVA=0x1000,意思是:当这个PE被加载到内存后,会放在 BaseAddress+0x1000的内存地址;Pointer to Raw Data 给出 0x400意思是这个段在文件中的位置是从 0x400开始的。

下面我们查看之前的 es.efi ,可以看到文件对齐和内存对齐是相同的。

再查看 Section,可以看到 RVA 和 Pointer to Raw Data 是相同的:

有兴趣的可以多查看几个EFI和 Section ,和上面是相同的。为此,我们再做一个实验,在\MdePkg\Library\UefiApplicationEntryPoint\ApplicationEntryPoint.c 加入一个中断:

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;
    }
  }
CpuBreakpoint(); //LABZ_Debug
  //
  // Call constructor for all libraries.
  //
  ProcessLibraryConstructorList (ImageHandle, SystemTable);

重新编译一次es.efi(注意不要重新编译 NT32Pkg)。执行之后报错可以用 VS2015断下来:

跳出int中断就来到我们EFI 代码的领空:

对照ApplicationEntryPoint.cod 查看停在CpuBreakpoint() 之后的语句了:

_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   : CpuBreakpoint(); //LABZ_Debug

  00015	e8 00 00 00 00	 call	 CpuBreakpoint

; 57   :   //
; 58   :   // Call constructor for all libraries.
; 59   :   //
; 60   :   ProcessLibraryConstructorList (ImageHandle, SystemTable);

  0001a	48 8b d7	 mov	 rdx, rdi
  0001d	48 8b ce	 mov	 rcx, rsi
  00020	e8 00 00 00 00	 call	 ProcessLibraryConstructorList

当前的位置是 0x 26BABD252DA,那么这个 .text 段是在26BABD252C0 开始的,我们还可以推理出Image 加载位置(BaseAddress)0x26BABD252C0 - 0x2c0=0x26BABD25000:

.text 段在 0x26BABD25000 + 0x2C0

.rdata段在0x26BABD25000 + 0x1700

.data段在0x26BABD25000 + 0x1F20

无名段在0x26BABD25000 +  0x1F60

.xdata段在0x26BABD25000 + 0x2020

.reloc段在0x26BABD25000 + 0x20C0

我们只验证一下 .reloc 段:

可见他们是完全相同的,就是说 EFI 文件加载到内存之后依然是相同的对齐。从设计上说,这样可以使得文件更加紧凑,能够化简加载动作,同时方便调试。

参考:

1. https://www.cnblogs.com/gd-luojialin/p/11306135.html

Leave a Reply

电子邮件地址不会被公开。 必填项已用*标注

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>