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