blob: e3641123e11c4f2a15875b8492abd6579b356623 [file] [log] [blame]
/*++
Copyright (c) 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Module Name:
PeLoader.c
Abstract:
Revision History:
--*/
#include "EfiLdr.h"
#include "Debug.h"
#include "Support.h"
EFI_STATUS
EfiLdrPeCoffLoadPeRelocate (
IN EFILDR_LOADED_IMAGE *Image,
IN EFI_IMAGE_DATA_DIRECTORY *RelocDir,
IN UINTN Adjust,
IN UINTN *NumberOfMemoryMapEntries,
IN EFI_MEMORY_DESCRIPTOR *EfiMemoryDescriptor
);
EFI_STATUS
EfiLdrPeCoffImageRead (
IN VOID *FHand,
IN UINTN Offset,
IN OUT UINTN ReadSize,
OUT VOID *Buffer
);
VOID *
EfiLdrPeCoffImageAddress (
IN EFILDR_LOADED_IMAGE *Image,
IN UINTN Address
);
EFI_STATUS
EfiLdrPeCoffSetImageType (
IN OUT EFILDR_LOADED_IMAGE *Image,
IN UINTN ImageType
);
EFI_STATUS
EfiLdrPeCoffCheckImageMachineType (
IN UINT16 MachineType
);
EFI_STATUS
EfiLdrGetPeImageInfo (
IN VOID *FHand,
OUT UINT64 *ImageBase,
OUT UINT32 *ImageSize
)
{
EFI_STATUS Status;
EFI_IMAGE_DOS_HEADER DosHdr;
EFI_IMAGE_OPTIONAL_HEADER_UNION PeHdr;
ZeroMem (&DosHdr, sizeof(DosHdr));
ZeroMem (&PeHdr, sizeof(PeHdr));
//
// Read image headers
//
EfiLdrPeCoffImageRead (FHand, 0, sizeof(DosHdr), &DosHdr);
if (DosHdr.e_magic != EFI_IMAGE_DOS_SIGNATURE) {
return EFI_UNSUPPORTED;
}
EfiLdrPeCoffImageRead (FHand, DosHdr.e_lfanew, sizeof(PeHdr), &PeHdr);
if (PeHdr.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
return EFI_UNSUPPORTED;
}
//
// Verify machine type
//
Status = EfiLdrPeCoffCheckImageMachineType (PeHdr.Pe32.FileHeader.Machine);
if (EFI_ERROR(Status)) {
return Status;
}
if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
*ImageBase = (UINT32)PeHdr.Pe32.OptionalHeader.ImageBase;
} else if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
*ImageBase = PeHdr.Pe32Plus.OptionalHeader.ImageBase;
} else {
return EFI_UNSUPPORTED;
}
*ImageSize = PeHdr.Pe32.OptionalHeader.SizeOfImage;
return EFI_SUCCESS;
}
EFI_STATUS
EfiLdrPeCoffLoadPeImage (
IN VOID *FHand,
IN EFILDR_LOADED_IMAGE *Image,
IN UINTN *NumberOfMemoryMapEntries,
IN EFI_MEMORY_DESCRIPTOR *EfiMemoryDescriptor
)
{
EFI_IMAGE_DOS_HEADER DosHdr;
EFI_IMAGE_OPTIONAL_HEADER_UNION PeHdr;
EFI_IMAGE_SECTION_HEADER *FirstSection;
EFI_IMAGE_SECTION_HEADER *Section;
UINTN Index;
EFI_STATUS Status;
UINT8 *Base;
UINT8 *End;
EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;
UINTN DirCount;
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY TempDebugEntry;
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;
UINTN CodeViewSize;
UINTN CodeViewOffset;
UINTN CodeViewFileOffset;
UINTN OptionalHeaderSize;
UINTN PeHeaderSize;
UINT32 NumberOfRvaAndSizes;
EFI_IMAGE_DATA_DIRECTORY *DataDirectory;
UINT64 ImageBase;
CHAR8 PrintBuffer[256];
ZeroMem (&DosHdr, sizeof(DosHdr));
ZeroMem (&PeHdr, sizeof(PeHdr));
//
// Read image headers
//
EfiLdrPeCoffImageRead (FHand, 0, sizeof(DosHdr), &DosHdr);
if (DosHdr.e_magic != EFI_IMAGE_DOS_SIGNATURE) {
AsciiSPrint (PrintBuffer, 256, "PeCoffLoadPeImage: Dos header signature not found\n");
PrintString (PrintBuffer);
PrintHeader ('F');
return EFI_UNSUPPORTED;
}
EfiLdrPeCoffImageRead (FHand, DosHdr.e_lfanew, sizeof(PeHdr), &PeHdr);
if (PeHdr.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
AsciiSPrint (PrintBuffer, 256, "PeCoffLoadPeImage: PE image header signature not found\n");
PrintString (PrintBuffer);
PrintHeader ('G');
return EFI_UNSUPPORTED;
}
//
// Set the image subsystem type
//
Status = EfiLdrPeCoffSetImageType (Image, PeHdr.Pe32.OptionalHeader.Subsystem);
if (EFI_ERROR(Status)) {
AsciiSPrint (PrintBuffer, 256, "PeCoffLoadPeImage: Subsystem type not known\n");
PrintString (PrintBuffer);
PrintHeader ('H');
return Status;
}
//
// Verify machine type
//
Status = EfiLdrPeCoffCheckImageMachineType (PeHdr.Pe32.FileHeader.Machine);
if (EFI_ERROR(Status)) {
AsciiSPrint (PrintBuffer, 256, "PeCoffLoadPeImage: Incorrect machine type\n");
PrintString (PrintBuffer);
PrintHeader ('I');
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
//
CodeViewSize = 0;
CodeViewFileOffset = 0;
if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(PeHdr.Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
} else if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(PeHdr.Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
} else {
return EFI_UNSUPPORTED;
}
for (DirCount = 0;
(DirCount < DirectoryEntry->Size / sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY)) && (CodeViewSize == 0);
DirCount++) {
Status = EfiLdrPeCoffImageRead (
FHand,
DirectoryEntry->VirtualAddress + DirCount * sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY),
sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY),
&TempDebugEntry
);
if (!EFI_ERROR (Status)) {
if (TempDebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
CodeViewSize = TempDebugEntry.SizeOfData;
CodeViewFileOffset = TempDebugEntry.FileOffset;
}
}
}
CodeViewOffset = PeHdr.Pe32.OptionalHeader.SizeOfImage + PeHdr.Pe32.OptionalHeader.SectionAlignment;
Image->NoPages = EFI_SIZE_TO_PAGES (CodeViewOffset + CodeViewSize);
//
// Compute the amount of memory needed to load the image and
// allocate it. Memory starts off as data
//
Image->ImageBasePage = (EFI_PHYSICAL_ADDRESS)FindSpace (Image->NoPages, NumberOfMemoryMapEntries, EfiMemoryDescriptor, EfiRuntimeServicesCode, EFI_MEMORY_WB);
if (Image->ImageBasePage == 0) {
return EFI_OUT_OF_RESOURCES;
}
if (EFI_ERROR(Status)) {
PrintHeader ('J');
return Status;
}
AsciiSPrint (PrintBuffer, 256, "LoadPe: new image base %lx\n", Image->ImageBasePage);
PrintString (PrintBuffer);
Image->Info.ImageBase = (VOID *)(UINTN)Image->ImageBasePage;
Image->Info.ImageSize = (Image->NoPages << EFI_PAGE_SHIFT) - 1;
Image->ImageBase = (UINT8 *)(UINTN)Image->ImageBasePage;
Image->ImageEof = Image->ImageBase + Image->Info.ImageSize;
Image->ImageAdjust = Image->ImageBase;
//
// Copy the Image header to the base location
//
Status = EfiLdrPeCoffImageRead (
FHand,
0,
PeHdr.Pe32.OptionalHeader.SizeOfHeaders,
Image->ImageBase
);
if (EFI_ERROR(Status)) {
PrintHeader ('K');
return Status;
}
//
// Load each directory of the image into memory...
// Save the address of the Debug directory for later
//
if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
NumberOfRvaAndSizes = PeHdr.Pe32.OptionalHeader.NumberOfRvaAndSizes;
DataDirectory = PeHdr.Pe32.OptionalHeader.DataDirectory;
} else {
NumberOfRvaAndSizes = PeHdr.Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
DataDirectory = PeHdr.Pe32Plus.OptionalHeader.DataDirectory;
}
DebugEntry = NULL;
for (Index = 0; Index < NumberOfRvaAndSizes; Index++) {
if ((DataDirectory[Index].VirtualAddress != 0) && (DataDirectory[Index].Size != 0)) {
Status = EfiLdrPeCoffImageRead (
FHand,
DataDirectory[Index].VirtualAddress,
DataDirectory[Index].Size,
Image->ImageBase + DataDirectory[Index].VirtualAddress
);
if (EFI_ERROR(Status)) {
return Status;
}
if (Index == EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {
DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) (Image->ImageBase + DataDirectory[Index].VirtualAddress);
}
}
}
//
// Load each section of the image
//
// BUGBUG: change this to use the in memory copy
if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
OptionalHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
PeHeaderSize = sizeof(EFI_IMAGE_NT_HEADERS32);
} else {
OptionalHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64);
PeHeaderSize = sizeof(EFI_IMAGE_NT_HEADERS64);
}
FirstSection = (EFI_IMAGE_SECTION_HEADER *) (
Image->ImageBase +
DosHdr.e_lfanew +
PeHeaderSize +
PeHdr.Pe32.FileHeader.SizeOfOptionalHeader -
OptionalHeaderSize
);
Section = FirstSection;
for (Index=0; Index < PeHdr.Pe32.FileHeader.NumberOfSections; Index += 1) {
//
// Compute sections address
//
Base = EfiLdrPeCoffImageAddress (Image, (UINTN)Section->VirtualAddress);
End = EfiLdrPeCoffImageAddress (Image, (UINTN)(Section->VirtualAddress + Section->Misc.VirtualSize));
if (EFI_ERROR(Status) || !Base || !End) {
// DEBUG((D_LOAD|D_ERROR, "LoadPe: Section %d was not loaded\n", Index));
PrintHeader ('L');
return EFI_LOAD_ERROR;
}
// DEBUG((D_LOAD, "LoadPe: Section %d, loaded at %x\n", Index, Base));
//
// Read the section
//
if (Section->SizeOfRawData) {
Status = EfiLdrPeCoffImageRead (FHand, Section->PointerToRawData, Section->SizeOfRawData, Base);
if (EFI_ERROR(Status)) {
PrintHeader ('M');
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;
}
//
// Copy in CodeView information if it exists
//
if (CodeViewSize != 0) {
Status = EfiLdrPeCoffImageRead (FHand, CodeViewFileOffset, CodeViewSize, Image->ImageBase + CodeViewOffset);
DebugEntry->RVA = (UINT32) (CodeViewOffset);
}
//
// Apply relocations only if needed
//
if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
ImageBase = (UINT64)PeHdr.Pe32.OptionalHeader.ImageBase;
} else {
ImageBase = PeHdr.Pe32Plus.OptionalHeader.ImageBase;
}
if ((UINTN)(Image->ImageBase) != (UINTN) (ImageBase)) {
Status = EfiLdrPeCoffLoadPeRelocate (
Image,
&DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC],
(UINTN) Image->ImageBase - (UINTN)ImageBase,
NumberOfMemoryMapEntries,
EfiMemoryDescriptor
);
if (EFI_ERROR(Status)) {
PrintHeader ('N');
return Status;
}
}
//
// Use exported EFI specific interface if present, else use the image's entry point
//
Image->EntryPoint = (EFI_IMAGE_ENTRY_POINT)(UINTN)
(EfiLdrPeCoffImageAddress(
Image,
PeHdr.Pe32.OptionalHeader.AddressOfEntryPoint
));
return Status;
}
EFI_STATUS
EfiLdrPeCoffLoadPeRelocate (
IN EFILDR_LOADED_IMAGE *Image,
IN EFI_IMAGE_DATA_DIRECTORY *RelocDir,
IN UINTN Adjust,
IN UINTN *NumberOfMemoryMapEntries,
IN EFI_MEMORY_DESCRIPTOR *EfiMemoryDescriptor
)
{
EFI_IMAGE_BASE_RELOCATION *RelocBase;
EFI_IMAGE_BASE_RELOCATION *RelocBaseEnd;
UINT16 *Reloc;
UINT16 *RelocEnd;
UINT8 *Fixup;
UINT8 *FixupBase;
UINT16 *F16;
UINT32 *F32;
UINT64 *F64;
UINT8 *FixupData;
UINTN NoFixupPages;
//
// Find the relocation block
//
RelocBase = EfiLdrPeCoffImageAddress (Image, RelocDir->VirtualAddress);
RelocBaseEnd = EfiLdrPeCoffImageAddress (Image, RelocDir->VirtualAddress + RelocDir->Size);
if (!RelocBase || !RelocBaseEnd) {
PrintHeader ('O');
return EFI_LOAD_ERROR;
}
NoFixupPages = EFI_SIZE_TO_PAGES(RelocDir->Size / sizeof(UINT16) * sizeof(UINTN));
Image->FixupData = (UINT8*) FindSpace (NoFixupPages, NumberOfMemoryMapEntries, EfiMemoryDescriptor, EfiRuntimeServicesData, EFI_MEMORY_WB);
if (Image->FixupData == 0) {
return EFI_OUT_OF_RESOURCES;
}
//
// Run the whole relocation block
//
FixupData = Image->FixupData;
while (RelocBase < RelocBaseEnd) {
Reloc = (UINT16 *) ((UINT8 *) RelocBase + sizeof(EFI_IMAGE_BASE_RELOCATION));
RelocEnd = (UINT16 *) ((UINT8 *) RelocBase + RelocBase->SizeOfBlock);
FixupBase = EfiLdrPeCoffImageAddress (Image, RelocBase->VirtualAddress);
if ((UINT8 *) RelocEnd < Image->ImageBase || (UINT8 *) RelocEnd > Image->ImageEof) {
PrintHeader ('P');
return EFI_LOAD_ERROR;
}
//
// Run this relocation record
//
while (Reloc < RelocEnd) {
Fixup = FixupBase + (*Reloc & 0xFFF);
switch ((*Reloc) >> 12) {
case EFI_IMAGE_REL_BASED_ABSOLUTE:
break;
case EFI_IMAGE_REL_BASED_HIGH:
F16 = (UINT16 *) Fixup;
*F16 = (UINT16) (*F16 + (UINT16)(((UINT32)Adjust) >> 16));
if (FixupData != NULL) {
*(UINT16 *) FixupData = *F16;
FixupData = FixupData + sizeof(UINT16);
}
break;
case EFI_IMAGE_REL_BASED_LOW:
F16 = (UINT16 *) Fixup;
*F16 = (UINT16) (*F16 + (UINT16) Adjust);
if (FixupData != NULL) {
*(UINT16 *) FixupData = *F16;
FixupData = FixupData + sizeof(UINT16);
}
break;
case EFI_IMAGE_REL_BASED_HIGHLOW:
F32 = (UINT32 *) Fixup;
*F32 = *F32 + (UINT32) Adjust;
if (FixupData != NULL) {
FixupData = ALIGN_POINTER(FixupData, sizeof(UINT32));
*(UINT32 *) FixupData = *F32;
FixupData = FixupData + sizeof(UINT32);
}
break;
case EFI_IMAGE_REL_BASED_DIR64:
F64 = (UINT64 *) Fixup;
*F64 = *F64 + (UINT64) Adjust;
if (FixupData != NULL) {
FixupData = ALIGN_POINTER(FixupData, sizeof(UINT64));
*(UINT64 *) FixupData = *F64;
FixupData = FixupData + sizeof(UINT64);
}
break;
case EFI_IMAGE_REL_BASED_HIGHADJ:
CpuDeadLoop(); // BUGBUG: not done
break;
default:
// DEBUG((D_LOAD|D_ERROR, "PeRelocate: unknown fixed type\n"));
PrintHeader ('Q');
CpuDeadLoop();
return EFI_LOAD_ERROR;
}
// Next reloc record
Reloc += 1;
}
// next reloc block
RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
}
//
// Add Fixup data to whole Image (assume Fixup data just below the image), so that there is no hole in the descriptor.
// Because only NoPages or ImageBasePage will be used in EfiLoader(), we update these 2 fields.
//
Image->NoPages += NoFixupPages;
Image->ImageBasePage -= (NoFixupPages << EFI_PAGE_SHIFT);
return EFI_SUCCESS;
}
EFI_STATUS
EfiLdrPeCoffImageRead (
IN VOID *FHand,
IN UINTN Offset,
IN OUT UINTN ReadSize,
OUT VOID *Buffer
)
{
CopyMem (Buffer, (VOID *)((UINTN)FHand + Offset), ReadSize);
return EFI_SUCCESS;
}
VOID *
EfiLdrPeCoffImageAddress (
IN EFILDR_LOADED_IMAGE *Image,
IN UINTN Address
)
{
UINT8 *FixedAddress;
FixedAddress = Image->ImageAdjust + Address;
if ((FixedAddress < Image->ImageBase) || (FixedAddress > Image->ImageEof)) {
// DEBUG((D_LOAD|D_ERROR, "PeCoffImageAddress: pointer is outside of image\n"));
FixedAddress = NULL;
}
// DEBUG((
// D_LOAD,
// "PeCoffImageAddress: ImageBase %x, ImageEof %x, Address %x, FixedAddress %x\n",
// Image->ImageBase,
// Image->ImageEof,
// Address,
// FixedAddress
// ));
return FixedAddress;
}
EFI_STATUS
EfiLdrPeCoffSetImageType (
IN OUT EFILDR_LOADED_IMAGE *Image,
IN UINTN ImageType
)
{
EFI_MEMORY_TYPE CodeType;
EFI_MEMORY_TYPE DataType;
switch (ImageType) {
case EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION:
CodeType = EfiLoaderCode;
DataType = EfiLoaderData;
break;
case EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
CodeType = EfiBootServicesCode;
DataType = EfiBootServicesData;
break;
case EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
CodeType = EfiRuntimeServicesCode;
DataType = EfiRuntimeServicesData;
break;
default:
return EFI_INVALID_PARAMETER;
}
Image->Type = ImageType;
Image->Info.ImageCodeType = CodeType;
Image->Info.ImageDataType = DataType;
return EFI_SUCCESS;
}
EFI_STATUS
EfiLdrPeCoffCheckImageMachineType (
IN UINT16 MachineType
)
{
EFI_STATUS Status;
Status = EFI_UNSUPPORTED;
#ifdef MDE_CPU_IA32
if (MachineType == EFI_IMAGE_MACHINE_IA32) {
Status = EFI_SUCCESS;
}
#endif
#ifdef MDE_CPU_X64
if (MachineType == EFI_IMAGE_MACHINE_X64) {
Status = EFI_SUCCESS;
}
#endif
#ifdef MDE_CPU_IPF
if (MachineType == EFI_IMAGE_MACHINE_IA64) {
Status = EFI_SUCCESS;
}
#endif
return Status;
}