CloverBootloader/MdePkg/Library/BasePeCoffLib2/PeCoffRelocate.c

939 lines
30 KiB
C

/** @file
Implements APIs to relocate PE/COFF Images.
Copyright (c) 2020 - 2021, Marvin Häuser. All rights reserved.<BR>
Copyright (c) 2020, Vitaly Cheptsov. All rights reserved.<BR>
Copyright (c) 2020, ISP RAS. All rights reserved.<BR>
Portions copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
Portions copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>
Portions copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
SPDX-License-Identifier: BSD-3-Clause
**/
#include <Base.h>
#include <IndustryStandard/PeImage2.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/BaseOverflowLib.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Library/PeCoffLib2.h>
#include "BasePeCoffLib2Internals.h"
struct PE_COFF_LOADER_RUNTIME_CONTEXT_ {
///
/// The RVA of the Relocation Directory.
///
UINT32 RelocDirRva;
///
/// The size, in Bytes, of the Relocation Directory.
///
UINT32 RelocDirSize;
///
/// Information bookkept during the initial Image relocation.
///
UINT64 FixupData[];
};
// FIXME: Add RISC-V support.
/**
Returns whether the Base Relocation type is supported by this loader.
@param[in] Type The type of the Base Relocation.
**/
#define IMAGE_RELOC_TYPE_SUPPORTED(Type) \
(((Type) == EFI_IMAGE_REL_BASED_ABSOLUTE) || \
((Type) == EFI_IMAGE_REL_BASED_HIGHLOW) || \
((Type) == EFI_IMAGE_REL_BASED_DIR64) || \
((PcdGet32 (PcdImageLoaderRelocTypePolicy) & PCD_RELOC_TYPE_POLICY_ARM) != 0 && (Type) == EFI_IMAGE_REL_BASED_ARM_MOV32T))
/**
Returns whether the Base Relocation is supported by this loader.
@param[in] Relocation The composite Base Relocation value.
**/
#define IMAGE_RELOC_SUPPORTED(Relocation) \
IMAGE_RELOC_TYPE_SUPPORTED (IMAGE_RELOC_TYPE (Reloc))
/**
Retrieve the immediate data encoded in an ARM MOVT or MOVW immediate
instruciton.
@param[in] Instruction Pointer to an ARM MOVT or MOVW immediate instruction.
@returns The Immediate address encoded in the instruction.
**/
STATIC
UINT16
ThumbMovtImmediateAddress (
IN CONST VOID *Instruction
)
{
UINT32 Movt;
UINT16 Movt1;
UINT16 Movt2;
UINT16 Address;
//
// Thumb2 is two separate 16-bit instructions working together, e.g.
// MOVT R0, #0 is 0x0000f2c0 or 0xf2c0 0x0000
//
Movt1 = *(CONST UINT16 *) (CONST VOID *) Instruction;
Movt2 = *(CONST UINT16 *) (CONST VOID *) ((CONST CHAR8 *) Instruction + sizeof (UINT16));
Movt = ((UINT32) Movt1 << 16U) | (UINT32) Movt2;
//
// imm16 = imm4:i:imm3:imm8
// imm4 -> Bit19:Bit16
// i -> Bit26
// imm3 -> Bit14:Bit12
// imm8 -> Bit7:Bit0
//
Address = (UINT16) (Movt & 0x000000FFU); // imm8
Address |= (UINT16) ((Movt >> 4U) & 0x0000F700U); // imm4 imm3
Address |= (UINT16) ((Movt & BIT26) >> 15U); // i, Bit26->11
return Address;
}
/**
Update an ARM MOVT or MOVW immediate instruction immediate data.
@param[in,out] Instruction Pointer to an ARM MOVT or MOVW immediate
instruction.
@param[in] Address New address to patch into the instruction.
**/
STATIC
VOID
ThumbMovtImmediatePatch (
IN OUT VOID *Instruction,
IN UINT16 Address
)
{
UINT16 Patch;
UINT16 PatchedInstruction;
//
// First 16-bit chunk of instruction.
//
Patch = (Address & 0xF000U) >> 12U; // imm4
Patch |= (Address & BIT11) >> 1U; // i, Bit11->10
//
// Mask out instruction bits and or in address.
//
PatchedInstruction = *(CONST UINT16 *) (CONST VOID *) Instruction;
*(UINT16 *) (VOID *) Instruction = (PatchedInstruction & ~(UINT16) 0x040FU) | Patch;
//
// Second 16-bit chunk of instruction.
//
Patch = Address & 0x000000FFU; // imm8
Patch |= ((UINT32) Address << 4U) & 0x00007000U; // imm3
//
// Mask out instruction bits and or in address.
//
PatchedInstruction = *(CONST UINT16 *) (CONST VOID *) ((CHAR8 *) Instruction + sizeof (UINT16));
*(UINT16 *) (VOID *) ((CHAR8 *) Instruction + sizeof (UINT16)) =
(PatchedInstruction & ~(UINT16) 0x70FFU) | Patch;
}
UINT32
PeCoffThumbMovwMovtImmediateAddress (
IN CONST VOID *Instructions
)
{
CONST CHAR8 *Word;
CONST CHAR8 *Top;
//
// Calculate the encoded address of the instruction pair.
//
Word = Instructions; // MOVW
Top = (CONST CHAR8 *) Instructions + 2 * sizeof (UINT16); // MOVT
return (UINT32) (((UINT32) ThumbMovtImmediateAddress (Top) << 16U) | ThumbMovtImmediateAddress (Word));
}
/**
Update an ARM MOVW/MOVT immediate instruction instruction pair.
@param[in,out] Instructions Pointer to ARM MOVW/MOVT instruction pair.
@param[in] Address New address to patch into the instructions.
**/
STATIC
VOID
ThumbMovwMovtImmediatePatch (
IN OUT VOID *Instructions,
IN UINT32 Address
)
{
CHAR8 *Word;
CHAR8 *Top;
//
// Patch the instruction pair's encoded address.
//
Word = Instructions; // MOVW
Top = (CHAR8 *) Instructions + 2 * sizeof (UINT16); // MOVT
ThumbMovtImmediatePatch (Word, (UINT16) (Address & 0x0000FFFFU));
ThumbMovtImmediatePatch (Top, (UINT16) ((Address & 0xFFFF0000U) >> 16U));
}
/**
Relocate an ARM MOVW/MOVT immediate instruction instruction pair.
@param[in,out] Instructions Pointer to ARM MOVW/MOVT instruction pair.
@param[in] Adjust The delta to add to the addresses.
**/
VOID
PeCoffThumbMovwMovtImmediateFixup (
IN OUT VOID *Instructions,
IN UINT64 Adjust
)
{
UINT32 Fixup32;
//
// Relocate the instruction pair.
//
Fixup32 = PeCoffThumbMovwMovtImmediateAddress (Instructions) + (UINT32) Adjust;
ThumbMovwMovtImmediatePatch (Instructions, Fixup32);
}
/**
Apply an Image Base Relocation.
Only a subset of the PE/COFF Base Relocation types are permited.
The Base Relocation target must be in bounds, aligned, and must not overlap
with the Relocation Directory.
@param[in] Context The context describing the Image. Must have been
loaded by PeCoffLoadImage().
@param[in] RelocBlock The Base Relocation Block to apply from.
@param[in] RelocIndex The index of the Base Relocation to apply.
@param[in] Adjust The delta to add to the addresses.
@param[out] FixupData On input, a pointer to a bookkeeping value or NULL.
On output, a value to preserve for Image runtime
relocation.
@retval RETURN_SUCCESS The Base Relocation has been applied successfully.
@retval other The Base Relocation could not be applied successfully.
**/
STATIC
RETURN_STATUS
InternalApplyRelocation (
IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context,
IN CONST EFI_IMAGE_BASE_RELOCATION_BLOCK *RelocBlock,
IN UINT32 RelocIndex,
IN UINT64 Adjust,
OUT UINT64 *FixupData OPTIONAL
)
{
BOOLEAN Overflow;
UINT16 RelocType;
UINT16 RelocOffset;
UINT32 RelocTargetRva;
UINT32 RemRelocTargetSize;
CHAR8 *Fixup;
UINT32 Fixup32;
UINT64 Fixup64;
RelocType = IMAGE_RELOC_TYPE (RelocBlock->Relocations[RelocIndex]);
RelocOffset = IMAGE_RELOC_OFFSET (RelocBlock->Relocations[RelocIndex]);
//
// Absolute Base Relocations are used for padding any must be skipped.
//
if (RelocType == EFI_IMAGE_REL_BASED_ABSOLUTE) {
if (FixupData != NULL) {
FixupData[RelocIndex] = 0;
}
return RETURN_SUCCESS;
}
//
// Verify the Base Relocation target address is in bounds of the Image buffer.
//
Overflow = BaseOverflowAddU32 (
RelocBlock->VirtualAddress,
RelocOffset,
&RelocTargetRva
);
if (Overflow) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
Overflow = BaseOverflowSubU32 (
Context->SizeOfImage,
RelocTargetRva,
&RemRelocTargetSize
);
if (Overflow) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
Fixup = (CHAR8 *) Context->ImageBuffer + RelocTargetRva;
//
// Apply the Base Relocation fixup per type.
// If RuntimeContext 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 the PE
// specification 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 they are virtually non-existent, they are unsupported for
// the time being.
//
switch (RelocType) {
case EFI_IMAGE_REL_BASED_HIGHLOW:
//
// Verify the Base Relocation target is in bounds of the Image buffer.
//
if (sizeof (UINT32) > RemRelocTargetSize) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Verify the Image Base Relocation does not target the Image Relocation
// Directory.
//
if (RelocTargetRva + sizeof (UINT32) > Context->RelocDirRva
&& Context->RelocDirRva + Context->RelocDirSize > RelocTargetRva) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Relocate the target instruction.
//
Fixup32 = ReadUnaligned32 ((CONST VOID *) Fixup);
Fixup32 += (UINT32) Adjust;
WriteUnaligned32 ((VOID *) Fixup, Fixup32);
//
// Record the relocated value for Image runtime relocation.
//
if (FixupData != NULL) {
FixupData[RelocIndex] = Fixup32;
}
break;
case EFI_IMAGE_REL_BASED_DIR64:
//
// Verify the Image Base Relocation target is in bounds of the Image
// buffer.
//
if (sizeof (UINT64) > RemRelocTargetSize) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Verify the Image Base Relocation does not target the Image Relocation
// Directory.
//
if (RelocTargetRva + sizeof (UINT64) > Context->RelocDirRva
&& Context->RelocDirRva + Context->RelocDirSize > RelocTargetRva) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Relocate target the instruction.
//
Fixup64 = ReadUnaligned64 ((CONST VOID *) Fixup);
Fixup64 += Adjust;
WriteUnaligned64 ((VOID *) Fixup, Fixup64);
//
// Record the relocated value for Image runtime relocation.
//
if (FixupData != NULL) {
FixupData[RelocIndex] = Fixup64;
}
break;
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
//
// Verify ARM Thumb mode Base Relocations are supported.
//
if ((PcdGet32 (PcdImageLoaderRelocTypePolicy) & PCD_RELOC_TYPE_POLICY_ARM) == 0) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Verify the Base Relocation target is in bounds of the Image buffer.
//
if (sizeof (UINT64) > RemRelocTargetSize) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Verify the Base Relocation target is sufficiently aligned.
// The ARM Thumb instruction pair must start on a 16-bit boundary.
//
if (!IS_ALIGNED (RelocTargetRva, ALIGNOF (UINT16))) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Verify the Base Relocation does not target the Relocation Directory.
//
if (RelocTargetRva + sizeof (UINT64) > Context->RelocDirRva
&& Context->RelocDirRva + Context->RelocDirSize > RelocTargetRva) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Relocate the target instruction.
//
PeCoffThumbMovwMovtImmediateFixup (Fixup, Adjust);
//
// Record the relocated value for Image runtime relocation.
//
if (FixupData != NULL) {
FixupData[RelocIndex] = ReadUnaligned64 ((CONST VOID *) Fixup);
}
break;
default:
//
// The Image Base Relocation type is unknown, disallow the Image.
//
DEBUG_RAISE ();
return RETURN_UNSUPPORTED;
}
return RETURN_SUCCESS;
}
RETURN_STATUS
PeCoffRelocateImage (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
IN UINT64 BaseAddress,
OUT PE_COFF_LOADER_RUNTIME_CONTEXT *RuntimeContext OPTIONAL,
IN UINT32 RuntimeContextSize
)
{
RETURN_STATUS Status;
BOOLEAN Overflow;
UINT64 Adjust;
UINT32 RelocBlockOffsetMax;
UINT32 TopOfRelocDir;
UINT32 RelocBlockOffset;
CONST EFI_IMAGE_BASE_RELOCATION_BLOCK *RelocBlock;
UINT32 RelocBlockSize;
UINT32 SizeOfRelocs;
UINT32 NumRelocs;
UINT32 RelocIndex;
UINT32 FixupDataIndex;
UINT64 *CurrentFixupData;
ASSERT (Context != NULL);
ASSERT (!Context->RelocsStripped || BaseAddress == Context->ImageBase);
ASSERT (RuntimeContext != NULL || RuntimeContextSize == 0);
ASSERT (RuntimeContext == NULL || RuntimeContextSize >= sizeof (PE_COFF_LOADER_RUNTIME_CONTEXT) + Context->RelocDirSize * (sizeof (UINT64) / sizeof (UINT16)));
//
// Initialise the Image runtime context header.
//
if (RuntimeContext != NULL) {
RuntimeContext->RelocDirRva = Context->RelocDirRva;
RuntimeContext->RelocDirSize = Context->RelocDirSize;
}
//
// Verify the Relocation Directory is not empty.
//
if (Context->RelocDirSize == 0) {
return RETURN_SUCCESS;
}
//
// Calculate the Image displacement from its prefered load address.
//
Adjust = 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 Base Relocations stripped, this is
// verified during context creation.
// Skip explicit Relocation when the Image is already loaded at its
// prefered location.
//
if (RuntimeContext == NULL && Adjust == 0) {
return RETURN_SUCCESS;
}
RelocBlockOffset = Context->RelocDirRva;
TopOfRelocDir = Context->RelocDirRva + Context->RelocDirSize;
RelocBlockOffsetMax = TopOfRelocDir - sizeof (EFI_IMAGE_BASE_RELOCATION_BLOCK);
FixupDataIndex = 0;
//
// Align TopOfRelocDir because, if the policy does not demand Relocation Block
// sizes to be aligned, the code below will manually align them. Thus, the
// end offset of the last Relocation Block must be compared to a manually
// aligned Relocation Directoriy end offset.
//
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_RELOCATION_BLOCK_SIZES) != 0) {
Overflow = BaseOverflowAlignUpU32 (
TopOfRelocDir,
ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK),
&TopOfRelocDir
);
if (Overflow) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
}
//
// Apply all Base Relocations of the Image.
//
while (RelocBlockOffset <= RelocBlockOffsetMax) {
RelocBlock = (CONST EFI_IMAGE_BASE_RELOCATION_BLOCK *) (CONST VOID *) (
(CONST CHAR8 *) Context->ImageBuffer + RelocBlockOffset
);
//
// Verify the Base Relocation Block size is well-formed.
//
Overflow = BaseOverflowSubU32 (
RelocBlock->SizeOfBlock,
sizeof (EFI_IMAGE_BASE_RELOCATION_BLOCK),
&SizeOfRelocs
);
if (Overflow) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Verify the Base Relocation Block is in bounds of the Relocation
// Directory.
//
if (SizeOfRelocs > RelocBlockOffsetMax - RelocBlockOffset) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Advance to the next Base Relocation Block offset based on the alignment
// policy.
//
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_RELOCATION_BLOCK_SIZES) == 0) {
RelocBlockSize = RelocBlock->SizeOfBlock;
//
// Verify the next Base Relocation Block offset is sufficiently aligned.
//
if (!IS_ALIGNED (RelocBlockSize, ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK))) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
} else {
//
// This arithmetic cannot overflow because we know
// 1) RelocBlock->SizeOfBlock <= RelocMax <= TopOfRelocDir
// 2) IS_ALIGNED (TopOfRelocDir, ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK)).
//
RelocBlockSize = ALIGN_VALUE (
RelocBlock->SizeOfBlock,
ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK)
);
}
//
// This division is safe due to the guarantee made above.
//
NumRelocs = SizeOfRelocs / sizeof (*RelocBlock->Relocations);
if (RuntimeContext != NULL) {
CurrentFixupData = &RuntimeContext->FixupData[FixupDataIndex];
//
// This arithmetic cannot overflow because The number of Image Base
// Relocations cannot exceed the size of their Image Relocation Block, and
// latter has been verified to be in bounds of the Image buffer. The Image
// buffer size and RelocDataIndex are both bound by MAX_UINT32.
//
FixupDataIndex += NumRelocs;
} else {
CurrentFixupData = NULL;
}
//
// Process all Base Relocations of the current Block.
//
for (RelocIndex = 0; RelocIndex < NumRelocs; ++RelocIndex) {
//
// Apply the Image Base Relocation fixup.
// If RuntimeContext 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 no known linker emits them, omit support.
//
Status = InternalApplyRelocation (
Context,
RelocBlock,
RelocIndex,
Adjust,
CurrentFixupData
);
if (Status != RETURN_SUCCESS) {
DEBUG_RAISE ();
return Status;
}
}
//
// This arithmetic cannot overflow because it has been checked that the
// Image Base Relocation Block is in bounds of the Image buffer.
//
RelocBlockOffset += RelocBlockSize;
}
//
// Verify the Relocation Directory size matches the contained data.
//
if (RelocBlockOffset != TopOfRelocDir) {
DEBUG_RAISE ();
return RETURN_VOLUME_CORRUPTED;
}
//
// Initialise the remaining uninitialised portion of the Image runtime
// context.
//
if (RuntimeContext != NULL) {
//
// This arithmetic cannot overflow due to the guarantee given by
// PeCoffLoaderGetRuntimeContextSize().
//
ZeroMem (
&RuntimeContext->FixupData[FixupDataIndex],
RuntimeContextSize - sizeof (PE_COFF_LOADER_RUNTIME_CONTEXT) - FixupDataIndex * sizeof (UINT64)
);
}
return RETURN_SUCCESS;
}
/**
Apply an Image Base Relocation for Image runtime relocation.
Well-formedness has been verified by PeCoffRelocateImage() previously.
Fails if the Relocation target value has changed since PeCoffRelocateImage().
@param[in] Image The Image destination memory. Must have been relocated
by PeCoffRelocateImage().
@param[in] RelocBlock The Base Relocation Block to apply from.
@param[in] RelocIndex The index of the Base Relocation to apply.
@param[in] Adjust The delta to add to the addresses.
@param[out] FixupData The bookkeeping value.
@retval RETURN_SUCCESS The Base Relocation has been applied successfully.
@retval other The Base Relocation could not be applied successfully.
**/
STATIC
RETURN_STATUS
InternalApplyRelocationRuntime (
IN OUT VOID *Fixup,
IN UINT16 RelocType,
IN UINT64 Adjust,
IN UINT64 FixupData
)
{
UINT32 Fixup32;
UINT64 Fixup64;
ASSERT (Fixup != NULL);
ASSERT (IMAGE_RELOC_TYPE_SUPPORTED (RelocType)
&& RelocType != EFI_IMAGE_REL_BASED_ABSOLUTE);
//
// Apply the Image Base Relocation fixup per type.
//
// If the Image relocation target value has changes since the initial
// Image relocation, skip or abort based on the policy. The fixup cannot
// be applies safely as the value might not reference an address within
// the Image memory space, e.g. when a global variable pointer to another
// variable is changed during runtime.
//
switch (RelocType) {
case EFI_IMAGE_REL_BASED_HIGHLOW:
Fixup32 = ReadUnaligned32 ((CONST VOID *) Fixup);
//
// If the Image relocation target value mismatches, skip or abort.
//
if (FixupData != Fixup32) {
if (PcdGetBool (PcdImageLoaderRtRelocAllowTargetMismatch)) {
return RETURN_SUCCESS;
}
return RETURN_VOLUME_CORRUPTED;
}
Fixup32 += (UINT32) Adjust;
WriteUnaligned32 (Fixup, Fixup32);
break;
case EFI_IMAGE_REL_BASED_DIR64:
Fixup64 = ReadUnaligned64 (Fixup);
//
// If the Image relocation target value mismatches, skip or abort.
//
if (FixupData != Fixup64) {
if (PcdGetBool (PcdImageLoaderRtRelocAllowTargetMismatch)) {
return RETURN_SUCCESS;
}
return RETURN_VOLUME_CORRUPTED;
}
Fixup64 += Adjust;
WriteUnaligned64 (Fixup, Fixup64);
break;
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
ASSERT ((PcdGet32 (PcdImageLoaderRelocTypePolicy) & PCD_RELOC_TYPE_POLICY_ARM) != 0);
Fixup64 = ReadUnaligned64 (Fixup);
//
// If the Image relocation target value mismatches, skip or abort.
//
if (FixupData != Fixup64) {
if (PcdGetBool (PcdImageLoaderRtRelocAllowTargetMismatch)) {
return RETURN_SUCCESS;
}
return RETURN_VOLUME_CORRUPTED;
}
PeCoffThumbMovwMovtImmediateFixup (Fixup, Adjust);
break;
default:
//
// Invalid Base Relocation types would have caused the Image to not be
// loaded relocated successfully earlier.
//
ASSERT (FALSE);
}
return RETURN_SUCCESS;
}
RETURN_STATUS
PeCoffLoaderGetRuntimeContextSize (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context,
OUT UINT32 *Size
)
{
BOOLEAN Overflow;
UINT32 FixupDataSize;
ASSERT (Context != NULL);
ASSERT (!Context->RelocsStripped);
ASSERT (Size != NULL);
//
// Because the ImageBase Relocations have not been stripped,
// PeCoffInitializeContext() has verified the Relocation Directory exists and
// is valid.
//
//
// Request 64-bit of source value per 16-bit Base Relocation.
// This allocates too many Bytes because it assumes that every Base Relocation
// refers to a 64-bit target and does not account for Base Relocation Block
// headers.
//
Overflow = BaseOverflowMulU32 (
Context->RelocDirSize,
sizeof (UINT64) / sizeof (UINT16),
&FixupDataSize
);
if (Overflow) {
return RETURN_UNSUPPORTED;
}
Overflow = BaseOverflowAddU32 (
FixupDataSize,
sizeof (PE_COFF_LOADER_RUNTIME_CONTEXT),
Size
);
if (Overflow) {
return RETURN_UNSUPPORTED;
}
return RETURN_SUCCESS;
}
RETURN_STATUS
PeCoffRuntimeRelocateImage (
IN OUT VOID *Image,
IN UINT32 ImageSize,
IN UINT64 BaseAddress,
IN CONST PE_COFF_LOADER_RUNTIME_CONTEXT *RuntimeContext
)
{
RETURN_STATUS Status;
UINTN ImageAddress;
UINT32 FixupDataIndex;
CONST EFI_IMAGE_BASE_RELOCATION_BLOCK *RelocBlock;
UINT64 Adjust;
UINT32 TopOfRelocDir;
UINT32 RelocBlockOffset;
UINT32 SizeOfRelocs;
UINT32 NumRelocs;
UINT32 RelocIndex;
UINT32 RelocTarget;
UINT32 RelocOffset;
UINT32 RelocBlockSize;
(VOID) ImageSize;
ASSERT (Image != NULL);
ASSERT (BaseAddress != 0);
ASSERT (RuntimeContext != NULL);
//
// If the relocation directory is empty, skip relocation.
//
if (RuntimeContext->RelocDirSize == 0) {
return RETURN_SUCCESS;
}
//
// The arithmetics in this function generally cannot overflow due to the
// guarantees given by PeCoffRelocateImage().
//
ImageAddress = (UINTN) Image;
Adjust = BaseAddress - ImageAddress;
//
// If the Image remains at the current address, skip relocation.
//
if (Adjust == 0) {
return RETURN_SUCCESS;
}
//
// Apply all Base Relocations of the Image.
//
RelocBlockOffset = RuntimeContext->RelocDirRva;
TopOfRelocDir = RuntimeContext->RelocDirRva + RuntimeContext->RelocDirSize;
FixupDataIndex = 0;
while (RelocBlockOffset < TopOfRelocDir) {
ASSERT (IS_ALIGNED (RelocBlockOffset, ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK)));
RelocBlock = (CONST EFI_IMAGE_BASE_RELOCATION_BLOCK *) (CONST VOID *) (
(CONST CHAR8 *) Image + RelocBlockOffset
);
STATIC_ASSERT (
(sizeof (UINT32) % ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK)) == 0,
"The following accesses must be performed unaligned."
);
ASSERT (sizeof (EFI_IMAGE_BASE_RELOCATION_BLOCK) <= RelocBlock->SizeOfBlock);
//
// Determine the number of Image Base Relocations in this Block.
//
SizeOfRelocs = RelocBlock->SizeOfBlock - sizeof (EFI_IMAGE_BASE_RELOCATION_BLOCK);
NumRelocs = SizeOfRelocs / sizeof (*RelocBlock->Relocations);
//
// Apply all Image Base Relocation fixups of the current Image Base
// Relocation Block.
//
for (RelocIndex = 0; RelocIndex < NumRelocs; ++RelocIndex) {
//
// Skip Absolute Image Base Relocations.
//
if (IMAGE_RELOC_TYPE (RelocBlock->Relocations[RelocIndex]) == EFI_IMAGE_REL_BASED_ABSOLUTE) {
++FixupDataIndex;
continue;
}
//
// Determine the Image Base Relocation target address.
//
RelocOffset = IMAGE_RELOC_OFFSET (RelocBlock->Relocations[RelocIndex]);
ASSERT (RelocOffset <= RelocBlock->VirtualAddress + RelocOffset);
//
// Apply the Image Base Relocation fixup.
//
RelocTarget = RelocBlock->VirtualAddress + RelocOffset;
Status = InternalApplyRelocationRuntime (
(CHAR8 *) Image + RelocTarget,
IMAGE_RELOC_TYPE (RelocBlock->Relocations[RelocIndex]),
Adjust,
RuntimeContext->FixupData[FixupDataIndex]
);
//
// If the original Image Relocation target value mismatches the expected
// value, and the policy demands it, report an error.
//
if (!PcdGetBool (PcdImageLoaderRtRelocAllowTargetMismatch)) {
if (Status != RETURN_SUCCESS) {
return Status;
}
} else {
ASSERT (Status == RETURN_SUCCESS);
}
++FixupDataIndex;
}
//
// Advance to the next Base Relocation Block based on the alignment policy.
//
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_RELOCATION_BLOCK_SIZES) == 0) {
RelocBlockSize = RelocBlock->SizeOfBlock;
ASSERT (IS_ALIGNED (RelocBlockSize, ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK)));
} else {
RelocBlockSize = ALIGN_VALUE (
RelocBlock->SizeOfBlock,
ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK)
);
}
RelocBlockOffset += RelocBlockSize;
}
//
// Align TopOfRelocDir because, if the policy does not demand Relocation Block
// sizes to be aligned, the code below will manually align them. Thus, the
// end offset of the last Relocation Block must be compared to a manually
// aligned Relocation Directoriy end offset.
//
if ((PcdGet32 (PcdImageLoaderAlignmentPolicy) & PCD_ALIGNMENT_POLICY_RELOCATION_BLOCK_SIZES) != 0) {
TopOfRelocDir = ALIGN_VALUE (TopOfRelocDir, ALIGNOF (EFI_IMAGE_BASE_RELOCATION_BLOCK));
}
//
// This condition is verified by PeCoffRelocateImage().
//
ASSERT (RelocBlockOffset == TopOfRelocDir);
return RETURN_SUCCESS;
}
RETURN_STATUS
PeCoffRelocateImageInplace (
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context
)
{
RETURN_STATUS Status;
UINTN NewBase;
Status = PeCoffLoadImageInplaceNoBase (Context);
if (RETURN_ERROR (Status)) {
DEBUG_RAISE ();
return Status;
}
NewBase = PeCoffLoaderGetImageAddress (Context);
Status = PeCoffRelocateImage (Context, NewBase, NULL, 0);
if (RETURN_ERROR (Status)) {
DEBUG_RAISE ();
}
return Status;
}