1 /** @file
2 
3   Functions to get info and load PE/COFF image.
4 
5 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
6 Portions Copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.<BR>
7 Portions Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include <Common/UefiBaseTypes.h>
13 #include <CommonLib.h>
14 #include <IndustryStandard/PeImage.h>
15 #include "PeCoffLib.h"
16 
17 typedef union {
18   VOID                         *Header;
19   EFI_IMAGE_OPTIONAL_HEADER32  *Optional32;
20   EFI_IMAGE_OPTIONAL_HEADER64  *Optional64;
21 } EFI_IMAGE_OPTIONAL_HEADER_POINTER;
22 
23 STATIC
24 RETURN_STATUS
25 PeCoffLoaderGetPeHeader (
26   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT    *ImageContext,
27   OUT    EFI_IMAGE_OPTIONAL_HEADER_UNION **PeHdr,
28   OUT    EFI_TE_IMAGE_HEADER             **TeHdr
29   );
30 
31 STATIC
32 RETURN_STATUS
33 PeCoffLoaderCheckImageType (
34   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT    *ImageContext,
35   IN     EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr,
36   IN     EFI_TE_IMAGE_HEADER             *TeHdr
37   );
38 
39 STATIC
40 VOID *
41 PeCoffLoaderImageAddress (
42   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext,
43   IN     UINTN                         Address
44   );
45 
46 RETURN_STATUS
47 PeCoffLoaderRelocateIa32Image (
48   IN UINT16      *Reloc,
49   IN OUT CHAR8   *Fixup,
50   IN OUT CHAR8   **FixupData,
51   IN UINT64      Adjust
52   );
53 
54 
55 RETURN_STATUS
56 PeCoffLoaderRelocateArmImage (
57   IN UINT16      **Reloc,
58   IN OUT CHAR8   *Fixup,
59   IN OUT CHAR8   **FixupData,
60   IN UINT64      Adjust
61   );
62 
63 RETURN_STATUS
64 PeCoffLoaderRelocateRiscVImage (
65   IN UINT16      *Reloc,
66   IN OUT CHAR8   *Fixup,
67   IN OUT CHAR8   **FixupData,
68   IN UINT64      Adjust
69   );
70 
71 STATIC
72 RETURN_STATUS
PeCoffLoaderGetPeHeader(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext,OUT EFI_IMAGE_OPTIONAL_HEADER_UNION ** PeHdr,OUT EFI_TE_IMAGE_HEADER ** TeHdr)73 PeCoffLoaderGetPeHeader (
74   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT    *ImageContext,
75   OUT    EFI_IMAGE_OPTIONAL_HEADER_UNION **PeHdr,
76   OUT    EFI_TE_IMAGE_HEADER             **TeHdr
77   )
78 /*++
79 
80 Routine Description:
81 
82   Retrieves the PE or TE Header from a PE/COFF or TE image
83 
84 Arguments:
85 
86   ImageContext  - The context of the image being loaded
87 
88   PeHdr         - The buffer in which to return the PE header
89 
90   TeHdr         - The buffer in which to return the TE header
91 
92 Returns:
93 
94   RETURN_SUCCESS if the PE or TE Header is read,
95   Otherwise, the error status from reading the PE/COFF or TE image using the ImageRead function.
96 
97 --*/
98 {
99   RETURN_STATUS         Status;
100   EFI_IMAGE_DOS_HEADER  DosHdr;
101   UINTN                 Size;
102 
103   ImageContext->IsTeImage = FALSE;
104   //
105   // Read the DOS image headers
106   //
107   Size = sizeof (EFI_IMAGE_DOS_HEADER);
108   Status = ImageContext->ImageRead (
109                           ImageContext->Handle,
110                           0,
111                           &Size,
112                           &DosHdr
113                           );
114   if (RETURN_ERROR (Status)) {
115     ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
116     return Status;
117   }
118 
119   ImageContext->PeCoffHeaderOffset = 0;
120   if (DosHdr.e_magic == EFI_IMAGE_DOS_SIGNATURE) {
121     //
122     // DOS image header is present, so read the PE header after the DOS image header
123     //
124     ImageContext->PeCoffHeaderOffset = DosHdr.e_lfanew;
125   }
126   //
127   // Get the PE/COFF Header pointer
128   //
129   *PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((UINTN)ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
130   if ((*PeHdr)->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
131     //
132     // Check the PE/COFF Header Signature. If not, then try to get a TE header
133     //
134     *TeHdr = (EFI_TE_IMAGE_HEADER *)*PeHdr;
135     if ((*TeHdr)->Signature != EFI_TE_IMAGE_HEADER_SIGNATURE) {
136       return RETURN_UNSUPPORTED;
137     }
138     ImageContext->IsTeImage = TRUE;
139   }
140 
141   return RETURN_SUCCESS;
142 }
143 
144 STATIC
145 RETURN_STATUS
PeCoffLoaderCheckImageType(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext,IN EFI_IMAGE_OPTIONAL_HEADER_UNION * PeHdr,IN EFI_TE_IMAGE_HEADER * TeHdr)146 PeCoffLoaderCheckImageType (
147   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT          *ImageContext,
148   IN     EFI_IMAGE_OPTIONAL_HEADER_UNION       *PeHdr,
149   IN     EFI_TE_IMAGE_HEADER                   *TeHdr
150   )
151 /*++
152 
153 Routine Description:
154 
155   Checks the PE or TE header of a PE/COFF or TE image to determine if it supported
156 
157 Arguments:
158 
159   ImageContext  - The context of the image being loaded
160 
161   PeHdr         - The buffer in which to return the PE header
162 
163   TeHdr         - The buffer in which to return the TE header
164 
165 Returns:
166 
167   RETURN_SUCCESS if the PE/COFF or TE image is supported
168   RETURN_UNSUPPORTED of the PE/COFF or TE image is not supported.
169 
170 --*/
171 {
172   //
173   // See if the machine type is supported.
174   // We support a native machine type (IA-32/Itanium-based)
175   //
176   if (ImageContext->IsTeImage == FALSE) {
177     ImageContext->Machine = PeHdr->Pe32.FileHeader.Machine;
178   } else {
179     ImageContext->Machine = TeHdr->Machine;
180   }
181 
182   if (ImageContext->Machine != EFI_IMAGE_MACHINE_IA32 && \
183       ImageContext->Machine != EFI_IMAGE_MACHINE_X64  && \
184       ImageContext->Machine != EFI_IMAGE_MACHINE_ARMT && \
185       ImageContext->Machine != EFI_IMAGE_MACHINE_EBC  && \
186       ImageContext->Machine != EFI_IMAGE_MACHINE_AARCH64 && \
187       ImageContext->Machine != EFI_IMAGE_MACHINE_RISCV64) {
188     if (ImageContext->Machine == IMAGE_FILE_MACHINE_ARM) {
189       //
190       // There are two types of ARM images. Pure ARM and ARM/Thumb.
191       // If we see the ARM say it is the ARM/Thumb so there is only
192       // a single machine type we need to check for ARM.
193       //
194       ImageContext->Machine = EFI_IMAGE_MACHINE_ARMT;
195       if (ImageContext->IsTeImage == FALSE) {
196         PeHdr->Pe32.FileHeader.Machine = ImageContext->Machine;
197       } else {
198         TeHdr->Machine = ImageContext->Machine;
199       }
200 
201     } else {
202       //
203       // unsupported PeImage machine type
204       //
205       return RETURN_UNSUPPORTED;
206     }
207   }
208 
209   //
210   // See if the image type is supported.  We support EFI Applications,
211   // EFI Boot Service Drivers, EFI Runtime Drivers and EFI SAL Drivers.
212   //
213   if (ImageContext->IsTeImage == FALSE) {
214     ImageContext->ImageType = PeHdr->Pe32.OptionalHeader.Subsystem;
215   } else {
216     ImageContext->ImageType = (UINT16) (TeHdr->Subsystem);
217   }
218 
219   if (ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION && \
220       ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER && \
221       ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER && \
222       ImageContext->ImageType != EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER) {
223     //
224     // unsupported PeImage subsystem type
225     //
226     return RETURN_UNSUPPORTED;
227   }
228 
229   return RETURN_SUCCESS;
230 }
231 
232 RETURN_STATUS
233 EFIAPI
PeCoffLoaderGetImageInfo(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)234 PeCoffLoaderGetImageInfo (
235   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT           *ImageContext
236   )
237 /*++
238 
239 Routine Description:
240 
241   Retrieves information on a PE/COFF image
242 
243 Arguments:
244 
245   This         - Calling context
246   ImageContext - The context of the image being loaded
247 
248 Returns:
249 
250   RETURN_SUCCESS           - The information on the PE/COFF image was collected.
251   RETURN_INVALID_PARAMETER - ImageContext is NULL.
252   RETURN_UNSUPPORTED       - The PE/COFF image is not supported.
253   Otherwise             - The error status from reading the PE/COFF image using the
254                           ImageContext->ImageRead() function
255 
256 --*/
257 {
258   RETURN_STATUS                   Status;
259   EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHdr;
260   EFI_TE_IMAGE_HEADER             *TeHdr;
261   EFI_IMAGE_DATA_DIRECTORY        *DebugDirectoryEntry;
262   UINTN                           Size;
263   UINTN                           Index;
264   UINTN                           DebugDirectoryEntryRva;
265   UINTN                           DebugDirectoryEntryFileOffset;
266   UINTN                           SectionHeaderOffset;
267   EFI_IMAGE_SECTION_HEADER        SectionHeader;
268   EFI_IMAGE_DEBUG_DIRECTORY_ENTRY DebugEntry;
269   EFI_IMAGE_OPTIONAL_HEADER_POINTER OptionHeader;
270 
271   PeHdr = NULL;
272   TeHdr = NULL;
273   DebugDirectoryEntry    = NULL;
274   DebugDirectoryEntryRva = 0;
275 
276   if (NULL == ImageContext) {
277     return RETURN_INVALID_PARAMETER;
278   }
279   //
280   // Assume success
281   //
282   ImageContext->ImageError  = IMAGE_ERROR_SUCCESS;
283 
284   Status                    = PeCoffLoaderGetPeHeader (ImageContext, &PeHdr, &TeHdr);
285   if (RETURN_ERROR (Status)) {
286     return Status;
287   }
288 
289   //
290   // Verify machine type
291   //
292   Status = PeCoffLoaderCheckImageType (ImageContext, PeHdr, TeHdr);
293   if (RETURN_ERROR (Status)) {
294     return Status;
295   }
296   OptionHeader.Header = (VOID *) &(PeHdr->Pe32.OptionalHeader);
297 
298   //
299   // Retrieve the base address of the image
300   //
301   if (!(ImageContext->IsTeImage)) {
302     if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
303       ImageContext->ImageAddress = (PHYSICAL_ADDRESS) OptionHeader.Optional32->ImageBase;
304     } else {
305       ImageContext->ImageAddress = (PHYSICAL_ADDRESS) OptionHeader.Optional64->ImageBase;
306     }
307   } else {
308     ImageContext->ImageAddress = (PHYSICAL_ADDRESS) (TeHdr->ImageBase + TeHdr->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER));
309   }
310   //
311   // Initialize the alternate destination address to 0 indicating that it
312   // should not be used.
313   //
314   ImageContext->DestinationAddress = 0;
315 
316   //
317   // Initialize the codeview pointer.
318   //
319   ImageContext->CodeView    = NULL;
320   ImageContext->PdbPointer  = NULL;
321 
322   //
323   // Three cases with regards to relocations:
324   // - Image has base relocs, RELOCS_STRIPPED==0    => image is relocatable
325   // - Image has no base relocs, RELOCS_STRIPPED==1 => Image is not relocatable
326   // - Image has no base relocs, RELOCS_STRIPPED==0 => Image is relocatable but
327   //   has no base relocs to apply
328   // Obviously having base relocations with RELOCS_STRIPPED==1 is invalid.
329   //
330   // Look at the file header to determine if relocations have been stripped, and
331   // save this info in the image context for later use.
332   //
333   if ((!(ImageContext->IsTeImage)) && ((PeHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0)) {
334     ImageContext->RelocationsStripped = TRUE;
335   } else if ((ImageContext->IsTeImage) && (TeHdr->DataDirectory[0].Size == 0) && (TeHdr->DataDirectory[0].VirtualAddress == 0)) {
336     ImageContext->RelocationsStripped = TRUE;
337   } else {
338     ImageContext->RelocationsStripped = FALSE;
339   }
340 
341   if (!(ImageContext->IsTeImage)) {
342 
343     if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
344       ImageContext->ImageSize         = (UINT64) OptionHeader.Optional32->SizeOfImage;
345       ImageContext->SectionAlignment  = OptionHeader.Optional32->SectionAlignment;
346       ImageContext->SizeOfHeaders     = OptionHeader.Optional32->SizeOfHeaders;
347 
348       //
349       // Modify ImageSize to contain .PDB file name if required and initialize
350       // PdbRVA field...
351       //
352       if (OptionHeader.Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
353         DebugDirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionHeader.Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
354         DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress;
355       }
356     } else {
357       ImageContext->ImageSize         = (UINT64) OptionHeader.Optional64->SizeOfImage;
358       ImageContext->SectionAlignment  = OptionHeader.Optional64->SectionAlignment;
359       ImageContext->SizeOfHeaders     = OptionHeader.Optional64->SizeOfHeaders;
360 
361       //
362       // Modify ImageSize to contain .PDB file name if required and initialize
363       // PdbRVA field...
364       //
365       if (OptionHeader.Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
366         DebugDirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *) &(OptionHeader.Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
367         DebugDirectoryEntryRva = DebugDirectoryEntry->VirtualAddress;
368       }
369     }
370 
371     if (DebugDirectoryEntryRva != 0) {
372       //
373       // Determine the file offset of the debug directory...  This means we walk
374       // the sections to find which section contains the RVA of the debug
375       // directory
376       //
377       DebugDirectoryEntryFileOffset = 0;
378 
379       SectionHeaderOffset = (UINTN)(
380                                ImageContext->PeCoffHeaderOffset +
381                                sizeof (UINT32) +
382                                sizeof (EFI_IMAGE_FILE_HEADER) +
383                                PeHdr->Pe32.FileHeader.SizeOfOptionalHeader
384                                );
385 
386       for (Index = 0; Index < PeHdr->Pe32.FileHeader.NumberOfSections; Index++) {
387         //
388         // Read section header from file
389         //
390         Size = sizeof (EFI_IMAGE_SECTION_HEADER);
391         Status = ImageContext->ImageRead (
392                                  ImageContext->Handle,
393                                  SectionHeaderOffset,
394                                  &Size,
395                                  &SectionHeader
396                                  );
397         if (RETURN_ERROR (Status)) {
398           ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
399           return Status;
400         }
401 
402         if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress &&
403             DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) {
404             DebugDirectoryEntryFileOffset =
405             DebugDirectoryEntryRva - SectionHeader.VirtualAddress + SectionHeader.PointerToRawData;
406           break;
407         }
408 
409         SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
410       }
411 
412       if (DebugDirectoryEntryFileOffset != 0) {
413         for (Index = 0; Index < DebugDirectoryEntry->Size; Index += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) {
414           //
415           // Read next debug directory entry
416           //
417           Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
418           Status = ImageContext->ImageRead (
419                                    ImageContext->Handle,
420                                    DebugDirectoryEntryFileOffset + Index,
421                                    &Size,
422                                    &DebugEntry
423                                    );
424           if (RETURN_ERROR (Status)) {
425             ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
426             return Status;
427           }
428 
429           if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
430             ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index);
431             if (DebugEntry.RVA == 0 && DebugEntry.FileOffset != 0) {
432               ImageContext->ImageSize += DebugEntry.SizeOfData;
433             }
434 
435             return RETURN_SUCCESS;
436           }
437         }
438       }
439     }
440   } else {
441     ImageContext->ImageSize         = 0;
442     ImageContext->SectionAlignment  = 4096;
443     ImageContext->SizeOfHeaders     = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN) TeHdr->BaseOfCode - (UINTN) TeHdr->StrippedSize;
444 
445     DebugDirectoryEntry             = &TeHdr->DataDirectory[1];
446     DebugDirectoryEntryRva          = DebugDirectoryEntry->VirtualAddress;
447     SectionHeaderOffset             = (UINTN) (sizeof (EFI_TE_IMAGE_HEADER));
448 
449     DebugDirectoryEntryFileOffset   = 0;
450 
451     for (Index = 0; Index < TeHdr->NumberOfSections;) {
452       //
453       // Read section header from file
454       //
455       Size = sizeof (EFI_IMAGE_SECTION_HEADER);
456       Status = ImageContext->ImageRead (
457                                ImageContext->Handle,
458                                SectionHeaderOffset,
459                                &Size,
460                                &SectionHeader
461                                );
462       if (RETURN_ERROR (Status)) {
463         ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
464         return Status;
465       }
466 
467       if (DebugDirectoryEntryRva >= SectionHeader.VirtualAddress &&
468           DebugDirectoryEntryRva < SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize) {
469         DebugDirectoryEntryFileOffset = DebugDirectoryEntryRva -
470           SectionHeader.VirtualAddress +
471           SectionHeader.PointerToRawData +
472           sizeof (EFI_TE_IMAGE_HEADER) -
473           TeHdr->StrippedSize;
474 
475         //
476         // File offset of the debug directory was found, if this is not the last
477         // section, then skip to the last section for calculating the image size.
478         //
479         if (Index < (UINTN) TeHdr->NumberOfSections - 1) {
480           SectionHeaderOffset += (TeHdr->NumberOfSections - 1 - Index) * sizeof (EFI_IMAGE_SECTION_HEADER);
481           Index = TeHdr->NumberOfSections - 1;
482           continue;
483         }
484       }
485 
486       //
487       // In Te image header there is not a field to describe the ImageSize.
488       // Actually, the ImageSize equals the RVA plus the VirtualSize of
489       // the last section mapped into memory (Must be rounded up to
490       // a multiple of Section Alignment). Per the PE/COFF specification, the
491       // section headers in the Section Table must appear in order of the RVA
492       // values for the corresponding sections. So the ImageSize can be determined
493       // by the RVA and the VirtualSize of the last section header in the
494       // Section Table.
495       //
496       if ((++Index) == (UINTN) TeHdr->NumberOfSections) {
497         ImageContext->ImageSize = (SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize +
498                                    ImageContext->SectionAlignment - 1) & ~(ImageContext->SectionAlignment - 1);
499       }
500 
501       SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
502     }
503 
504     if (DebugDirectoryEntryFileOffset != 0) {
505       for (Index = 0; Index < DebugDirectoryEntry->Size; Index += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) {
506         //
507         // Read next debug directory entry
508         //
509         Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
510         Status = ImageContext->ImageRead (
511                                  ImageContext->Handle,
512                                  DebugDirectoryEntryFileOffset,
513                                  &Size,
514                                  &DebugEntry
515                                  );
516         if (RETURN_ERROR (Status)) {
517           ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
518           return Status;
519         }
520 
521         if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
522           ImageContext->DebugDirectoryEntryRva = (UINT32) (DebugDirectoryEntryRva + Index);
523           return RETURN_SUCCESS;
524         }
525       }
526     }
527   }
528 
529   return RETURN_SUCCESS;
530 }
531 
532 STATIC
533 VOID *
PeCoffLoaderImageAddress(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext,IN UINTN Address)534 PeCoffLoaderImageAddress (
535   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT          *ImageContext,
536   IN     UINTN                                 Address
537   )
538 /*++
539 
540 Routine Description:
541 
542   Converts an image address to the loaded address
543 
544 Arguments:
545 
546   ImageContext  - The context of the image being loaded
547 
548   Address       - The address to be converted to the loaded address
549 
550 Returns:
551 
552   NULL if the address can not be converted, otherwise, the converted address
553 
554 --*/
555 {
556   if (Address >= ImageContext->ImageSize) {
557     ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS;
558     return NULL;
559   }
560 
561   return (UINT8 *) ((UINTN) ImageContext->ImageAddress + Address);
562 }
563 
564 RETURN_STATUS
565 EFIAPI
PeCoffLoaderRelocateImage(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)566 PeCoffLoaderRelocateImage (
567   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
568   )
569 /*++
570 
571 Routine Description:
572 
573   Relocates a PE/COFF image in memory
574 
575 Arguments:
576 
577   This         - Calling context
578 
579   ImageContext - Contains information on the loaded image to relocate
580 
581 Returns:
582 
583   RETURN_SUCCESS      if the PE/COFF image was relocated
584   RETURN_LOAD_ERROR   if the image is not a valid PE/COFF image
585   RETURN_UNSUPPORTED  not support
586 
587 --*/
588 {
589   RETURN_STATUS                         Status;
590   EFI_IMAGE_OPTIONAL_HEADER_UNION       *PeHdr;
591   EFI_TE_IMAGE_HEADER                   *TeHdr;
592   EFI_IMAGE_DATA_DIRECTORY              *RelocDir;
593   UINT64                                Adjust;
594   EFI_IMAGE_BASE_RELOCATION             *RelocBase;
595   EFI_IMAGE_BASE_RELOCATION             *RelocBaseEnd;
596   UINT16                                *Reloc;
597   UINT16                                *RelocEnd;
598   CHAR8                                 *Fixup;
599   CHAR8                                 *FixupBase;
600   UINT16                                *F16;
601   UINT32                                *F32;
602   UINT64                                *F64;
603   CHAR8                                 *FixupData;
604   PHYSICAL_ADDRESS                      BaseAddress;
605   UINT16                                MachineType;
606   EFI_IMAGE_OPTIONAL_HEADER_POINTER     OptionHeader;
607 
608   PeHdr = NULL;
609   TeHdr = NULL;
610   //
611   // Assume success
612   //
613   ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
614 
615   //
616   // If there are no relocation entries, then we are done
617   //
618   if (ImageContext->RelocationsStripped) {
619     return RETURN_SUCCESS;
620   }
621 
622   //
623   // Use DestinationAddress field of ImageContext as the relocation address even if it is 0.
624   //
625   BaseAddress = ImageContext->DestinationAddress;
626 
627   if (!(ImageContext->IsTeImage)) {
628     PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINTN)ImageContext->ImageAddress +
629                                             ImageContext->PeCoffHeaderOffset);
630     OptionHeader.Header = (VOID *) &(PeHdr->Pe32.OptionalHeader);
631     if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
632       Adjust = (UINT64) BaseAddress - OptionHeader.Optional32->ImageBase;
633       OptionHeader.Optional32->ImageBase = (UINT32) BaseAddress;
634       MachineType = ImageContext->Machine;
635       //
636       // Find the relocation block
637       //
638       // Per the PE/COFF spec, you can't assume that a given data directory
639       // is present in the image. You have to check the NumberOfRvaAndSizes in
640       // the optional header to verify a desired directory entry is there.
641       //
642       if (OptionHeader.Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
643         RelocDir  = &OptionHeader.Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
644         if ((RelocDir != NULL) && (RelocDir->Size > 0)) {
645           RelocBase = PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress);
646           RelocBaseEnd = PeCoffLoaderImageAddress (
647                            ImageContext,
648                            RelocDir->VirtualAddress + RelocDir->Size - 1
649                            );
650           if (RelocBase == NULL || RelocBaseEnd == NULL || RelocBaseEnd < RelocBase) {
651             ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
652             return RETURN_LOAD_ERROR;
653           }
654         } else {
655           //
656           // Set base and end to bypass processing below.
657           //
658           RelocBase = RelocBaseEnd = 0;
659         }
660       } else {
661         //
662         // Set base and end to bypass processing below.
663         //
664         RelocBase = RelocBaseEnd = 0;
665       }
666     } else {
667       Adjust = (UINT64) BaseAddress - OptionHeader.Optional64->ImageBase;
668       OptionHeader.Optional64->ImageBase = BaseAddress;
669       MachineType = ImageContext->Machine;
670       //
671       // Find the relocation block
672       //
673       // Per the PE/COFF spec, you can't assume that a given data directory
674       // is present in the image. You have to check the NumberOfRvaAndSizes in
675       // the optional header to verify a desired directory entry is there.
676       //
677       if (OptionHeader.Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
678         RelocDir  = &OptionHeader.Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
679         if ((RelocDir != NULL) && (RelocDir->Size > 0)) {
680           RelocBase = PeCoffLoaderImageAddress (ImageContext, RelocDir->VirtualAddress);
681           RelocBaseEnd = PeCoffLoaderImageAddress (
682                            ImageContext,
683                            RelocDir->VirtualAddress + RelocDir->Size - 1
684                           );
685           if (RelocBase == NULL || RelocBaseEnd == NULL || RelocBaseEnd < RelocBase) {
686             ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
687             return RETURN_LOAD_ERROR;
688           }
689         } else {
690           //
691           // Set base and end to bypass processing below.
692           //
693           RelocBase = RelocBaseEnd = 0;
694         }
695       } else {
696         //
697         // Set base and end to bypass processing below.
698         //
699         RelocBase = RelocBaseEnd = 0;
700       }
701     }
702   } else {
703     TeHdr             = (EFI_TE_IMAGE_HEADER *) (UINTN) (ImageContext->ImageAddress);
704     Adjust            = (UINT64) (BaseAddress - TeHdr->ImageBase);
705     TeHdr->ImageBase  = (UINT64) (BaseAddress);
706     MachineType = TeHdr->Machine;
707 
708     //
709     // Find the relocation block
710     //
711     RelocDir = &TeHdr->DataDirectory[0];
712     RelocBase = (EFI_IMAGE_BASE_RELOCATION *)(UINTN)(
713                                     ImageContext->ImageAddress +
714                                     RelocDir->VirtualAddress +
715                                     sizeof(EFI_TE_IMAGE_HEADER) -
716                                     TeHdr->StrippedSize
717                                     );
718     RelocBaseEnd = (EFI_IMAGE_BASE_RELOCATION *) ((UINTN) RelocBase + (UINTN) RelocDir->Size - 1);
719   }
720 
721   //
722   // Run the relocation information and apply the fixups
723   //
724   FixupData = ImageContext->FixupData;
725   while (RelocBase < RelocBaseEnd) {
726 
727     Reloc     = (UINT16 *) ((CHAR8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
728     RelocEnd  = (UINT16 *) ((CHAR8 *) RelocBase + RelocBase->SizeOfBlock);
729     if (!(ImageContext->IsTeImage)) {
730       FixupBase = PeCoffLoaderImageAddress (ImageContext, RelocBase->VirtualAddress);
731       if (FixupBase == NULL) {
732         ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
733         return RETURN_LOAD_ERROR;
734       }
735     } else {
736       FixupBase = (CHAR8 *)(UINTN)(ImageContext->ImageAddress +
737                     RelocBase->VirtualAddress +
738                     sizeof(EFI_TE_IMAGE_HEADER) -
739                     TeHdr->StrippedSize
740                     );
741     }
742 
743     if ((CHAR8 *) RelocEnd < (CHAR8 *) ((UINTN) ImageContext->ImageAddress) ||
744         (CHAR8 *) RelocEnd > (CHAR8 *)((UINTN)ImageContext->ImageAddress +
745           (UINTN)ImageContext->ImageSize)) {
746       ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
747       return RETURN_LOAD_ERROR;
748     }
749 
750     //
751     // Run this relocation record
752     //
753     while (Reloc < RelocEnd) {
754 
755       Fixup = FixupBase + (*Reloc & 0xFFF);
756       switch ((*Reloc) >> 12) {
757       case EFI_IMAGE_REL_BASED_ABSOLUTE:
758         break;
759 
760       case EFI_IMAGE_REL_BASED_HIGH:
761         F16   = (UINT16 *) Fixup;
762         *F16 = (UINT16) (*F16 + ((UINT16) ((UINT32) Adjust >> 16)));
763         if (FixupData != NULL) {
764           *(UINT16 *) FixupData = *F16;
765           FixupData             = FixupData + sizeof (UINT16);
766         }
767         break;
768 
769       case EFI_IMAGE_REL_BASED_LOW:
770         F16   = (UINT16 *) Fixup;
771         *F16  = (UINT16) (*F16 + (UINT16) Adjust);
772         if (FixupData != NULL) {
773           *(UINT16 *) FixupData = *F16;
774           FixupData             = FixupData + sizeof (UINT16);
775         }
776         break;
777 
778       case EFI_IMAGE_REL_BASED_HIGHLOW:
779         F32   = (UINT32 *) Fixup;
780         *F32  = *F32 + (UINT32) Adjust;
781         if (FixupData != NULL) {
782           FixupData             = ALIGN_POINTER (FixupData, sizeof (UINT32));
783           *(UINT32 *) FixupData = *F32;
784           FixupData             = FixupData + sizeof (UINT32);
785         }
786         break;
787 
788       case EFI_IMAGE_REL_BASED_DIR64:
789         F64   = (UINT64 *) Fixup;
790         *F64  = *F64 + (UINT64) Adjust;
791         if (FixupData != NULL) {
792           FixupData             = ALIGN_POINTER (FixupData, sizeof (UINT64));
793           *(UINT64 *) FixupData = *F64;
794           FixupData             = FixupData + sizeof (UINT64);
795         }
796         break;
797 
798       case EFI_IMAGE_REL_BASED_HIGHADJ:
799         //
800         // Return the same EFI_UNSUPPORTED return code as
801         // PeCoffLoaderRelocateImageEx() returns if it does not recognize
802         // the relocation type.
803         //
804         ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
805         return RETURN_UNSUPPORTED;
806 
807       default:
808         switch (MachineType) {
809         case EFI_IMAGE_MACHINE_IA32:
810           Status = PeCoffLoaderRelocateIa32Image (Reloc, Fixup, &FixupData, Adjust);
811           break;
812         case EFI_IMAGE_MACHINE_ARMT:
813           Status = PeCoffLoaderRelocateArmImage (&Reloc, Fixup, &FixupData, Adjust);
814           break;
815         case EFI_IMAGE_MACHINE_RISCV64:
816           Status = PeCoffLoaderRelocateRiscVImage (Reloc, Fixup, &FixupData, Adjust);
817           break;
818         default:
819           Status = RETURN_UNSUPPORTED;
820           break;
821         }
822         if (RETURN_ERROR (Status)) {
823           ImageContext->ImageError = IMAGE_ERROR_FAILED_RELOCATION;
824           return Status;
825         }
826       }
827 
828       //
829       // Next relocation record
830       //
831       Reloc += 1;
832     }
833 
834     //
835     // Next reloc block
836     //
837     RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
838   }
839 
840   return RETURN_SUCCESS;
841 }
842 
843 RETURN_STATUS
844 EFIAPI
PeCoffLoaderLoadImage(IN OUT PE_COFF_LOADER_IMAGE_CONTEXT * ImageContext)845 PeCoffLoaderLoadImage (
846   IN OUT PE_COFF_LOADER_IMAGE_CONTEXT  *ImageContext
847   )
848 /*++
849 
850 Routine Description:
851 
852   Loads a PE/COFF image into memory
853 
854 Arguments:
855 
856   This         - Calling context
857 
858   ImageContext - Contains information on image to load into memory
859 
860 Returns:
861 
862   RETURN_SUCCESS            if the PE/COFF image was loaded
863   RETURN_BUFFER_TOO_SMALL   if the caller did not provide a large enough buffer
864   RETURN_LOAD_ERROR         if the image is a runtime driver with no relocations
865   RETURN_INVALID_PARAMETER  if the image address is invalid
866 
867 --*/
868 {
869   RETURN_STATUS                         Status;
870   EFI_IMAGE_OPTIONAL_HEADER_UNION       *PeHdr;
871   EFI_TE_IMAGE_HEADER                   *TeHdr;
872   PE_COFF_LOADER_IMAGE_CONTEXT          CheckContext;
873   EFI_IMAGE_SECTION_HEADER              *FirstSection;
874   EFI_IMAGE_SECTION_HEADER              *Section;
875   UINTN                                 NumberOfSections;
876   UINTN                                 Index;
877   CHAR8                                 *Base;
878   CHAR8                                 *End;
879   CHAR8                                 *MaxEnd;
880   EFI_IMAGE_DATA_DIRECTORY              *DirectoryEntry;
881   EFI_IMAGE_DEBUG_DIRECTORY_ENTRY       *DebugEntry;
882   UINTN                                 Size;
883   UINT32                                TempDebugEntryRva;
884   EFI_IMAGE_OPTIONAL_HEADER_POINTER     OptionHeader;
885 
886   PeHdr = NULL;
887   TeHdr = NULL;
888   OptionHeader.Header = NULL;
889   //
890   // Assume success
891   //
892   ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
893 
894   //
895   // Copy the provided context info into our local version, get what we
896   // can from the original image, and then use that to make sure everything
897   // is legit.
898   //
899   CopyMem (&CheckContext, ImageContext, sizeof (PE_COFF_LOADER_IMAGE_CONTEXT));
900 
901   Status = PeCoffLoaderGetImageInfo (&CheckContext);
902   if (RETURN_ERROR (Status)) {
903     return Status;
904   }
905 
906   //
907   // Make sure there is enough allocated space for the image being loaded
908   //
909   if (ImageContext->ImageSize < CheckContext.ImageSize) {
910     ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_SIZE;
911     return RETURN_BUFFER_TOO_SMALL;
912   }
913 
914   //
915   // If there's no relocations, then make sure it's not a runtime driver,
916   // and that it's being loaded at the linked address.
917   //
918   if (CheckContext.RelocationsStripped) {
919     //
920     // If the image does not contain relocations and it is a runtime driver
921     // then return an error.
922     //
923     if (CheckContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) {
924       ImageContext->ImageError = IMAGE_ERROR_INVALID_SUBSYSTEM;
925       return RETURN_LOAD_ERROR;
926     }
927     //
928     // If the image does not contain relocations, and the requested load address
929     // is not the linked address, then return an error.
930     //
931     if (CheckContext.ImageAddress != ImageContext->ImageAddress) {
932       ImageContext->ImageError = IMAGE_ERROR_INVALID_IMAGE_ADDRESS;
933       return RETURN_INVALID_PARAMETER;
934     }
935   }
936   //
937   // Make sure the allocated space has the proper section alignment
938   //
939   if (!(ImageContext->IsTeImage)) {
940     if ((ImageContext->ImageAddress & (CheckContext.SectionAlignment - 1)) != 0) {
941       ImageContext->ImageError = IMAGE_ERROR_INVALID_SECTION_ALIGNMENT;
942       return RETURN_INVALID_PARAMETER;
943     }
944   }
945   //
946   // Read the entire PE/COFF or TE header into memory
947   //
948   if (!(ImageContext->IsTeImage)) {
949     Status = ImageContext->ImageRead (
950                             ImageContext->Handle,
951                             0,
952                             &ImageContext->SizeOfHeaders,
953                             (VOID *) (UINTN) ImageContext->ImageAddress
954                             );
955 
956     PeHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)
957       ((UINTN)ImageContext->ImageAddress + ImageContext->PeCoffHeaderOffset);
958 
959     OptionHeader.Header = (VOID *) &(PeHdr->Pe32.OptionalHeader);
960 
961     FirstSection = (EFI_IMAGE_SECTION_HEADER *) (
962                       (UINTN)ImageContext->ImageAddress +
963                       ImageContext->PeCoffHeaderOffset +
964                       sizeof(UINT32) +
965                       sizeof(EFI_IMAGE_FILE_HEADER) +
966                       PeHdr->Pe32.FileHeader.SizeOfOptionalHeader
967       );
968     NumberOfSections = (UINTN) (PeHdr->Pe32.FileHeader.NumberOfSections);
969   } else {
970     Status = ImageContext->ImageRead (
971                             ImageContext->Handle,
972                             0,
973                             &ImageContext->SizeOfHeaders,
974                             (VOID *) (UINTN) ImageContext->ImageAddress
975                             );
976 
977     TeHdr             = (EFI_TE_IMAGE_HEADER *) (UINTN) (ImageContext->ImageAddress);
978 
979     FirstSection = (EFI_IMAGE_SECTION_HEADER *) (
980           (UINTN)ImageContext->ImageAddress +
981           sizeof(EFI_TE_IMAGE_HEADER)
982           );
983     NumberOfSections  = (UINTN) (TeHdr->NumberOfSections);
984 
985   }
986 
987   if (RETURN_ERROR (Status)) {
988     ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
989     return RETURN_LOAD_ERROR;
990   }
991 
992   //
993   // Load each section of the image
994   //
995   Section = FirstSection;
996   for (Index = 0, MaxEnd = NULL; Index < NumberOfSections; Index++) {
997 
998     //
999     // Compute sections address
1000     //
1001     Base = PeCoffLoaderImageAddress (ImageContext, Section->VirtualAddress);
1002     End = PeCoffLoaderImageAddress (
1003             ImageContext,
1004             Section->VirtualAddress + Section->Misc.VirtualSize - 1
1005             );
1006 
1007     //
1008     // If the base start or end address resolved to 0, then fail.
1009     //
1010     if ((Base == NULL) || (End == NULL)) {
1011       ImageContext->ImageError = IMAGE_ERROR_SECTION_NOT_LOADED;
1012       return RETURN_LOAD_ERROR;
1013     }
1014 
1015 
1016     if (ImageContext->IsTeImage) {
1017       Base  = (CHAR8 *) ((UINTN) Base + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize);
1018       End   = (CHAR8 *) ((UINTN) End + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize);
1019     }
1020 
1021     if (End > MaxEnd) {
1022       MaxEnd = End;
1023     }
1024 
1025     //
1026     // Read the section
1027     //
1028     Size = (UINTN) Section->Misc.VirtualSize;
1029     if ((Size == 0) || (Size > Section->SizeOfRawData)) {
1030       Size = (UINTN) Section->SizeOfRawData;
1031     }
1032 
1033     if (Section->SizeOfRawData) {
1034       if (!(ImageContext->IsTeImage)) {
1035         Status = ImageContext->ImageRead (
1036                                 ImageContext->Handle,
1037                                 Section->PointerToRawData,
1038                                 &Size,
1039                                 Base
1040                                 );
1041       } else {
1042         Status = ImageContext->ImageRead (
1043                                 ImageContext->Handle,
1044                                 Section->PointerToRawData + sizeof (EFI_TE_IMAGE_HEADER) - (UINTN) TeHdr->StrippedSize,
1045                                 &Size,
1046                                 Base
1047                                 );
1048       }
1049 
1050       if (RETURN_ERROR (Status)) {
1051         ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
1052         return Status;
1053       }
1054     }
1055 
1056     //
1057     // If raw size is less then virt size, zero fill the remaining
1058     //
1059 
1060     if (Size < Section->Misc.VirtualSize) {
1061       ZeroMem (Base + Size, Section->Misc.VirtualSize - Size);
1062     }
1063 
1064     //
1065     // Next Section
1066     //
1067     Section += 1;
1068   }
1069 
1070   //
1071   // Get image's entry point
1072   //
1073   if (!(ImageContext->IsTeImage)) {
1074     ImageContext->EntryPoint = (PHYSICAL_ADDRESS) (UINTN) PeCoffLoaderImageAddress (
1075                                                                 ImageContext,
1076                                                                 PeHdr->Pe32.OptionalHeader.AddressOfEntryPoint
1077                                                                 );
1078   } else {
1079     ImageContext->EntryPoint = (UINTN)ImageContext->ImageAddress +
1080                                (UINTN)TeHdr->AddressOfEntryPoint +
1081                                (UINTN)sizeof(EFI_TE_IMAGE_HEADER) -
1082                                (UINTN) TeHdr->StrippedSize;
1083   }
1084 
1085   //
1086   // Determine the size of the fixup data
1087   //
1088   // Per the PE/COFF spec, you can't assume that a given data directory
1089   // is present in the image. You have to check the NumberOfRvaAndSizes in
1090   // the optional header to verify a desired directory entry is there.
1091   //
1092   if (!(ImageContext->IsTeImage)) {
1093     if (PeHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
1094       if (OptionHeader.Optional32->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
1095         DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)
1096           &OptionHeader.Optional32->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
1097         ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
1098       } else {
1099         ImageContext->FixupDataSize = 0;
1100       }
1101     } else {
1102       if (OptionHeader.Optional64->NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
1103         DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)
1104           &OptionHeader.Optional64->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
1105         ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
1106       } else {
1107         ImageContext->FixupDataSize = 0;
1108       }
1109     }
1110   } else {
1111     DirectoryEntry              = &TeHdr->DataDirectory[0];
1112     ImageContext->FixupDataSize = DirectoryEntry->Size / sizeof (UINT16) * sizeof (UINTN);
1113   }
1114   //
1115   // Consumer must allocate a buffer for the relocation fixup log.
1116   // Only used for runtime drivers.
1117   //
1118   ImageContext->FixupData = NULL;
1119 
1120   //
1121   // Load the Codeview info if present
1122   //
1123   if (ImageContext->DebugDirectoryEntryRva != 0) {
1124     if (!(ImageContext->IsTeImage)) {
1125       DebugEntry = PeCoffLoaderImageAddress (
1126                     ImageContext,
1127                     ImageContext->DebugDirectoryEntryRva
1128                     );
1129     } else {
1130       DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)(UINTN)(
1131                                                ImageContext->ImageAddress +
1132                                                ImageContext->DebugDirectoryEntryRva +
1133                                                sizeof(EFI_TE_IMAGE_HEADER) -
1134                                                TeHdr->StrippedSize
1135                                                );
1136     }
1137 
1138     if (DebugEntry != NULL) {
1139       TempDebugEntryRva = DebugEntry->RVA;
1140       if (DebugEntry->RVA == 0 && DebugEntry->FileOffset != 0) {
1141         Section--;
1142         if ((UINTN) Section->SizeOfRawData < Section->Misc.VirtualSize) {
1143           TempDebugEntryRva = Section->VirtualAddress + Section->Misc.VirtualSize;
1144         } else {
1145           TempDebugEntryRva = Section->VirtualAddress + Section->SizeOfRawData;
1146         }
1147       }
1148 
1149       if (TempDebugEntryRva != 0) {
1150         if (!(ImageContext->IsTeImage)) {
1151           ImageContext->CodeView = PeCoffLoaderImageAddress (ImageContext, TempDebugEntryRva);
1152         } else {
1153           ImageContext->CodeView = (VOID *)(
1154                       (UINTN)ImageContext->ImageAddress +
1155                       (UINTN)TempDebugEntryRva +
1156                       (UINTN)sizeof(EFI_TE_IMAGE_HEADER) -
1157                 (UINTN) TeHdr->StrippedSize
1158             );
1159         }
1160 
1161         if (ImageContext->CodeView == NULL) {
1162           ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
1163           return RETURN_LOAD_ERROR;
1164         }
1165 
1166         if (DebugEntry->RVA == 0) {
1167           Size = DebugEntry->SizeOfData;
1168           if (!(ImageContext->IsTeImage)) {
1169             Status = ImageContext->ImageRead (
1170                                     ImageContext->Handle,
1171                                     DebugEntry->FileOffset,
1172                                     &Size,
1173                                     ImageContext->CodeView
1174                                     );
1175           } else {
1176             Status = ImageContext->ImageRead (
1177                                     ImageContext->Handle,
1178                                     DebugEntry->FileOffset + sizeof (EFI_TE_IMAGE_HEADER) - TeHdr->StrippedSize,
1179                                     &Size,
1180                                     ImageContext->CodeView
1181                                     );
1182             //
1183             // Should we apply fix up to this field according to the size difference between PE and TE?
1184             // Because now we maintain TE header fields unfixed, this field will also remain as they are
1185             // in original PE image.
1186             //
1187           }
1188 
1189           if (RETURN_ERROR (Status)) {
1190             ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
1191             return RETURN_LOAD_ERROR;
1192           }
1193 
1194           DebugEntry->RVA = TempDebugEntryRva;
1195         }
1196 
1197         switch (*(UINT32 *) ImageContext->CodeView) {
1198         case CODEVIEW_SIGNATURE_NB10:
1199           ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY);
1200           break;
1201 
1202         case CODEVIEW_SIGNATURE_RSDS:
1203           ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY);
1204           break;
1205 
1206         case CODEVIEW_SIGNATURE_MTOC:
1207           ImageContext->PdbPointer = (CHAR8 *) ImageContext->CodeView + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY);
1208 
1209         default:
1210           break;
1211         }
1212       }
1213     }
1214   }
1215 
1216   return Status;
1217 }
1218 
1219 /**
1220   Returns a pointer to the PDB file name for a raw PE/COFF image that is not
1221   loaded into system memory with the PE/COFF Loader Library functions.
1222 
1223   Returns the PDB file name for the PE/COFF image specified by Pe32Data.  If
1224   the PE/COFF image specified by Pe32Data is not a valid, then NULL is
1225   returned.  If the PE/COFF image specified by Pe32Data does not contain a
1226   debug directory entry, then NULL is returned.  If the debug directory entry
1227   in the PE/COFF image specified by Pe32Data does not contain a PDB file name,
1228   then NULL is returned.
1229   If Pe32Data is NULL, then return NULL.
1230 
1231   @param  Pe32Data   Pointer to the PE/COFF image that is loaded in system
1232                      memory.
1233 
1234   @return The PDB file name for the PE/COFF image specified by Pe32Data or NULL
1235           if it cannot be retrieved.
1236 
1237 **/
1238 VOID *
1239 EFIAPI
PeCoffLoaderGetPdbPointer(IN VOID * Pe32Data)1240 PeCoffLoaderGetPdbPointer (
1241   IN VOID  *Pe32Data
1242   )
1243 {
1244   EFI_IMAGE_DOS_HEADER                  *DosHdr;
1245   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
1246   EFI_IMAGE_DATA_DIRECTORY              *DirectoryEntry;
1247   EFI_IMAGE_DEBUG_DIRECTORY_ENTRY       *DebugEntry;
1248   UINTN                                 DirCount;
1249   VOID                                  *CodeViewEntryPointer;
1250   INTN                                  TEImageAdjust;
1251   UINT32                                NumberOfRvaAndSizes;
1252   UINT16                                Magic;
1253   EFI_IMAGE_SECTION_HEADER              *SectionHeader;
1254   UINT32                                Index, Index1;
1255 
1256   if (Pe32Data == NULL) {
1257     return NULL;
1258   }
1259 
1260   TEImageAdjust       = 0;
1261   DirectoryEntry      = NULL;
1262   DebugEntry          = NULL;
1263   NumberOfRvaAndSizes = 0;
1264   Index               = 0;
1265   Index1              = 0;
1266   SectionHeader       = NULL;
1267 
1268   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
1269   if (EFI_IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
1270     //
1271     // DOS image header is present, so read the PE header after the DOS image header.
1272     //
1273     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
1274   } else {
1275     //
1276     // DOS image header is not present, so PE header is at the image base.
1277     //
1278     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
1279   }
1280 
1281   if (EFI_TE_IMAGE_HEADER_SIGNATURE == Hdr.Te->Signature) {
1282     if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) {
1283       DirectoryEntry  = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG];
1284       TEImageAdjust   = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize;
1285 
1286       //
1287       // Get the DebugEntry offset in the raw data image.
1288       //
1289       SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (Hdr.Te + 1);
1290       Index = Hdr.Te->NumberOfSections;
1291       for (Index1 = 0; Index1 < Index; Index1 ++) {
1292         if ((DirectoryEntry->VirtualAddress >= SectionHeader[Index1].VirtualAddress) &&
1293            (DirectoryEntry->VirtualAddress < (SectionHeader[Index1].VirtualAddress + SectionHeader[Index1].Misc.VirtualSize))) {
1294           DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te +
1295                         DirectoryEntry->VirtualAddress -
1296                         SectionHeader [Index1].VirtualAddress +
1297                         SectionHeader [Index1].PointerToRawData +
1298                         TEImageAdjust);
1299           break;
1300         }
1301       }
1302     }
1303   } else if (EFI_IMAGE_NT_SIGNATURE == Hdr.Pe32->Signature) {
1304     //
1305     // NOTE: We use Machine field to identify PE32/PE32+, instead of Magic.
1306     //       It is due to backward-compatibility, for some system might
1307     //       generate PE32+ image with PE32 Magic.
1308     //
1309     switch (Hdr.Pe32->FileHeader.Machine) {
1310     case EFI_IMAGE_MACHINE_IA32:
1311     case EFI_IMAGE_MACHINE_ARMT:
1312       //
1313       // Assume PE32 image with IA32 Machine field.
1314       //
1315       Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;
1316       break;
1317     case EFI_IMAGE_MACHINE_X64:
1318       //
1319       // Assume PE32+ image with X64 Machine field
1320       //
1321       Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
1322       break;
1323     default:
1324       //
1325       // For unknown Machine field, use Magic in optional Header
1326       //
1327       Magic = Hdr.Pe32->OptionalHeader.Magic;
1328     }
1329 
1330     SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
1331                        (UINT8 *) Hdr.Pe32 +
1332                        sizeof (UINT32) +
1333                        sizeof (EFI_IMAGE_FILE_HEADER) +
1334                        Hdr.Pe32->FileHeader.SizeOfOptionalHeader
1335                        );
1336     Index = Hdr.Pe32->FileHeader.NumberOfSections;
1337 
1338     if (EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC == Magic) {
1339       //
1340       // Use PE32 offset get Debug Directory Entry
1341       //
1342       NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;
1343       DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
1344     } else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
1345       //
1346       // Use PE32+ offset get Debug Directory Entry
1347       //
1348       NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;
1349       DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
1350     }
1351 
1352     if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG || DirectoryEntry->VirtualAddress == 0) {
1353       DirectoryEntry = NULL;
1354       DebugEntry = NULL;
1355     } else {
1356       //
1357       // Get the DebugEntry offset in the raw data image.
1358       //
1359       for (Index1 = 0; Index1 < Index; Index1 ++) {
1360         if ((DirectoryEntry->VirtualAddress >= SectionHeader[Index1].VirtualAddress) &&
1361            (DirectoryEntry->VirtualAddress < (SectionHeader[Index1].VirtualAddress + SectionHeader[Index1].Misc.VirtualSize))) {
1362           DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) (
1363                        (UINTN) Pe32Data +
1364                        DirectoryEntry->VirtualAddress -
1365                        SectionHeader[Index1].VirtualAddress +
1366                        SectionHeader[Index1].PointerToRawData);
1367           break;
1368         }
1369       }
1370     }
1371   } else {
1372     return NULL;
1373   }
1374 
1375   if (NULL == DebugEntry || NULL == DirectoryEntry) {
1376     return NULL;
1377   }
1378 
1379   //
1380   // Scan the directory to find the debug entry.
1381   //
1382   for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) {
1383     if (EFI_IMAGE_DEBUG_TYPE_CODEVIEW == DebugEntry->Type) {
1384       if (DebugEntry->SizeOfData > 0) {
1385         //
1386         // Get the DebugEntry offset in the raw data image.
1387         //
1388         CodeViewEntryPointer = NULL;
1389         for (Index1 = 0; Index1 < Index; Index1 ++) {
1390           if ((DebugEntry->RVA >= SectionHeader[Index1].VirtualAddress) &&
1391              (DebugEntry->RVA < (SectionHeader[Index1].VirtualAddress + SectionHeader[Index1].Misc.VirtualSize))) {
1392             CodeViewEntryPointer = (VOID *) (
1393                                    ((UINTN)Pe32Data) +
1394                                    (UINTN) DebugEntry->RVA -
1395                                    SectionHeader[Index1].VirtualAddress +
1396                                    SectionHeader[Index1].PointerToRawData +
1397                                    (UINTN)TEImageAdjust);
1398             break;
1399           }
1400         }
1401         if (Index1 >= Index) {
1402           //
1403           // Can't find CodeViewEntryPointer in raw PE/COFF image.
1404           //
1405           continue;
1406         }
1407         switch (* (UINT32 *) CodeViewEntryPointer) {
1408         case CODEVIEW_SIGNATURE_NB10:
1409           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));
1410         case CODEVIEW_SIGNATURE_RSDS:
1411           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));
1412         case CODEVIEW_SIGNATURE_MTOC:
1413           return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));
1414         default:
1415           break;
1416         }
1417       }
1418     }
1419   }
1420 
1421   return NULL;
1422 }
1423 
1424 
1425 RETURN_STATUS
1426 EFIAPI
PeCoffLoaderGetEntryPoint(IN VOID * Pe32Data,OUT VOID ** EntryPoint,OUT VOID ** BaseOfImage)1427 PeCoffLoaderGetEntryPoint (
1428   IN  VOID  *Pe32Data,
1429   OUT VOID  **EntryPoint,
1430   OUT VOID  **BaseOfImage
1431   )
1432 {
1433   EFI_IMAGE_DOS_HEADER                  *DosHdr;
1434   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;
1435 
1436   DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
1437   if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
1438     //
1439     // DOS image header is present, so read the PE header after the DOS image header.
1440     //
1441     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));
1442   } else {
1443     //
1444     // DOS image header is not present, so PE header is at the image base.
1445     //
1446     Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
1447   }
1448 
1449   //
1450   // Calculate the entry point relative to the start of the image.
1451   // AddressOfEntryPoint is common for PE32 & PE32+
1452   //
1453   if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
1454     *BaseOfImage = (VOID *)(UINTN)(Hdr.Te->ImageBase + Hdr.Te->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER));
1455     *EntryPoint = (VOID *)((UINTN)*BaseOfImage + (Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof(EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize);
1456     return RETURN_SUCCESS;
1457   } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
1458     *EntryPoint = (VOID *)(UINTN)Hdr.Pe32->OptionalHeader.AddressOfEntryPoint;
1459     if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
1460       *BaseOfImage = (VOID *)(UINTN)Hdr.Pe32->OptionalHeader.ImageBase;
1461     } else {
1462       *BaseOfImage = (VOID *)(UINTN)Hdr.Pe32Plus->OptionalHeader.ImageBase;
1463     }
1464     *EntryPoint = (VOID *)(UINTN)((UINTN)*EntryPoint + (UINTN)*BaseOfImage);
1465     return RETURN_SUCCESS;
1466   }
1467 
1468   return RETURN_UNSUPPORTED;
1469 }
1470