📄 pe.c
字号:
/*++
Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.
Module Name:
pe.c
Abstract:
Routines for managing PE32 images.
--*/
#include "loader.h"
#include "pe.h"
#include EFI_PROTOCOL_DEFINITION(Ebc)
#define MAX_PAGES_FOR_CODEVIEW 4
STATIC
EFI_STATUS
RUNTIMEFUNCTION
LoadPeRelocate (
IN LOADED_IMAGE *Image,
IMAGE_DATA_DIRECTORY *RelocDir,
IN UINT64 Adjust,
IN BOOLEAN RelocsStripped
);
VOID
RUNTIMEFUNCTION INTERNAL
ConvertPeImage (
IN LOADED_IMAGE *Image
);
STATIC
VOID *
GetPeProcAddress (
IN LOADED_IMAGE *Image,
IMAGE_NT_HEADERS *PeHdr,
IN CHAR8 *Name
);
STATIC
EFI_STATUS
ImageRead (
IN SIMPLE_READ_FILE FHand,
IN UINTN Offset,
IN OUT UINTN ReadSize,
OUT VOID *Buffer
);
STATIC
RUNTIMEFUNCTION
VOID *
ImageAddress (
IN LOADED_IMAGE *Image,
IN UINTN Address
);
STATIC
EFI_STATUS
FlushICache (
IN EFI_PHYSICAL_ADDRESS Start,
IN UINT64 Length
);
#ifdef EFI_NT_EMULATOR
EFI_STATUS
WinNtLoadAsDll (
IN CHAR8 *PdbFileName,
IN VOID **ImageEntryPoint
);
#endif
//
// Declare runtime functions
//
#ifdef RUNTIME_CODE
#pragma RUNTIME_CODE(ConvertPeImage)
#pragma RUNTIME_CODE(ImageAddress)
#endif
//
//
//
EFI_STATUS
LoadPeImage (
IN SIMPLE_READ_FILE FHand,
IN LOADED_IMAGE *Image
)
{
IMAGE_DOS_HEADER DosHdr;
IMAGE_NT_HEADERS PeHdr;
IMAGE_SECTION_HEADER *FirstSection;
IMAGE_SECTION_HEADER *Section;
UINTN Index, NumDebugEntries;
IMAGE_DATA_DIRECTORY *DirectoryEntry;
IMAGE_DEBUG_DIRECTORY_ENTRY *DebugDirectory;
EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY *CodeViewEntry;
EFI_STATUS Status;
CHAR8 *Base, *End, *MaxEnd, *CodeViewEntryEnd;
IMAGE_DATA_DIRECTORY *RelocDir;
IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
EFI_PHYSICAL_ADDRESS BaseAddress;
EFI_EBC_PROTOCOL *EbcProtocol;
BOOLEAN RelocsStripped;
#ifdef EFI_NT_EMULATOR
VOID *DllEntryPoint;
#endif
ZeroMem (&DosHdr, sizeof(DosHdr));
ZeroMem (&PeHdr, sizeof(PeHdr));
//
// Read image headers
//
ImageRead (FHand, 0, sizeof(IMAGE_DOS_HEADER), &DosHdr);
if (DosHdr.e_magic != IMAGE_DOS_SIGNATURE) {
DEBUG ((D_LOAD, "LoadPeImage: Dos header signature not found\n"));
return EFI_UNSUPPORTED;
}
ImageRead (FHand, DosHdr.e_lfanew, sizeof(IMAGE_NT_HEADERS), &PeHdr);
//
// Make sure it has a normal PE32 image header
//
if (PeHdr.Signature != IMAGE_NT_SIGNATURE) {
DEBUG ((D_LOAD, "LoadPeImage: PE image header signature not found\n"));
return EFI_UNSUPPORTED;
}
//
// Set the image subsystem type
//
Status = SetImageType (Image, PeHdr.OptionalHeader.Subsystem);
if (EFI_ERROR(Status)) {
DEBUG ((D_LOAD, "LoadPeImage: Subsystem type not known\n"));
return Status;
}
//
// Verify machine type
//
Status = CheckImageMachineType (PeHdr.FileHeader.Machine);
if (EFI_ERROR(Status)) {
DEBUG ((D_LOAD, "LoadPeImage: Incorrect machine type\n"));
return Status;
}
//
// Compute the amount of memory needed to load the image and
// allocate it. This will include all sections plus the codeview debug info.
// Since the codeview info is actually outside of the image, we calculate
// its size seperately and add it to the total.
//
// Memory starts off as data
//
Image->NumberOfPages = EFI_SIZE_TO_PAGES (PeHdr.OptionalHeader.SizeOfImage + PeHdr.OptionalHeader.SectionAlignment) + MAX_PAGES_FOR_CODEVIEW;
(UINTN) (Image->ImageBasePage) = (UINTN)(PeHdr.OptionalHeader.ImageBase);
Image->Info.ImageSize = ((Image->NumberOfPages) << EFI_PAGE_SHIFT);
Image->ImageBase = (CHAR8 *) Image->ImageBasePage;
Image->ImageEof = Image->ImageBase + Image->Info.ImageSize;
Image->ImageAdjust = Image->ImageBase;
//
// Check to see if relocations are available in this image.
//
RelocBase = NULL;
RelocBaseEnd = NULL;
if (IMAGE_DIRECTORY_ENTRY_BASERELOC < PeHdr.OptionalHeader.NumberOfRvaAndSizes) {
RelocDir = &(PeHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
RelocBase = (VOID *)RelocDir->VirtualAddress;
RelocBaseEnd = (VOID *)(RelocDir->VirtualAddress + RelocDir->Size);
}
//
// Load the image based on whether or not relocation have been stripped
// from it.
//
RelocsStripped =
(PeHdr.FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) ? TRUE : FALSE;
if (RelocsStripped) {
//
// Relocations have been stripped, so attempt to load the image at
// its linked address.
//
Status = AllocatePages (
AllocateAddress,
Image->Info.ImageCodeType,
Image->NumberOfPages,
&Image->ImageBasePage
);
Image->Info.ImageBase = (VOID *) Image->ImageBasePage;
Image->ImageBase = (CHAR8 *) Image->ImageBasePage;
} else {
//
// Relocations have not been stripped so attempt to load the image
// near top of memory.
//
BaseAddress = MAX_ADDRESS;
DEBUG ((D_LOAD, "LoadPeImage: Attempting to load image at NON linker assigned address\n"));
Status = AllocatePages (
AllocateMaxAddress,
Image->Info.ImageCodeType,
Image->NumberOfPages,
&BaseAddress
);
Image->ImageBasePage = (BaseAddress + PeHdr.OptionalHeader.SectionAlignment - 1) &
~((UINTN)PeHdr.OptionalHeader.SectionAlignment - 1);
Image->Info.ImageBase = (VOID *) Image->ImageBasePage;
Image->ImageBase = (CHAR8 *) Image->ImageBasePage;
Image->ImageBasePage = BaseAddress;
}
if (EFI_ERROR(Status)) {
Image->ImageBasePage = 0;
return Status;
}
DEBUG((D_LOAD, "LoadPe: new image base %lx\n", Image->ImageBasePage));
Image->ImageEof = (CHAR8 *)(Image->ImageBasePage + Image->Info.ImageSize);
Image->ImageAdjust = Image->ImageBase;
//
// Copy the machine type from the context to the image private data. This
// is needed during image unload to know if we should call an EBC protocol
// to unload the image.
//
Image->Machine = PeHdr.FileHeader.Machine;
//
// Copy the Image header to the base location
//
Status = ImageRead (
FHand,
0,
PeHdr.OptionalHeader.SizeOfHeaders,
Image->ImageBase
);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Load each section of the image
//
// BUGBUG: change this to use the in memory copy
FirstSection = (IMAGE_SECTION_HEADER *) (
Image->ImageBase +
DosHdr.e_lfanew +
sizeof(PeHdr) +
PeHdr.FileHeader.SizeOfOptionalHeader -
sizeof (IMAGE_OPTIONAL_HEADER)
);
Section = FirstSection;
for (Index=0, MaxEnd = NULL; Index < PeHdr.FileHeader.NumberOfSections; Index += 1) {
//
// Compute sections address
//
Base = ImageAddress(Image, Section->VirtualAddress);
End = ImageAddress(Image, Section->VirtualAddress + Section->Misc.VirtualSize - 1);
if (End > MaxEnd) {
MaxEnd = End;
}
if (EFI_ERROR(Status) || !Base || !End) {
DEBUG((D_LOAD|D_ERROR, "LoadPe: Section %d was not loaded\n", Index));
return EFI_LOAD_ERROR;
}
DEBUG((D_LOAD, "LoadPe: Section %d, loaded at %x\n", Index, Base));
//
// Read the section
//
if (Section->SizeOfRawData) {
Status = ImageRead (FHand, Section->PointerToRawData, Section->SizeOfRawData, Base);
if (EFI_ERROR(Status)) {
return Status;
}
}
//
// If raw size is less then virt size, zero fill the remaining
//
if (Section->SizeOfRawData < Section->Misc.VirtualSize) {
ZeroMem (
Base + Section->SizeOfRawData,
Section->Misc.VirtualSize - Section->SizeOfRawData
);
}
//
// Next Section
//
Section += 1;
}
//
// Apply relocations. Call the relocate function even if there are no
// relocations because that function performs additional checks to guarantee
// that it is ok to not have them.
//
if (IMAGE_DIRECTORY_ENTRY_BASERELOC < PeHdr.OptionalHeader.NumberOfRvaAndSizes) {
Status = LoadPeRelocate (
Image,
&PeHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC],
(UINT64) Image->ImageBase - PeHdr.OptionalHeader.ImageBase,
RelocsStripped
);
} else {
Status = LoadPeRelocate (
Image,
NULL,
(UINT64) Image->ImageBase - PeHdr.OptionalHeader.ImageBase,
RelocsStripped
);
}
if (EFI_ERROR(Status)) {
return Status;
}
//
// The codeview information can be located nearly anywhere in the file.
// It is found by looking at the members of the debug directory for a codeview
// record. If there is a codeview record that contains an RVA that is not 0,
// then the codeview record is contained within a section and has been loaded
// at the RVA. If the RVA is zero and the FileOffset is not zero, then the
// codeview record is outside of all the sections. We pre-allocated an extra
// 4 pages for this contingency (a 16K PDB path seems ridiculous). We'll read
// the codeview record from the file image into this extra space. Whichever
// is the case, we'll free any unused pages at the end of the image allocatoin.
//
CodeViewEntry = NULL;
if (IMAGE_DIRECTORY_ENTRY_DEBUG < PeHdr.OptionalHeader.NumberOfRvaAndSizes) {
DirectoryEntry = (VOID *)&(PeHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]);
DebugDirectory = (VOID *) (Image->ImageBase + DirectoryEntry->VirtualAddress);
NumDebugEntries = DirectoryEntry->Size / sizeof (IMAGE_DEBUG_DIRECTORY_ENTRY);
for (Index = 0; Index < NumDebugEntries; Index++) {
if (DebugDirectory[Index].Type == IMAGE_DEBUG_TYPE_CODEVIEW) {
if (DebugDirectory[Index].RVA == 0) {
if (DebugDirectory[Index].FileOffset != 0 && MAX_PAGES_FOR_CODEVIEW >= EFI_SIZE_TO_PAGES (DebugDirectory[Index].SizeOfData)) {
//
// The PDB file path is outside the normal image, so copy it into
// the extra space we allocated beyond the end of the image
//
CodeViewEntry = (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY *)(Image->ImageBase + PeHdr.OptionalHeader.SizeOfImage);
CodeViewEntryEnd = Image->ImageBase + PeHdr.OptionalHeader.SizeOfImage + DebugDirectory[Index].SizeOfData;
Status = ImageRead (FHand,
DebugDirectory[Index].FileOffset,
DebugDirectory[Index].SizeOfData,
CodeViewEntry);
if (EFI_ERROR (Status)) {
return (Status);
}
DebugDirectory[Index].RVA = (UINT32)((UINTN) CodeViewEntry - (UINTN) Image->ImageBase);
}
} else {
CodeViewEntry = (VOID *) ((UINTN) Image->ImageBase + DebugDirectory[Index].RVA);
CodeViewEntryEnd = Image->ImageBase + PeHdr.OptionalHeader.SizeOfImage;
}
break;
}
}
}
//
// We allocated extra space for the image in case we needed to copy in
// the PDB path. We need to free any pages that were not used for this
// purpose and adjust the Image data fields to reflect the new size...
//
// BUGBUG : However, freeing the extra pages fragments the memory map with
// a small chunk of free memory next to every image. For now, don't free the
// extra space to keep the memory map from fragmenting.
//
// if (CodeViewEntry) {
// Image->NumberOfPages = EFI_SIZE_TO_PAGES((UINTN)(CodeViewEntryEnd - Image->ImageBasePage)) ;
// Image->Info.ImageSize = ((Image->NumberOfPages) << EFI_PAGE_SHIFT);
// BS->FreePages (
// (EFI_PHYSICAL_ADDRESS)Image->ImageBasePage + Image->Info.ImageSize,
// EFI_SIZE_TO_PAGES((UINTN)Image->ImageEof - ((UINTN)Image->ImageBasePage + (UINTN)Image->Info.ImageSize))
// );
// Image->ImageEof = (CHAR8 *)(Image->ImageBasePage + Image->Info.ImageSize);
// }
//
// Use exported EFI specific interface if present, else use the image's entry point
//
DEBUG((D_LOAD|D_WARN, "LoadPe: using PE image entry point\n"));
Image->EntryPoint = (EFI_IMAGE_ENTRY_POINT)
(ImageAddress(
Image,
PeHdr.OptionalHeader.AddressOfEntryPoint
));
DEBUG((D_LOAD, "LoadPe: Image Entry Point %016x [%016x]\n", Image->EntryPoint, *(UINT64 *)Image->EntryPoint));
if (PL->FlushCache) {
//
// On some machines I-caches do not maintain coherency so you need to
// flush the data that we loaded out of any D-cache.
//
PL->FlushCache (Image->ImageBase, MaxEnd);
DEBUG((D_LOAD, "LoadPe: Cache Flush Done\n"));
}
EbcProtocol = NULL;
if (Image->Machine == EFI_IMAGE_MACHINE_EBC) {
//
// Locate the EBC interpreter protocol
//
Status = BS->LocateProtocol (&gEfiEbcProtocolGuid, NULL, &EbcProtocol);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Register a callback for flushing the instruction cache so that created
// thunks can be flushed.
//
Status = EbcProtocol->RegisterICacheFlush (EbcProtocol, FlushICache);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Create a thunk for the image's entry point. This will be the new
// entry point for the image.
//
Status = EbcProtocol->CreateThunk ( EbcProtocol,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -