mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-29 12:35:53 +01:00
773 lines
24 KiB
C
773 lines
24 KiB
C
/** @file
|
|
UEFI Image Loader library implementation for PE/COFF Images.
|
|
|
|
Copyright (c) 2021, Marvin Häuser. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include <Base.h>
|
|
#include <Uefi/UefiBaseType.h>
|
|
#include <Uefi/UefiSpec.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/BaseOverflowLib.h>
|
|
#include <Library/CacheMaintenanceLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PcdLib.h>
|
|
#include <Library/PeCoffLib2.h>
|
|
#include <Library/UefiImageLib.h>
|
|
#include <Library/PcdLib.h>
|
|
|
|
#include "PeSupport.h"
|
|
|
|
RETURN_STATUS
|
|
UefiImageInitializeContextPreHashPe (
|
|
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN CONST VOID *FileBuffer,
|
|
IN UINT32 FileSize,
|
|
IN UINT8 ImageOrigin
|
|
)
|
|
{
|
|
return PeCoffInitializeContext (&Context->Ctx.Pe, FileBuffer, FileSize, ImageOrigin);
|
|
}
|
|
|
|
BOOLEAN
|
|
UefiImageHashImageDefaultPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN OUT VOID *HashContext,
|
|
IN UEFI_IMAGE_LOADER_HASH_UPDATE HashUpdate
|
|
)
|
|
{
|
|
return PeCoffHashImageAuthenticode (&Context->Ctx.Pe, HashContext, HashUpdate);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageInitializeContextPostHashPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageLoadImagePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT VOID *Destination,
|
|
IN UINT32 DestinationSize
|
|
)
|
|
{
|
|
return PeCoffLoadImage (&Context->Ctx.Pe, Destination, DestinationSize);
|
|
}
|
|
|
|
BOOLEAN
|
|
UefiImageImageIsInplacePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffImageIsInplace (&Context->Ctx.Pe);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageLoadImageInplacePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffLoadImageInplace (&Context->Ctx.Pe);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageRelocateImageInplacePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffRelocateImageInplace (&Context->Ctx.Pe);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageLoaderGetRuntimeContextSizePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT UINT32 *Size
|
|
)
|
|
{
|
|
return PeCoffLoaderGetRuntimeContextSize (&Context->Ctx.Pe, Size);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageRelocateImagePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINT64 BaseAddress,
|
|
OUT VOID *RuntimeContext OPTIONAL,
|
|
IN UINT32 RuntimeContextSize
|
|
)
|
|
{
|
|
return PeCoffRelocateImage (
|
|
&Context->Ctx.Pe,
|
|
BaseAddress,
|
|
(PE_COFF_LOADER_RUNTIME_CONTEXT *)RuntimeContext,
|
|
RuntimeContextSize
|
|
);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageRuntimeRelocateImagePe (
|
|
IN OUT VOID *Image,
|
|
IN UINT32 ImageSize,
|
|
IN UINT64 BaseAddress,
|
|
IN CONST VOID *RuntimeContext
|
|
)
|
|
{
|
|
return PeCoffRuntimeRelocateImage (
|
|
Image,
|
|
ImageSize,
|
|
BaseAddress,
|
|
(CONST PE_COFF_LOADER_RUNTIME_CONTEXT *)RuntimeContext
|
|
);
|
|
}
|
|
|
|
VOID
|
|
UefiImageDiscardSegmentsPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
PeCoffDiscardSections (&Context->Ctx.Pe);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetSymbolsPathPe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT CONST CHAR8 **SymbolsPath,
|
|
OUT UINT32 *SymbolsPathSize
|
|
)
|
|
{
|
|
return PeCoffGetPdbPath (&Context->Ctx.Pe, SymbolsPath, SymbolsPathSize);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetFirstCertificatePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT CONST WIN_CERTIFICATE **Certificate
|
|
)
|
|
{
|
|
return PeCoffGetFirstCertificate (&Context->Ctx.Pe, Certificate);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetNextCertificatePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN OUT CONST WIN_CERTIFICATE **Certificate
|
|
)
|
|
{
|
|
return PeCoffGetNextCertificate (&Context->Ctx.Pe, Certificate);
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetHiiDataRvaPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT UINT32 *HiiRva,
|
|
OUT UINT32 *HiiSize
|
|
)
|
|
{
|
|
return PeCoffGetHiiDataRva (&Context->Ctx.Pe, HiiRva, HiiSize);
|
|
}
|
|
|
|
UINT32
|
|
UefiImageGetEntryPointAddressPe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffGetAddressOfEntryPoint (&Context->Ctx.Pe);
|
|
}
|
|
|
|
UINT16
|
|
UefiImageGetMachinePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffGetMachine (&Context->Ctx.Pe);
|
|
}
|
|
|
|
UINT16
|
|
UefiImageGetSubsystemPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffGetSubsystem (&Context->Ctx.Pe);
|
|
}
|
|
|
|
UINT32
|
|
UefiImageGetSegmentAlignmentPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffGetSectionAlignment (&Context->Ctx.Pe);
|
|
}
|
|
|
|
UINT32
|
|
UefiImageGetImageSizePe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffGetSizeOfImage (&Context->Ctx.Pe);
|
|
}
|
|
|
|
UINT64
|
|
UefiImageGetBaseAddressPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffGetImageBase (&Context->Ctx.Pe);
|
|
}
|
|
|
|
BOOLEAN
|
|
UefiImageGetRelocsStrippedPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffGetRelocsStripped (&Context->Ctx.Pe);
|
|
}
|
|
|
|
UINTN
|
|
UefiImageLoaderGetImageAddressPe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffLoaderGetImageAddress (&Context->Ctx.Pe);
|
|
}
|
|
|
|
UINTN
|
|
UefiImageLoaderGetDebugAddressPe (
|
|
IN CONST UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
return PeCoffLoaderGetImageAddress (&Context->Ctx.Pe);
|
|
}
|
|
|
|
/**
|
|
Retrieves the memory protection attributes corresponding to PE/COFF Image
|
|
section permissions.
|
|
|
|
@param[in] Characteristics The PE/COFF Image section permissions
|
|
|
|
@returns The memory protection attributes corresponding to the PE/COFF Image
|
|
section permissions.
|
|
**/
|
|
STATIC
|
|
UINT32
|
|
InternalCharacteristicsToAttributes (
|
|
IN UINT32 Characteristics
|
|
)
|
|
{
|
|
UINT32 Attributes;
|
|
|
|
if (PcdGetBool (PcdImageLoaderRemoveXForWX) && (Characteristics & (EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_MEM_WRITE)) == (EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_MEM_WRITE)) {
|
|
Characteristics &= ~EFI_IMAGE_SCN_MEM_EXECUTE;
|
|
}
|
|
|
|
Attributes = 0;
|
|
if ((Characteristics & EFI_IMAGE_SCN_MEM_READ) == 0) {
|
|
Attributes |= EFI_MEMORY_RP;
|
|
}
|
|
if ((Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) == 0) {
|
|
Attributes |= EFI_MEMORY_XP;
|
|
}
|
|
if ((Characteristics & EFI_IMAGE_SCN_MEM_WRITE) == 0) {
|
|
Attributes |= EFI_MEMORY_RO;
|
|
}
|
|
|
|
return Attributes;
|
|
}
|
|
|
|
/**
|
|
Index the read-only padding following an Image record section, if existent.
|
|
|
|
@param[in,out] RecordSegment The Image record section for the current memory
|
|
protection range. May be extended if it is of
|
|
the same type as the adjacent padding. At least
|
|
one more record section must be reserved after
|
|
it in order to index the read-only padding.
|
|
@param[in] NextAddress The start address of the next memory permission
|
|
range. This may be the end address of the Image
|
|
in order to cover the Image trailer.
|
|
@param[in] EndAddress The end address of the current memory permission
|
|
range. This also denotes the start of the added
|
|
read-only padding.
|
|
@param[in] Attributes The memory protection attributes of the current
|
|
memory permission range.
|
|
|
|
@returns The amount of Image record sections that have been appended.
|
|
**/
|
|
STATIC
|
|
UINT8
|
|
InternalInsertImageRecordSegmentPadding (
|
|
IN OUT UEFI_IMAGE_RECORD_SEGMENT *RecordSegment,
|
|
IN UINT32 EndAddress,
|
|
IN UINT32 NextAddress,
|
|
IN UINT32 Attributes
|
|
)
|
|
{
|
|
ASSERT (RecordSegment != NULL);
|
|
ASSERT (EndAddress <= NextAddress);
|
|
|
|
if (NextAddress == EndAddress) {
|
|
return 0;
|
|
}
|
|
//
|
|
// Add a new Image record section or expand the previous one depending on
|
|
// the the permissions of the previous Image record section.
|
|
//
|
|
if (Attributes == (EFI_MEMORY_XP | EFI_MEMORY_RO)) {
|
|
RecordSegment->Size += NextAddress - EndAddress;
|
|
|
|
return 0;
|
|
}
|
|
|
|
++RecordSegment;
|
|
RecordSegment->Size = NextAddress - EndAddress;
|
|
RecordSegment->Attributes = EFI_MEMORY_XP | EFI_MEMORY_RO;
|
|
|
|
return 1;
|
|
}
|
|
|
|
UEFI_IMAGE_RECORD *
|
|
UefiImageLoaderGetImageRecordPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
UEFI_IMAGE_RECORD *ImageRecord;
|
|
UINT32 MaxNumRecordSegments;
|
|
UINT32 NumRecordSegments;
|
|
UEFI_IMAGE_RECORD_SEGMENT *RecordSegment;
|
|
UINTN ImageAddress;
|
|
UINT32 SizeOfImage;
|
|
UINT32 SectionAlignment;
|
|
CONST EFI_IMAGE_SECTION_HEADER *Sections;
|
|
UINT16 NumberOfSections;
|
|
UINT16 SectionIndex;
|
|
CONST EFI_IMAGE_SECTION_HEADER *Section;
|
|
UINT32 SectionAddress;
|
|
UINT32 SectionSize;
|
|
UINT32 SectionCharacteristics;
|
|
UINT32 StartAddress;
|
|
UINT32 EndAddress;
|
|
UINT32 Characteristics;
|
|
UINT32 Attributes;
|
|
|
|
ASSERT (Context != NULL);
|
|
//
|
|
// Determine the maximum amount of Image record sections and allocate the
|
|
// Image record.
|
|
//
|
|
NumberOfSections = PeCoffGetSectionTable (&Context->Ctx.Pe, &Sections);
|
|
|
|
STATIC_ASSERT (
|
|
MAX_UINT16 <= MAX_UINT32 / 2 - 1,
|
|
"The following arithmetic may overflow."
|
|
);
|
|
|
|
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_CONTIGUOUS_SECTIONS) == 0) {
|
|
//
|
|
// In case of contiguous Image sections, there can be two additional record
|
|
// sections (Image Headers and trailer, e.g. debug information).
|
|
//
|
|
MaxNumRecordSegments = (UINT32) NumberOfSections + 2;
|
|
} else {
|
|
//
|
|
// In case of possibly non-contiguous Image sections, there can be a trailer
|
|
// per Image section (the last Image section's trailer is the same as the
|
|
// Image trailer), as well as additionally the Image Headers.
|
|
//
|
|
MaxNumRecordSegments = (UINT32) NumberOfSections * 2 + 1;
|
|
}
|
|
|
|
ImageRecord = AllocatePool (
|
|
sizeof (*ImageRecord)
|
|
+ MaxNumRecordSegments * sizeof (*ImageRecord->Segments)
|
|
);
|
|
if (ImageRecord == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ImageRecord->Signature = UEFI_IMAGE_RECORD_SIGNATURE;
|
|
InitializeListHead (&ImageRecord->Link);
|
|
|
|
SectionAlignment = PeCoffGetSectionAlignment (&Context->Ctx.Pe);
|
|
//
|
|
// Map the Image Headers as read-only data. If the first Image section is
|
|
// loaded at the start of the Image memory space, the condition
|
|
// SectionAddress != StartAddress does not hold and these definitions will be
|
|
// ignored.
|
|
//
|
|
StartAddress = 0;
|
|
EndAddress = PeCoffGetSizeOfHeaders (&Context->Ctx.Pe);
|
|
Characteristics = EFI_IMAGE_SCN_MEM_READ;
|
|
Attributes = EFI_MEMORY_XP | EFI_MEMORY_RO;
|
|
ASSERT (Attributes == InternalCharacteristicsToAttributes (Characteristics));
|
|
//
|
|
// Create an Image record section for every permission-distinct range of the
|
|
// Image. The current range [StartAddress, EndAddress) shares the memory
|
|
// permissions Charactersitics/Attributes and is extended till a new
|
|
// memory permission configuration is required. Headers and trailers treated
|
|
// as read-only data.
|
|
//
|
|
NumRecordSegments = 0;
|
|
for (SectionIndex = 0; SectionIndex < NumberOfSections; ++SectionIndex) {
|
|
Section = Sections + SectionIndex;
|
|
//
|
|
// Skip empty Image sections to avoid unnecessary splitting.
|
|
//
|
|
if (Section->VirtualSize == 0) {
|
|
continue;
|
|
}
|
|
//
|
|
// These arithmetics are safe as guaranteed by PeCoffInitializeContext().
|
|
//
|
|
SectionAddress = Section->VirtualAddress;
|
|
SectionSize = ALIGN_VALUE (Section->VirtualSize, SectionAlignment);
|
|
SectionCharacteristics = Section->Characteristics & (EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_MEM_READ | EFI_IMAGE_SCN_MEM_WRITE);
|
|
//
|
|
// Skip Image sections with the same memory permissions as the current range
|
|
// as they can be merged. For this, the Image sections must be adjacent, or
|
|
// the range must have the same memory permissions as the padding inbetween
|
|
// (read-only).
|
|
//
|
|
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_CONTIGUOUS_SECTIONS) == 0
|
|
|| SectionAddress == EndAddress
|
|
|| Characteristics == EFI_IMAGE_SCN_MEM_READ) {
|
|
if (SectionCharacteristics == Characteristics) {
|
|
EndAddress = SectionAddress + SectionSize;
|
|
continue;
|
|
}
|
|
}
|
|
//
|
|
// Only create an entry if the range is not empty, otherwise discard it and
|
|
// start a new one. Even with skipping empty Image sections, this can still
|
|
// happen for the Image Headers when the first Image section starts at 0.
|
|
//
|
|
if (SectionAddress != StartAddress) {
|
|
//
|
|
// Create an Image record section for the current memory permission range.
|
|
//
|
|
RecordSegment = &ImageRecord->Segments[NumRecordSegments];
|
|
RecordSegment->Size = EndAddress - StartAddress;
|
|
RecordSegment->Attributes = Attributes;
|
|
++NumRecordSegments;
|
|
//
|
|
// If the previous range is not adjacent to the current Image section,
|
|
// report the padding as read-only data.
|
|
//
|
|
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_CONTIGUOUS_SECTIONS) != 0) {
|
|
NumRecordSegments += InternalInsertImageRecordSegmentPadding (
|
|
RecordSegment,
|
|
EndAddress,
|
|
SectionAddress,
|
|
Attributes
|
|
);
|
|
}
|
|
|
|
StartAddress = SectionAddress;
|
|
}
|
|
//
|
|
// Start a Image record section with the current Image section.
|
|
//
|
|
EndAddress = SectionAddress + SectionSize;
|
|
Characteristics = SectionCharacteristics;
|
|
Attributes = InternalCharacteristicsToAttributes (Characteristics);
|
|
}
|
|
//
|
|
// Image Record sections are only created once a non-empty Image section is
|
|
// encountered that requests a different memory permission configuration.
|
|
// As such, the last memory permission range is never converted in the loop.
|
|
// If the loop never produced such, this is true for the Image Headers, which
|
|
// cannot be empty.
|
|
//
|
|
ASSERT (StartAddress < EndAddress);
|
|
//
|
|
// Create an Image record section for the last Image memory permission range.
|
|
//
|
|
RecordSegment = &ImageRecord->Segments[NumRecordSegments];
|
|
RecordSegment->Size = EndAddress - StartAddress;
|
|
RecordSegment->Attributes = Attributes;
|
|
++NumRecordSegments;
|
|
|
|
ImageAddress = PeCoffLoaderGetImageAddress (&Context->Ctx.Pe);
|
|
SizeOfImage = PeCoffGetSizeOfImage (&Context->Ctx.Pe);
|
|
//
|
|
// The Image trailer, if existent, is treated as padding and as such is
|
|
// reported as read-only data, as intended. Because it is not part of the
|
|
// original Image memory space, this needs to happen whether Image sections
|
|
// are guaranteed to be contiguously form the entire Image memory space or
|
|
// not.
|
|
//
|
|
NumRecordSegments += InternalInsertImageRecordSegmentPadding (
|
|
RecordSegment,
|
|
EndAddress,
|
|
SizeOfImage,
|
|
Attributes
|
|
);
|
|
|
|
ImageRecord->NumSegments = NumRecordSegments;
|
|
ImageRecord->StartAddress = ImageAddress;
|
|
ImageRecord->EndAddress = ImageAddress + SizeOfImage;
|
|
//
|
|
// Zero the remaining array entries to avoid uninitialised data.
|
|
//
|
|
ZeroMem (
|
|
ImageRecord->Segments + NumRecordSegments,
|
|
(MaxNumRecordSegments - NumRecordSegments) * sizeof (*ImageRecord->Segments)
|
|
);
|
|
|
|
return ImageRecord;
|
|
}
|
|
|
|
// FIXME: Docs
|
|
STATIC
|
|
RETURN_STATUS
|
|
InternalDebugLocateImage (
|
|
OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
|
|
IN CHAR8 *Buffer,
|
|
IN UINTN Address,
|
|
IN BOOLEAN Recurse,
|
|
IN UINT8 ImageOrigin
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
RETURN_STATUS DosStatus;
|
|
PE_COFF_LOADER_IMAGE_CONTEXT DosContext;
|
|
|
|
ASSERT (((UINTN) Buffer & 3U) == 0);
|
|
//
|
|
// Align the search buffer to a 4 Byte boundary.
|
|
//
|
|
// Search for the Image Header in 4 Byte steps. All dynamically loaded
|
|
// Images start at a page boundary to allow for Image section protection,
|
|
// but XIP Images may not. As all Image Headers are at least 4 Byte aligned
|
|
// due to natural alignment, even XIP TE Image Headers should start at a
|
|
// 4 Byte boundary.
|
|
//
|
|
// Do not attempt to access memory of the first page as it may be protected as
|
|
// part of NULL dereference detection.
|
|
//
|
|
for (; EFI_PAGE_SIZE <= (UINTN) Buffer; Buffer -= 4) {
|
|
//
|
|
// Try to parse the current memory as PE/COFF or TE Image. Pass MAX_UINT32
|
|
// as the file size as there isn't any more information available. Only the
|
|
// Image Header memory will be accessed as part of initialisation.
|
|
//
|
|
Status = PeCoffInitializeContext (
|
|
Context,
|
|
Buffer,
|
|
MAX_UINT32,
|
|
ImageOrigin
|
|
);
|
|
if (RETURN_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
if (!Recurse) {
|
|
//
|
|
// For PE/COFF Images, the PE/COFF Image Header may be discovered while
|
|
// there may still be a preceeding DOS Image Header. All RVAs are relatvie
|
|
// to the start of the Image memory space, of which the DOS Image Header
|
|
// is a part of, if existent. Allow one level of recursion to find a lower
|
|
// Image Base including the DOS Image Header.
|
|
//
|
|
if (Context->ExeHdrOffset == 0) {
|
|
DosStatus = InternalDebugLocateImage (
|
|
&DosContext,
|
|
Buffer - 4,
|
|
Address,
|
|
TRUE,
|
|
ImageOrigin
|
|
);
|
|
if (!RETURN_ERROR (DosStatus)) {
|
|
Buffer = DosContext.ImageBuffer;
|
|
CopyMem (Context, &DosContext, sizeof (*Context));
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// We know that (UINTN) Buffer <= Address from the initialisation.
|
|
//
|
|
// FIXME: Set to non-stripped base for XIP TE Images.
|
|
if (Address < (UINTN) Buffer + PeCoffGetSizeOfImage (Context)) {
|
|
Context->ImageBuffer = Buffer;
|
|
//
|
|
// Zero the raw file information as we are initialising from a potentially
|
|
// non-XIP in-memory Image.
|
|
//
|
|
Context->FileBuffer = NULL;
|
|
Context->FileSize = 0;
|
|
|
|
return RETURN_SUCCESS;
|
|
}
|
|
//
|
|
// Continue for the unlikely case that a PE/COFF or TE Image embeds another
|
|
// one within its data, the outer Image may still follow.
|
|
//
|
|
}
|
|
|
|
return RETURN_NOT_FOUND;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageDebugLocateImagePe (
|
|
OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
IN UINTN Address,
|
|
IN UINT8 ImageOrigin
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
|
|
Status = RETURN_NOT_FOUND;
|
|
//
|
|
// As this function is intrinsically unsafe, do not allow its usage outside of
|
|
// DEBUG-enabled code.
|
|
//
|
|
DEBUG_CODE_BEGIN ();
|
|
|
|
//
|
|
// If the Image Headers are not loaded explicitly, only XIP Images and Images
|
|
// that embed the Image Header in the first Image section can be located. As
|
|
// this is not the case for the majority of Images, don't attempt to locate
|
|
// the Image base to not access too much (potentially protected) memory.
|
|
//
|
|
if (!PcdGetBool (PcdImageLoaderLoadHeader)) {
|
|
return RETURN_NOT_FOUND;
|
|
}
|
|
|
|
Context->FormatIndex = UefiImageFormatPe;
|
|
//
|
|
// Align the search buffer to a 4 Byte boundary.
|
|
//
|
|
Status = InternalDebugLocateImage (
|
|
&Context->Ctx.Pe,
|
|
(CHAR8 *) (Address & ~(UINTN) 3U),
|
|
Address,
|
|
FALSE,
|
|
ImageOrigin
|
|
);
|
|
|
|
DEBUG_CODE_END ();
|
|
|
|
return Status;
|
|
}
|
|
|
|
RETURN_STATUS
|
|
UefiImageGetFixedAddressPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context,
|
|
OUT UINT64 *Address
|
|
)
|
|
{
|
|
UINT32 SectionAlignment;
|
|
CONST EFI_IMAGE_SECTION_HEADER *Sections;
|
|
UINT16 NumberOfSections;
|
|
UINT16 SectionIndex;
|
|
UINT64 FixedAddress;
|
|
|
|
ASSERT (Address != NULL);
|
|
|
|
SectionAlignment = PeCoffGetSectionAlignment (&Context->Ctx.Pe);
|
|
//
|
|
// If this feature is enabled, the build tool will save the address in the
|
|
// PointerToRelocations and PointerToLineNumbers fields of the first Image
|
|
// section header that doesn't hold code. The 64-bit value across those fields
|
|
// will be non-zero if and only if the module has been assigned an address.
|
|
//
|
|
NumberOfSections = PeCoffGetSectionTable (&Context->Ctx.Pe, &Sections);
|
|
for (SectionIndex = 0; SectionIndex < NumberOfSections; ++SectionIndex) {
|
|
if ((Sections[SectionIndex].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) {
|
|
continue;
|
|
}
|
|
|
|
FixedAddress = ReadUnaligned64 (
|
|
(CONST VOID *) &Sections[SectionIndex].PointerToRelocations
|
|
);
|
|
if (FixedAddress != 0) {
|
|
if (!IS_ALIGNED (FixedAddress, SectionAlignment)) {
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
*Address = FixedAddress;
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return RETURN_NOT_FOUND;
|
|
}
|
|
|
|
VOID
|
|
UefiImageDebugPrintSegmentsPe (
|
|
IN OUT UEFI_IMAGE_LOADER_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
CONST EFI_IMAGE_SECTION_HEADER *Sections;
|
|
UINT16 NumberOfSections;
|
|
UINT16 SectionIndex;
|
|
CONST UINT8 *Name;
|
|
|
|
NumberOfSections = PeCoffGetSectionTable (&Context->Ctx.Pe, &Sections);
|
|
|
|
for (SectionIndex = 0; SectionIndex < NumberOfSections; ++SectionIndex) {
|
|
Name = Sections[SectionIndex].Name;
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
" Section - '%c%c%c%c%c%c%c%c'\n"
|
|
" VirtualSize - 0x%08x\n"
|
|
" VirtualAddress - 0x%08x\n"
|
|
" SizeOfRawData - 0x%08x\n"
|
|
" PointerToRawData - 0x%08x\n"
|
|
" PointerToRelocations - 0x%08x\n"
|
|
" PointerToLinenumbers - 0x%08x\n"
|
|
" NumberOfRelocations - 0x%08x\n"
|
|
" NumberOfLinenumbers - 0x%08x\n"
|
|
" Characteristics - 0x%08x\n",
|
|
Name[0], Name[1], Name[2], Name[3], Name[4], Name[5], Name[6], Name[7],
|
|
Sections[SectionIndex].VirtualSize,
|
|
Sections[SectionIndex].VirtualAddress,
|
|
Sections[SectionIndex].SizeOfRawData,
|
|
Sections[SectionIndex].PointerToRawData,
|
|
Sections[SectionIndex].PointerToRelocations,
|
|
Sections[SectionIndex].PointerToLinenumbers,
|
|
Sections[SectionIndex].NumberOfRelocations,
|
|
Sections[SectionIndex].NumberOfLinenumbers,
|
|
Sections[SectionIndex].Characteristics
|
|
));
|
|
}
|
|
}
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CONST UEFI_IMAGE_FORMAT_SUPPORT mPeSupport = {
|
|
UefiImageInitializeContextPreHashPe,
|
|
UefiImageHashImageDefaultPe,
|
|
UefiImageInitializeContextPostHashPe,
|
|
UefiImageLoadImagePe,
|
|
UefiImageImageIsInplacePe,
|
|
UefiImageLoadImageInplacePe,
|
|
UefiImageRelocateImageInplacePe,
|
|
UefiImageLoaderGetRuntimeContextSizePe,
|
|
UefiImageRelocateImagePe,
|
|
UefiImageRuntimeRelocateImagePe,
|
|
UefiImageDiscardSegmentsPe,
|
|
UefiImageGetSymbolsPathPe,
|
|
UefiImageGetFirstCertificatePe,
|
|
UefiImageGetNextCertificatePe,
|
|
UefiImageGetHiiDataRvaPe,
|
|
UefiImageGetEntryPointAddressPe,
|
|
UefiImageGetMachinePe,
|
|
UefiImageGetSubsystemPe,
|
|
UefiImageGetSegmentAlignmentPe,
|
|
UefiImageGetImageSizePe,
|
|
UefiImageGetBaseAddressPe,
|
|
UefiImageGetRelocsStrippedPe,
|
|
UefiImageLoaderGetImageAddressPe,
|
|
UefiImageLoaderGetDebugAddressPe,
|
|
UefiImageLoaderGetImageRecordPe,
|
|
UefiImageDebugLocateImagePe,
|
|
UefiImageGetFixedAddressPe,
|
|
UefiImageDebugPrintSegmentsPe
|
|
};
|