mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
1178 lines
37 KiB
C
1178 lines
37 KiB
C
/** @file
|
|
Base PE/COFF loader supports loading any PE32/PE32+ or TE image.
|
|
|
|
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
**/
|
|
|
|
#include <Base.h>
|
|
|
|
#include <IndustryStandard/OcPeImage.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcGuardLib.h>
|
|
#include <Library/OcPeCoffLib.h>
|
|
#include <Library/PcdLib.h>
|
|
|
|
#define IS_POW2(v) ((v) != 0U && ((v) & ((v) - 1U)) == 0U)
|
|
#define IS_ALIGNED(v, a) (((v) & ((a) - 1U)) == 0U)
|
|
|
|
#define IMAGE_RELOC_TYPE(Relocation) ((Relocation) >> 12U)
|
|
#define IMAGE_RELOC_OFFSET(Relocation) ((Relocation) & 0x0FFFU)
|
|
|
|
#define IMAGE_IS_EFI_SUBYSYSTEM(Subsystem) \
|
|
((Subsystem) >= EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION && \
|
|
(Subsystem) <= EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER)
|
|
|
|
#define IMAGE_RELOC_TYPE_SUPPORTED(Type) \
|
|
((Type) == EFI_IMAGE_REL_BASED_ABSOLUTE) || \
|
|
((Type) == EFI_IMAGE_REL_BASED_HIGHLOW) || \
|
|
((Type) == EFI_IMAGE_REL_BASED_DIR64))
|
|
|
|
#define IMAGE_RELOC_SUPPORTED(Reloc) \
|
|
IMAGE_RELOC_TYPE_SUPPORTED (IMAGE_RELOC_TYPE (Reloc))
|
|
|
|
STATIC
|
|
IMAGE_STATUS
|
|
InternalVerifySections (
|
|
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINTN FileSize,
|
|
IN UINT32 SectionsOffset,
|
|
IN UINT16 NumberOfSections,
|
|
OUT UINT32 *BottomAddress,
|
|
OUT UINT32 *TopAddress
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
UINT32 NextSectRva;
|
|
UINT32 SectRvaPrevEnd;
|
|
UINT32 SectRvaEnd;
|
|
UINT32 SectRawEnd;
|
|
UINT16 SectIndex;
|
|
CONST EFI_IMAGE_SECTION_HEADER *Sections;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Context->SizeOfHeaders >= Context->TeStrippedOffset);
|
|
ASSERT (IS_POW2 (Context->SectionAlignment));
|
|
ASSERT (NumberOfSections > 0);
|
|
ASSERT (BottomAddress != NULL);
|
|
ASSERT (TopAddress != NULL);
|
|
|
|
Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + SectionsOffset
|
|
);
|
|
|
|
if (Sections[0].VirtualAddress == 0) {
|
|
if (Context->ImageType == ImageTypeTe) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
NextSectRva = 0;
|
|
} else {
|
|
// TODO: Disallow PCD
|
|
|
|
Result = OcOverflowAlignUpU32 (
|
|
Context->SizeOfHeaders,
|
|
Context->SectionAlignment,
|
|
&NextSectRva
|
|
);
|
|
if (Result) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
SectRvaPrevEnd = NextSectRva;
|
|
|
|
*BottomAddress = NextSectRva;
|
|
|
|
for (SectIndex = 0; SectIndex < NumberOfSections; ++SectIndex) {
|
|
if (Sections[SectIndex].SizeOfRawData > 0) {
|
|
if (Context->TeStrippedOffset > Sections[SectIndex].PointerToRawData) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Result = OcOverflowAddU32 (
|
|
Sections[SectIndex].PointerToRawData,
|
|
Sections[SectIndex].SizeOfRawData,
|
|
&SectRawEnd
|
|
);
|
|
if (Result) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
if ((SectRawEnd - Context->TeStrippedOffset) > FileSize) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
Result = OcOverflowAddU32 (
|
|
Sections[SectIndex].VirtualAddress,
|
|
Sections[SectIndex].VirtualSize,
|
|
&SectRvaEnd
|
|
);
|
|
if (Result) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// FIXME: Misaligned images should be handled with a PCD.
|
|
//
|
|
if (Sections[SectIndex].VirtualAddress < SectRvaPrevEnd) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
SectRvaPrevEnd = SectRvaEnd;
|
|
|
|
//
|
|
// Sections must have virtual addresses adjacent in ascending order.
|
|
// SectionSize does not need to be aligned, so align the result.
|
|
//
|
|
Result = OcOverflowAlignUpU32 (
|
|
SectRvaEnd,
|
|
Context->SectionAlignment,
|
|
&NextSectRva
|
|
);
|
|
|
|
if (Result) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
*TopAddress = NextSectRva;
|
|
|
|
return IMAGE_ERROR_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
IMAGE_STATUS
|
|
InternalValidateRelocInfo (
|
|
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINT32 BottomAddress
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
UINT32 SectRvaEnd;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (BottomAddress == 0 || BottomAddress == ALIGN_VALUE (Context->SizeOfHeaders, Context->SectionAlignment));
|
|
//
|
|
// If the relocations have not been stripped, sanitize their directory.
|
|
//
|
|
if (!Context->RelocsStripped) {
|
|
if (sizeof (EFI_IMAGE_BASE_RELOCATION) > Context->RelocDirSize) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Result = OcOverflowAddU32 (
|
|
Context->RelocDirRva,
|
|
Context->RelocDirSize,
|
|
&SectRvaEnd
|
|
);
|
|
//
|
|
// Ensure no overflow has occured.
|
|
//
|
|
if (Result) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure the directory does not overlap with the image header.
|
|
//
|
|
if (BottomAddress > Context->RelocDirRva) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure the directory is within the bounds of the image's virtual space.
|
|
//
|
|
if (SectRvaEnd > Context->SizeOfImage) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure the directory's start is propery aligned.
|
|
//
|
|
if (!OC_TYPE_ALIGNED (EFI_IMAGE_BASE_RELOCATION, Context->RelocDirRva)) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
} else {
|
|
if (!IS_ALIGNED (Context->ImageBase, Context->SectionAlignment)) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure DXE Runtime Driver can be relocated.
|
|
//
|
|
if (Context->Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
}
|
|
|
|
return IMAGE_ERROR_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
IMAGE_STATUS
|
|
InternalInitializeTe (
|
|
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINTN FileSize
|
|
)
|
|
{
|
|
IMAGE_STATUS Status;
|
|
BOOLEAN Result;
|
|
CONST EFI_TE_IMAGE_HEADER *TeHdr;
|
|
UINT32 BottomAddress;
|
|
UINT32 SizeOfImage;
|
|
UINT32 SectionsOffset;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Context->ExeHdrOffset <= FileSize);
|
|
ASSERT (FileSize - Context->ExeHdrOffset >= sizeof (EFI_TE_IMAGE_HEADER));
|
|
ASSERT (OC_TYPE_ALIGNED (EFI_TE_IMAGE_HEADER, Context->ExeHdrOffset));
|
|
|
|
Context->ImageType = ImageTypeTe;
|
|
|
|
|
|
TeHdr = (CONST EFI_TE_IMAGE_HEADER *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
STATIC_ASSERT (
|
|
OC_TYPE_ALIGNED (EFI_IMAGE_SECTION_HEADER, sizeof (*TeHdr)),
|
|
"The section alignment requirements are violated."
|
|
);
|
|
|
|
Result = OcOverflowSubU32 (
|
|
TeHdr->StrippedSize,
|
|
sizeof (*TeHdr),
|
|
&Context->TeStrippedOffset
|
|
);
|
|
if (Result) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
if (TeHdr->NumberOfSections == 0) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
STATIC_ASSERT (
|
|
sizeof (EFI_IMAGE_SECTION_HEADER) <= (MAX_UINT32 - sizeof (*TeHdr)) / MAX_UINT8,
|
|
"These arithmetics may overflow."
|
|
);
|
|
//
|
|
// Assign SizeOfHeaders in a way that is equivalent to what the size would
|
|
// be if this was the original (unstripped) PE32 binary. As the TE image
|
|
// creation fixes no fields up, tests work the same way as for PE32.
|
|
// when referencing raw data however, the offset must be subracted.
|
|
//
|
|
Result = OcOverflowAddU32 (
|
|
TeHdr->StrippedSize,
|
|
(UINT32) TeHdr->NumberOfSections * sizeof (EFI_IMAGE_SECTION_HEADER),
|
|
&Context->SizeOfHeaders
|
|
);
|
|
if (Result) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure that all headers are in bounds of the file buffer.
|
|
//
|
|
if ((Context->SizeOfHeaders - Context->TeStrippedOffset) > FileSize) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Context->SectionAlignment = BASE_4KB;
|
|
SectionsOffset = Context->ExeHdrOffset + sizeof (EFI_TE_IMAGE_HEADER);
|
|
//
|
|
// Validate the sections.
|
|
// TE images do not have a field to explicitly describe the image size.
|
|
// Set it to the top of the image's virtual space.
|
|
//
|
|
Status = InternalVerifySections (
|
|
Context,
|
|
FileSize,
|
|
SectionsOffset,
|
|
TeHdr->NumberOfSections,
|
|
&BottomAddress,
|
|
&SizeOfImage
|
|
);
|
|
|
|
if (Status != IMAGE_ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
Context->SizeOfImage = SizeOfImage;
|
|
Context->Machine = TeHdr->Machine;
|
|
Context->Subsystem = TeHdr->Subsystem;
|
|
Context->ImageBase = TeHdr->ImageBase;
|
|
Context->RelocsStripped = TeHdr->DataDirectory[0].Size > 0;
|
|
Context->AddressOfEntryPoint = TeHdr->AddressOfEntryPoint;
|
|
Context->RelocDirRva = TeHdr->DataDirectory[0].VirtualAddress;
|
|
Context->RelocDirSize = TeHdr->DataDirectory[0].Size;
|
|
|
|
return InternalValidateRelocInfo (Context, BottomAddress);
|
|
}
|
|
|
|
STATIC
|
|
IMAGE_STATUS
|
|
InternalInitializePe (
|
|
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINTN FileSize
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
CONST EFI_IMAGE_NT_HEADERS_COMMON_HDR *PeCommon;
|
|
CONST EFI_IMAGE_NT_HEADERS32 *Pe32;
|
|
CONST EFI_IMAGE_NT_HEADERS64 *Pe32Plus;
|
|
CONST VOID *OptHdrPtr;
|
|
UINT32 HdrSizeWithoutDataDir;
|
|
UINT32 SizeOfOptionalHdr;
|
|
UINT32 SizeOfHeaders;
|
|
CONST EFI_IMAGE_DATA_DIRECTORY *RelocDir;
|
|
UINT32 NumberOfRvaAndSizes;
|
|
UINT32 SectHdrOffset;
|
|
IMAGE_STATUS Status;
|
|
UINT32 BottomAddress;
|
|
UINT32 SizeOfImage;
|
|
|
|
ASSERT (Context != NULL);
|
|
|
|
OptHdrPtr = (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR);
|
|
ASSERT (OC_TYPE_ALIGNED (EFI_IMAGE_NT_HEADERS_COMMON_HDR, Context->ExeHdrOffset));
|
|
|
|
STATIC_ASSERT (
|
|
OC_TYPE_ALIGNED (UINT16, OC_ALIGNOF (EFI_IMAGE_NT_HEADERS_COMMON_HDR))
|
|
&& OC_TYPE_ALIGNED (UINT16, sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR)),
|
|
"The following operation might be an unaligned access."
|
|
);
|
|
|
|
//
|
|
// Determine the type of and retrieve data from the PE Optional Header.
|
|
//
|
|
//
|
|
// FIXME: OptHdrPtr could point to unaligned memory.
|
|
//
|
|
switch (*(CONST UINT16 *) OptHdrPtr) {
|
|
case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
|
|
if (sizeof (*Pe32) > FileSize - Context->ExeHdrOffset) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: Invalid 32-bit OPT header\n"));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Context->ImageType = ImageTypePe32;
|
|
|
|
Pe32 = (CONST EFI_IMAGE_NT_HEADERS32 *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
HdrSizeWithoutDataDir = OFFSET_OF (EFI_IMAGE_NT_HEADERS32, DataDirectory) - sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR);
|
|
Context->Subsystem = Pe32->Subsystem;
|
|
NumberOfRvaAndSizes = Pe32->NumberOfRvaAndSizes;
|
|
|
|
RelocDir = Pe32->DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC;
|
|
|
|
Context->SizeOfImage = Pe32->SizeOfImage;
|
|
Context->SizeOfHeaders = Pe32->SizeOfHeaders;
|
|
Context->ImageBase = Pe32->ImageBase;
|
|
Context->AddressOfEntryPoint = Pe32->AddressOfEntryPoint;
|
|
Context->SectionAlignment = Pe32->SectionAlignment;
|
|
|
|
PeCommon = &Pe32->CommonHeader;
|
|
break;
|
|
|
|
case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
|
|
if (sizeof (*Pe32Plus) > FileSize - Context->ExeHdrOffset) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: Invalid 64-bit OPT header\n"));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Context->ImageType = ImageTypePe32Plus;
|
|
|
|
|
|
Pe32Plus = (CONST EFI_IMAGE_NT_HEADERS64 *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
HdrSizeWithoutDataDir = OFFSET_OF (EFI_IMAGE_NT_HEADERS64, DataDirectory) - sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR);
|
|
Context->Subsystem = Pe32Plus->Subsystem;
|
|
NumberOfRvaAndSizes = Pe32Plus->NumberOfRvaAndSizes;
|
|
|
|
RelocDir = Pe32Plus->DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC;
|
|
|
|
Context->SizeOfImage = Pe32Plus->SizeOfImage;
|
|
Context->SizeOfHeaders = Pe32Plus->SizeOfHeaders;
|
|
Context->ImageBase = Pe32Plus->ImageBase;
|
|
Context->AddressOfEntryPoint = Pe32Plus->AddressOfEntryPoint;
|
|
Context->SectionAlignment = Pe32Plus->SectionAlignment;
|
|
|
|
PeCommon = &Pe32Plus->CommonHeader;
|
|
break;
|
|
|
|
default:
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
if (PeCommon->FileHeader.NumberOfSections == 0) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: No sections in the image\n"));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Do not load images with unknown directories.
|
|
//
|
|
if (NumberOfRvaAndSizes > EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: NumberOfRvaAndSizes is too high %u\n", NumberOfRvaAndSizes));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
if (!IS_POW2 (Context->SectionAlignment)) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: Invalid section alignment %u\n", Context->SectionAlignment));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// SizeOfOptionalHdr cannot overflow because NumberOfRvaAndSizes has
|
|
// been sanitized and the other two components are validated constants.
|
|
//
|
|
STATIC_ASSERT (
|
|
sizeof (EFI_IMAGE_DATA_DIRECTORY) <= MAX_UINT32 / EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES,
|
|
"These arithmetics may overflow."
|
|
);
|
|
|
|
SizeOfOptionalHdr = HdrSizeWithoutDataDir +
|
|
NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY);
|
|
|
|
ASSERT (SizeOfOptionalHdr >= HdrSizeWithoutDataDir);
|
|
//
|
|
// Context->ExeHdrOffset + sizeof (*PeCommon) cannot overflow because
|
|
// * ExeFileSize > sizeof (*PeCommon) and
|
|
// * Context->ExeHdrOffset + ExeFileSize = FileSize
|
|
//
|
|
Result = OcOverflowAddU32 (
|
|
Context->ExeHdrOffset + sizeof (*PeCommon),
|
|
SizeOfOptionalHdr,
|
|
&SectHdrOffset
|
|
);
|
|
|
|
if (Result) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCPE: Sections offset overflow %u + %u + %u\n",
|
|
Context->ExeHdrOffset,
|
|
(UINT32) sizeof (*PeCommon),
|
|
SizeOfOptionalHdr
|
|
));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure the section headers offset is properly aligned.
|
|
//
|
|
if (!OC_TYPE_ALIGNED (EFI_IMAGE_SECTION_HEADER, SectHdrOffset)) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: Sections are unaligned %u\n", SectHdrOffset));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
STATIC_ASSERT (
|
|
sizeof (EFI_IMAGE_SECTION_HEADER) <= (MAX_UINT32 + 1ULL) / (MAX_UINT16 + 1ULL),
|
|
"These arithmetics may overflow."
|
|
);
|
|
|
|
Result = OcOverflowAddU32 (
|
|
SectHdrOffset,
|
|
(UINT32) PeCommon->FileHeader.NumberOfSections * sizeof (EFI_IMAGE_SECTION_HEADER),
|
|
&SizeOfHeaders
|
|
);
|
|
|
|
if (Result) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: Sections overflow %u %u\n", SectHdrOffset, PeCommon->FileHeader.NumberOfSections));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure the header sizes are sane. SizeOfHeaders contains all header
|
|
// components (DOS, PE Common and Optional Header).
|
|
//
|
|
if (PeCommon->FileHeader.SizeOfOptionalHeader < SizeOfOptionalHdr
|
|
|| Context->SizeOfHeaders < SizeOfHeaders) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: SizeOfOptionalHeader %u %u\n", PeCommon->FileHeader.SizeOfOptionalHeader, SizeOfOptionalHdr));
|
|
DEBUG ((DEBUG_INFO, "OCPE: ImageSizeOfHeaders %u %u\n", Context->SizeOfHeaders, SizeOfHeaders));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Ensure that all headers are in bounds of the file buffer.
|
|
//
|
|
if (Context->SizeOfHeaders > FileSize) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: Context->SizeOfHeaders > FileSize %u %u\n", Context->SizeOfHeaders, FileSize));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Status = InternalVerifySections (
|
|
Context,
|
|
FileSize,
|
|
SectHdrOffset,
|
|
PeCommon->FileHeader.NumberOfSections,
|
|
&BottomAddress,
|
|
&SizeOfImage
|
|
);
|
|
if (Status != IMAGE_ERROR_SUCCESS) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: InternalVerifySections %d\n", Status));
|
|
return Status;
|
|
}
|
|
//
|
|
// Ensure SizeOfImage is equal to the top of the image's virtual space.
|
|
// FIXME: Misaligned images should load with a PCD
|
|
//
|
|
if (Context->SizeOfImage < SizeOfImage) {
|
|
DEBUG ((DEBUG_INFO, "OCPE: Context->SizeOfImage < SizeOfImage %u %u\n", Context->SizeOfImage, SizeOfImage));
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
//
|
|
// If there's no relocations, then make sure it's not a runtime driver.
|
|
//
|
|
Context->RelocsStripped =
|
|
(
|
|
PeCommon->FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED
|
|
) != 0;
|
|
|
|
Context->Machine = PeCommon->FileHeader.Machine;
|
|
|
|
if (EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC < NumberOfRvaAndSizes) {
|
|
Context->RelocDirRva = RelocDir->VirtualAddress;
|
|
Context->RelocDirSize = RelocDir->Size;
|
|
} else {
|
|
Context->RelocDirRva = 0;
|
|
Context->RelocDirSize = 0;
|
|
}
|
|
|
|
return InternalValidateRelocInfo (Context, BottomAddress);
|
|
}
|
|
|
|
IMAGE_STATUS
|
|
OcPeCoffLoaderInitializeContext (
|
|
OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN CONST VOID *FileBuffer,
|
|
IN UINTN FileSize
|
|
)
|
|
{
|
|
CONST VOID *ImageSig;
|
|
CONST EFI_IMAGE_DOS_HEADER *DosHdr;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (FileBuffer != NULL);
|
|
ASSERT (FileSize > 0);
|
|
|
|
ZeroMem (Context, sizeof (*Context));
|
|
|
|
Context->FileBuffer = (VOID *) FileBuffer;
|
|
|
|
ASSERT (Context != NULL);
|
|
//
|
|
// Check whether the DOS image header is present.
|
|
//
|
|
if (FileSize > sizeof (*DosHdr)
|
|
&& *(CONST UINT16 *) Context->FileBuffer == EFI_IMAGE_DOS_SIGNATURE) {
|
|
DosHdr = (CONST EFI_IMAGE_DOS_HEADER *) Context->FileBuffer;
|
|
//
|
|
// When the DOS image header is present, sanitize the offset and
|
|
// retrieve the size of the executable image.
|
|
//
|
|
if (sizeof (EFI_IMAGE_DOS_HEADER) > DosHdr->e_lfanew
|
|
|| DosHdr->e_lfanew >= FileSize) {
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Context->ExeHdrOffset = DosHdr->e_lfanew;
|
|
} else {
|
|
//
|
|
// When the DOS image header is not present, assume the image starts with
|
|
// the executable header.
|
|
//
|
|
Context->ExeHdrOffset = 0;
|
|
}
|
|
//
|
|
// Use Signature to determine and handle the image format (PE32(+) / TE).
|
|
//
|
|
ImageSig = (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset;
|
|
|
|
if (FileSize - Context->ExeHdrOffset >= sizeof (EFI_TE_IMAGE_HEADER)
|
|
&& OC_TYPE_ALIGNED (EFI_TE_IMAGE_HEADER, Context->ExeHdrOffset)
|
|
&& *(CONST UINT16 *) ImageSig == EFI_TE_IMAGE_HEADER_SIGNATURE) {
|
|
return InternalInitializeTe (Context, FileSize);
|
|
}
|
|
|
|
if (FileSize - Context->ExeHdrOffset >= sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR) + sizeof (UINT16)
|
|
&& OC_TYPE_ALIGNED (EFI_IMAGE_NT_HEADERS_COMMON_HDR, Context->ExeHdrOffset)
|
|
&& *(CONST UINT32 *) ImageSig == EFI_IMAGE_NT_SIGNATURE) {
|
|
return InternalInitializePe (Context, FileSize);
|
|
}
|
|
|
|
return IMAGE_ERROR_INVALID_MACHINE_TYPE;
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
InternalHashSections (
|
|
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINT32 SectionsOffset,
|
|
IN UINT16 NumberOfSections,
|
|
IN HASH_UPDATE HashUpdate,
|
|
IN OUT VOID *HashContext
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
|
|
CONST EFI_IMAGE_SECTION_HEADER *Sections;
|
|
CONST EFI_IMAGE_SECTION_HEADER **SortedSections;
|
|
UINT16 SectIndex;
|
|
UINT16 SectionPos;
|
|
UINT32 SectionTop;
|
|
//
|
|
// Build a temporary table of pointers to all section headers of the image
|
|
// to sort them appropiately.
|
|
//
|
|
SortedSections = AllocatePool (
|
|
(UINT32) NumberOfSections * sizeof (*SortedSections)
|
|
);
|
|
|
|
if (SortedSections == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + SectionsOffset
|
|
);
|
|
//
|
|
// Sort SortedSections by PointerToRawData in ascending order.
|
|
//
|
|
SortedSections[0] = &Sections[0];
|
|
//
|
|
// Insertion Sort.
|
|
//
|
|
for (SectIndex = 1; SectIndex < NumberOfSections; ++SectIndex) {
|
|
for (SectionPos = SectIndex;
|
|
0 < SectionPos
|
|
&& SortedSections[SectionPos - 1]->PointerToRawData > Sections[SectIndex].PointerToRawData;
|
|
--SectionPos) {
|
|
SortedSections[SectionPos] = SortedSections[SectionPos - 1];
|
|
}
|
|
|
|
SortedSections[SectionPos] = &Sections[SectIndex];
|
|
}
|
|
|
|
Result = TRUE;
|
|
|
|
SectionTop = 0;
|
|
//
|
|
// Hash the image's sections' data in the ascending order of their offset.
|
|
//
|
|
for (SectIndex = 0; SectIndex < NumberOfSections; ++SectIndex) {
|
|
if (PcdGetBool (PcdImageLoaderHashProhibitOverlap)) {
|
|
if (SectionTop > SortedSections[SectIndex]->PointerToRawData) {
|
|
Result = FALSE;
|
|
break;
|
|
}
|
|
|
|
SectionTop = SortedSections[SectIndex]->PointerToRawData + SortedSections[SectIndex]->SizeOfRawData;
|
|
}
|
|
|
|
if (SortedSections[SectIndex]->SizeOfRawData > 0) {
|
|
Result = HashUpdate (
|
|
HashContext,
|
|
(CONST CHAR8 *) Context->FileBuffer + SortedSections[SectIndex]->PointerToRawData,
|
|
SortedSections[SectIndex]->SizeOfRawData
|
|
);
|
|
if (!Result) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool ((VOID *)SortedSections);
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
OcPeCoffLoaderHashImage (
|
|
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN HASH_UPDATE HashUpdate,
|
|
IN OUT VOID *HashContext
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
UINT32 NumberOfRvaAndSizes;
|
|
UINT32 ChecksumOffset;
|
|
UINT32 SecurityDirOffset;
|
|
UINT32 CurrentOffset;
|
|
UINT32 HashSize;
|
|
CONST EFI_IMAGE_NT_HEADERS32 *Pe32;
|
|
CONST EFI_IMAGE_NT_HEADERS64 *Pe32Plus;
|
|
UINT32 SectionsOffset;
|
|
UINT16 NumberOfSections;
|
|
//
|
|
// Hash the entire image excluding:
|
|
// * its checksum
|
|
// * its security directory
|
|
// * its signature
|
|
//
|
|
switch (Context->ImageType) {
|
|
case ImageTypeTe:
|
|
//
|
|
// TE images are not to be signed, as they are supposed to only be part of
|
|
// Firmware Volumes, which may be signed as a whole.
|
|
//
|
|
return FALSE;
|
|
|
|
case ImageTypePe32:
|
|
Pe32 = (CONST EFI_IMAGE_NT_HEADERS32 *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
ChecksumOffset = Context->ExeHdrOffset + OFFSET_OF (EFI_IMAGE_NT_HEADERS32, CheckSum);
|
|
SecurityDirOffset = Context->ExeHdrOffset + (UINT32) OFFSET_OF (EFI_IMAGE_NT_HEADERS32, DataDirectory) +(UINT32) (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY * sizeof (EFI_IMAGE_DATA_DIRECTORY));
|
|
NumberOfRvaAndSizes = Pe32->NumberOfRvaAndSizes;
|
|
SectionsOffset = Context->ExeHdrOffset + sizeof (Pe32->CommonHeader) + Pe32->CommonHeader.FileHeader.SizeOfOptionalHeader;
|
|
NumberOfSections = Pe32->CommonHeader.FileHeader.NumberOfSections;
|
|
|
|
break;
|
|
|
|
case ImageTypePe32Plus:
|
|
Pe32Plus = (CONST EFI_IMAGE_NT_HEADERS64 *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
ChecksumOffset = Context->ExeHdrOffset + OFFSET_OF (EFI_IMAGE_NT_HEADERS64, CheckSum);
|
|
SecurityDirOffset = Context->ExeHdrOffset + (UINT32) OFFSET_OF (EFI_IMAGE_NT_HEADERS64, DataDirectory) +(UINT32) (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY * sizeof (EFI_IMAGE_DATA_DIRECTORY));
|
|
NumberOfRvaAndSizes = Pe32Plus->NumberOfRvaAndSizes;
|
|
SectionsOffset = Context->ExeHdrOffset + sizeof (Pe32Plus->CommonHeader) + Pe32Plus->CommonHeader.FileHeader.SizeOfOptionalHeader;
|
|
NumberOfSections = Pe32Plus->CommonHeader.FileHeader.NumberOfSections;
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Hash the image header till the image's checksum.
|
|
//
|
|
Result = HashUpdate (HashContext, Context->FileBuffer, ChecksumOffset);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Skip over the image's checksum.
|
|
//
|
|
CurrentOffset = ChecksumOffset + sizeof (UINT32);
|
|
|
|
if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY < NumberOfRvaAndSizes) {
|
|
//
|
|
// Hash everything after the checksum till the security directory.
|
|
//
|
|
HashSize = SecurityDirOffset - CurrentOffset;
|
|
Result = HashUpdate (
|
|
HashContext,
|
|
(CONST CHAR8 *) Context->FileBuffer + CurrentOffset,
|
|
HashSize
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Skip over the security directory. If no further directory exists, this
|
|
// will point to the top of the directory.
|
|
//
|
|
CurrentOffset = SecurityDirOffset + sizeof (EFI_IMAGE_DATA_DIRECTORY);
|
|
}
|
|
//
|
|
// Hash the remainders of the image header.
|
|
//
|
|
HashSize = Context->SizeOfHeaders - CurrentOffset;
|
|
Result = HashUpdate (
|
|
HashContext,
|
|
(CONST CHAR8 *) Context->FileBuffer + CurrentOffset,
|
|
HashSize
|
|
);
|
|
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
|
|
return InternalHashSections (
|
|
Context,
|
|
SectionsOffset,
|
|
NumberOfSections,
|
|
HashUpdate,
|
|
HashContext
|
|
);
|
|
}
|
|
|
|
STATIC
|
|
IMAGE_STATUS
|
|
InternalApplyRelocation (
|
|
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINT32 RelocOffset,
|
|
IN UINT32 RelocIndex,
|
|
IN UINT64 Adjust
|
|
)
|
|
{
|
|
CONST EFI_IMAGE_BASE_RELOCATION *RelocWalker;
|
|
UINT16 RelocType;
|
|
UINT16 RelocOff;
|
|
BOOLEAN Result;
|
|
UINT32 RelocTarget;
|
|
UINT32 RemRelocTargetSize;
|
|
UINT32 Fixup32;
|
|
UINT64 Fixup64;
|
|
CHAR8 *Fixup;
|
|
|
|
RelocWalker = (CONST EFI_IMAGE_BASE_RELOCATION *) (
|
|
(CONST VOID *) ((CONST CHAR8 *) Context->FileBuffer + RelocOffset)
|
|
);
|
|
|
|
RelocType = IMAGE_RELOC_TYPE (RelocWalker->Relocations[RelocIndex]);
|
|
RelocOff = IMAGE_RELOC_OFFSET (RelocWalker->Relocations[RelocIndex]);
|
|
|
|
if (RelocType == EFI_IMAGE_REL_BASED_ABSOLUTE) {
|
|
return IMAGE_ERROR_SUCCESS;
|
|
}
|
|
//
|
|
// Determine the relocation's target address.
|
|
//
|
|
Result = OcOverflowAddU32 (
|
|
RelocWalker->VirtualAddress,
|
|
RelocOff,
|
|
&RelocTarget
|
|
);
|
|
|
|
if (Result) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
Result = OcOverflowSubU32 (
|
|
Context->SizeOfImage,
|
|
RelocTarget,
|
|
&RemRelocTargetSize
|
|
);
|
|
|
|
if (Result) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
Fixup = (CHAR8 *) Context->FileBuffer + RelocTarget;
|
|
//
|
|
// Apply the relocation fixup per type.
|
|
// If RelocationData is not NULL, store the current value of the fixup
|
|
// target to determine whether it has been changed during runtime
|
|
// execution.
|
|
//
|
|
// It is not clear how EFI_IMAGE_REL_BASED_HIGH and
|
|
// EFI_IMAGE_REL_BASED_LOW are supposed to be handled. While PE reference
|
|
// suggests to just add the high or low part of the displacement, there
|
|
// are concerns about how it's supposed to deal with wraparounds.
|
|
// As neither LLD,
|
|
//
|
|
|
|
switch (RelocType) {
|
|
case EFI_IMAGE_REL_BASED_HIGHLOW:
|
|
if (sizeof (UINT32) > RemRelocTargetSize) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
if (RelocTarget + sizeof (UINT32) > Context->RelocDirRva
|
|
&& Context->RelocDirRva + Context->RelocDirSize > RelocTarget) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
Fixup32 = ReadUnaligned32 ((UINT32 *) (VOID *) Fixup) +(UINT32)Adjust;
|
|
WriteUnaligned32 ((UINT32 *) (VOID *) Fixup, Fixup32);
|
|
|
|
break;
|
|
|
|
case EFI_IMAGE_REL_BASED_DIR64:
|
|
if (sizeof (UINT64) > RemRelocTargetSize) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
if (RelocTarget + sizeof (UINT64) > Context->RelocDirRva
|
|
&& Context->RelocDirRva + Context->RelocDirSize > RelocTarget) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
Fixup64 = ReadUnaligned64 ((UINT64 *) (VOID *) Fixup) + Adjust;
|
|
WriteUnaligned64 ((UINT64 *) (VOID *) Fixup, Fixup64);
|
|
|
|
break;
|
|
|
|
default:
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
return IMAGE_ERROR_SUCCESS;
|
|
}
|
|
|
|
IMAGE_STATUS
|
|
OcPeCoffLoaderRelocateImage (
|
|
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINTN BaseAddress
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
IMAGE_STATUS Status;
|
|
|
|
UINT64 Adjust;
|
|
CONST EFI_IMAGE_BASE_RELOCATION *RelocWalker;
|
|
|
|
UINT32 SizeOfRelocs;
|
|
UINT32 NumRelocs;
|
|
|
|
UINT32 RelocDataIndex;
|
|
|
|
UINT32 RelocOffset;
|
|
UINT32 RelocMax;
|
|
|
|
UINT32 RelocIndex;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Context->RelocDirRva + Context->RelocDirSize >= Context->RelocDirRva);
|
|
ASSERT (Context->RelocDirRva + Context->RelocDirSize <= Context->SizeOfImage);
|
|
//
|
|
// Calculate the image's displacement from its prefered location.
|
|
//
|
|
Adjust = (UINT64) BaseAddress -Context->ImageBase;
|
|
//
|
|
// Runtime drivers should unconditionally go through the full relocation
|
|
// procedure early to eliminate the possibility of errors later at runtime.
|
|
// Runtime drivers don't have their relocations stripped, this is verified
|
|
// during context creation.
|
|
// Skip explicit relocation when the image is already loaded at its
|
|
// prefered location.
|
|
//
|
|
if (Context->Subsystem != EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER
|
|
&& Adjust == 0) {
|
|
return IMAGE_ERROR_SUCCESS;
|
|
}
|
|
//
|
|
// Ensure relocations have not been stripped.
|
|
//
|
|
ASSERT (!Context->RelocsStripped);
|
|
//
|
|
// Apply relocation fixups to the image.
|
|
//
|
|
RelocOffset = Context->RelocDirRva;
|
|
|
|
RelocMax = Context->RelocDirRva + Context->RelocDirSize - sizeof (EFI_IMAGE_BASE_RELOCATION);
|
|
|
|
RelocDataIndex = 0;
|
|
|
|
while (RelocOffset < RelocMax) {
|
|
RelocWalker = (CONST EFI_IMAGE_BASE_RELOCATION *) (
|
|
(CONST VOID *) ((CONST CHAR8 *) Context->FileBuffer + RelocOffset)
|
|
);
|
|
|
|
STATIC_ASSERT (
|
|
(sizeof (UINT32) % OC_ALIGNOF (EFI_IMAGE_BASE_RELOCATION)) == 0,
|
|
"The following accesses must be performed unaligned."
|
|
);
|
|
|
|
Result = OcOverflowSubU32 (
|
|
RelocWalker->SizeOfBlock,
|
|
sizeof (EFI_IMAGE_BASE_RELOCATION),
|
|
&SizeOfRelocs
|
|
);
|
|
//
|
|
// Ensure no overflow has occured and there is at least one entry.
|
|
//
|
|
if (Result || SizeOfRelocs == 0 || SizeOfRelocs > RelocMax - RelocOffset) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
//
|
|
// Ensure the block's size is padded to ensure proper alignment.
|
|
//
|
|
if ((RelocWalker->SizeOfBlock % sizeof (UINT32)) != 0) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
//
|
|
// This division is safe due to the guarantee made above.
|
|
//
|
|
NumRelocs = SizeOfRelocs / sizeof (*RelocWalker->Relocations);
|
|
//
|
|
// Apply all relocation fixups of the current block.
|
|
//
|
|
for (RelocIndex = 0; RelocIndex < NumRelocs; ++RelocIndex) {
|
|
//
|
|
// Apply the relocation fixup per type.
|
|
// If RelocationData is not NULL, store the current value of the fixup
|
|
// target to determine whether it has been changed during runtime
|
|
// execution.
|
|
//
|
|
// It is not clear how EFI_IMAGE_REL_BASED_HIGH and
|
|
// EFI_IMAGE_REL_BASED_LOW are supposed to be handled. While PE reference
|
|
// suggests to just add the high or low part of the displacement, there
|
|
// are concerns about how it's supposed to deal with wraparounds.
|
|
// As neither LLD,
|
|
//
|
|
Status = InternalApplyRelocation (
|
|
Context,
|
|
RelocOffset,
|
|
RelocIndex,
|
|
Adjust
|
|
);
|
|
if (Status != IMAGE_ERROR_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
RelocDataIndex += NumRelocs;
|
|
RelocOffset += RelocWalker->SizeOfBlock;
|
|
}
|
|
//
|
|
// Ensure the relocation directory size matches the contained relocations.
|
|
//
|
|
if (RelocOffset != RelocMax + sizeof (EFI_IMAGE_BASE_RELOCATION)) {
|
|
return IMAGE_ERROR_FAILED_RELOCATION;
|
|
}
|
|
|
|
return IMAGE_ERROR_SUCCESS;
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
InternalLoadSections (
|
|
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINT32 LoadedSizeOfHeaders,
|
|
IN UINT32 SectionsOffset,
|
|
IN UINT16 NumberOfSections,
|
|
OUT VOID *Destination,
|
|
IN UINT32 DestinationSize
|
|
)
|
|
{
|
|
CONST EFI_IMAGE_SECTION_HEADER *Sections;
|
|
UINT16 Index;
|
|
UINT32 DataSize;
|
|
UINT32 PreviousTopRva;
|
|
|
|
Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + SectionsOffset
|
|
);
|
|
|
|
PreviousTopRva = LoadedSizeOfHeaders;
|
|
|
|
for (Index = 0; Index < NumberOfSections; ++Index) {
|
|
if (Sections[Index].VirtualSize < Sections[Index].SizeOfRawData) {
|
|
DataSize = Sections[Index].VirtualSize;
|
|
} else {
|
|
DataSize = Sections[Index].SizeOfRawData;
|
|
}
|
|
|
|
ZeroMem ((CHAR8 *) Destination + PreviousTopRva, Sections[Index].VirtualAddress - PreviousTopRva);
|
|
CopyMem (
|
|
(CHAR8 *) Destination + Sections[Index].VirtualAddress,
|
|
(CONST CHAR8 *) Context->FileBuffer + (Sections[Index].PointerToRawData -Context->TeStrippedOffset),
|
|
DataSize
|
|
);
|
|
|
|
PreviousTopRva = Sections[Index].VirtualAddress + DataSize;
|
|
}
|
|
|
|
ZeroMem (
|
|
(CHAR8 *) Destination + PreviousTopRva,
|
|
DestinationSize - PreviousTopRva
|
|
);
|
|
}
|
|
|
|
IMAGE_STATUS
|
|
OcPeCoffLoaderLoadImage (
|
|
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT VOID *Destination,
|
|
IN UINT32 DestinationSize
|
|
)
|
|
{
|
|
CHAR8 *AlignedDest;
|
|
UINT32 AlignOffset;
|
|
UINT32 AlignedSize;
|
|
UINT32 LoadedSizeOfHeaders;
|
|
CONST EFI_IMAGE_NT_HEADERS32 *SrcPe32;
|
|
CONST EFI_IMAGE_NT_HEADERS64 *SrcPe32Plus;
|
|
CONST EFI_TE_IMAGE_HEADER *SrcTe;
|
|
CONST EFI_IMAGE_SECTION_HEADER *Sections;
|
|
UINT32 SectionsOffset;
|
|
UINT16 NumberOfSections;
|
|
UINTN Address;
|
|
UINTN AlignedAddress;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Destination != NULL);
|
|
ASSERT (DestinationSize > 0);
|
|
ASSERT (DestinationSize >= Context->SectionAlignment);
|
|
|
|
Address = (UINTN)Destination;
|
|
|
|
ASSERT (!Context->RelocsStripped || Context->ImageBase == Address);
|
|
|
|
AlignedAddress = ALIGN_VALUE (Address, (UINTN) Context->SectionAlignment);
|
|
AlignOffset = (UINT32) (AlignedAddress - Address);
|
|
AlignedSize = DestinationSize - AlignOffset;
|
|
|
|
ASSERT (Context->SizeOfImage <= AlignedSize);
|
|
|
|
AlignedDest = (CHAR8 *) Destination + AlignOffset;
|
|
|
|
ZeroMem (Destination, AlignOffset);
|
|
|
|
switch (Context->ImageType) {
|
|
case ImageTypeTe:
|
|
SrcTe = (CONST EFI_TE_IMAGE_HEADER *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
NumberOfSections = SrcTe->NumberOfSections;
|
|
SectionsOffset = Context->ExeHdrOffset + sizeof (*SrcTe);
|
|
break;
|
|
|
|
case ImageTypePe32:
|
|
SrcPe32 = (CONST EFI_IMAGE_NT_HEADERS32 *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
NumberOfSections = SrcPe32->CommonHeader.FileHeader.NumberOfSections;
|
|
SectionsOffset = Context->ExeHdrOffset + sizeof (SrcPe32->CommonHeader) + SrcPe32->CommonHeader.FileHeader.SizeOfOptionalHeader;
|
|
break;
|
|
|
|
case ImageTypePe32Plus:
|
|
SrcPe32Plus = (CONST EFI_IMAGE_NT_HEADERS64 *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset
|
|
);
|
|
|
|
NumberOfSections = SrcPe32Plus->CommonHeader.FileHeader.NumberOfSections;
|
|
SectionsOffset = Context->ExeHdrOffset + sizeof (SrcPe32Plus->CommonHeader) + SrcPe32Plus->CommonHeader.FileHeader.SizeOfOptionalHeader;
|
|
break;
|
|
|
|
default:
|
|
ASSERT (FALSE);
|
|
return IMAGE_ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) (
|
|
(CONST CHAR8 *) Context->FileBuffer + SectionsOffset
|
|
);
|
|
|
|
if (Sections[0].VirtualAddress != 0 && PcdGetBool (PcdImageLoaderLoadHeader)) {
|
|
LoadedSizeOfHeaders = (Context->SizeOfHeaders - Context->TeStrippedOffset);
|
|
|
|
CopyMem (AlignedDest, Context->FileBuffer, LoadedSizeOfHeaders);
|
|
} else {
|
|
LoadedSizeOfHeaders = 0;
|
|
}
|
|
|
|
InternalLoadSections (
|
|
Context,
|
|
LoadedSizeOfHeaders,
|
|
SectionsOffset,
|
|
NumberOfSections,
|
|
AlignedDest,
|
|
AlignedSize
|
|
);
|
|
//
|
|
// Update the location-dependent fields to the loaded destination.
|
|
//
|
|
Context->FileBuffer = AlignedDest;
|
|
|
|
return IMAGE_ERROR_SUCCESS;
|
|
}
|