2024-07-22 13:49:00 +03:00

1119 lines
30 KiB
C

/** @file
Copyright (c) 2023, Marvin Häuser. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "ImageTool.h"
#include "DynamicBuffer.h"
static
uint8_t
AlignmentToExponent (
uint64_t Alignment
)
{
uint8_t Index;
assert (IS_POW2 (Alignment));
for (
Index = 0;
// LCOV_EXCL_START
Index < 64;
// LCOV_EXCL_STOP
++Index
) {
if ((Alignment & (1ULL << Index)) == Alignment) {
return Index;
}
}
// LCOV_EXCL_START
assert (false);
return 0;
// LCOV_EXCL_STOP
}
static
bool
ToolImageEmitUeSegmentHeaders (
image_tool_dynamic_buffer *Buffer,
const image_tool_image_info_t *Image,
bool Xip
)
{
uint16_t Index;
uint8_t Permissions;
UE_SEGMENT Segment;
uint32_t Offset;
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
// LCOV_EXCL_START
if (Image->SegmentInfo.Segments[Index].Write &&
Image->SegmentInfo.Segments[Index].Execute) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
if (!Image->SegmentInfo.Segments[Index].Read &&
!Image->SegmentInfo.Segments[Index].Write &&
Image->SegmentInfo.Segments[Index].Execute) {
Permissions = UeSegmentPermX;
} else if (Image->SegmentInfo.Segments[Index].Read &&
!Image->SegmentInfo.Segments[Index].Write &&
Image->SegmentInfo.Segments[Index].Execute) {
Permissions = UeSegmentPermRX;
} else if (Image->SegmentInfo.Segments[Index].Read &&
Image->SegmentInfo.Segments[Index].Write) {
assert (!Image->SegmentInfo.Segments[Index].Execute);
Permissions = UeSegmentPermRW;
} else if (Image->SegmentInfo.Segments[Index].Read) {
assert (!Image->SegmentInfo.Segments[Index].Write);
assert (!Image->SegmentInfo.Segments[Index].Execute);
Permissions = UeSegmentPermR;
} else {
DEBUG_RAISE ();
return false;
}
Segment.ImageInfo = Image->SegmentInfo.Segments[Index].ImageSize >> 12U;
Segment.ImageInfo |= Permissions << 20U;
assert (UE_SEGMENT_SIZE (Segment.ImageInfo) == Image->SegmentInfo.Segments[Index].ImageSize);
assert (UE_SEGMENT_PERMISSIONS (Segment.ImageInfo) == Permissions);
Segment.FileSize = Xip ? Image->SegmentInfo.Segments[Index].ImageSize : Image->SegmentInfo.Segments[Index].UnpaddedSize;
Offset = ImageToolBufferAppend (Buffer, &Segment, sizeof (Segment));
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
}
return true;
}
static
bool
ToolImageUeRelocTableRequired (
const image_tool_image_info_t *Image
)
{
return Image->RelocInfo.NumRelocs != 0;
}
static
bool
ToolImageUeDebugTableRequired (
const image_tool_image_info_t *Image
)
{
// FIXME: Segment names when symbols are absent?
return Image->DebugInfo.SymbolsPath != NULL;
}
static
int8_t
InternalGetUeMachine (
uint16_t Machine
)
{
switch (Machine) {
case IMAGE_FILE_MACHINE_I386:
{
return UeMachineI386;
}
case IMAGE_FILE_MACHINE_X64:
{
return UeMachineX64;
}
case IMAGE_FILE_MACHINE_ARMTHUMB_MIXED:
{
return UeMachineArmThumbMixed;
}
case IMAGE_FILE_MACHINE_ARM64:
{
return UeMachineArm64;
}
case IMAGE_FILE_MACHINE_RISCV32:
{
return UeMachineRiscV32;
}
case IMAGE_FILE_MACHINE_RISCV64:
{
return UeMachineRiscV64;
}
case IMAGE_FILE_MACHINE_RISCV128:
{
return UeMachineRiscV128;
}
default:
{
DEBUG_RAISE ();
return -1;
}
}
}
static
bool
ToolImageEmitUeSegments (
image_tool_dynamic_buffer *Buffer,
const image_tool_image_info_t *Image
)
{
uint16_t Index;
uint32_t Offset;
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
Offset = ImageToolBufferAppend (
Buffer,
Image->SegmentInfo.Segments[Index].Data,
Image->SegmentInfo.Segments[Index].UnpaddedSize
);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
}
Offset = ImageToolBufferAppendReserveAlign (Buffer, UE_LOAD_TABLE_ALIGNMENT);
return Offset != MAX_UINT32;
}
static
void
ToolImageEmitUePadChainedRelocs (
const image_tool_image_info_t *Image
)
{
uint16_t SegIndex;
image_tool_segment_t *Segment;
uint32_t RelocIndex;
const image_tool_reloc_t *Reloc;
uint32_t EndOfReloc;
bool HasReloc;
uint32_t LastRelocEnd;
uint32_t EndOfRelocOffset;
LastRelocEnd = 0;
RelocIndex = 0;
for (SegIndex = 0; SegIndex < Image->SegmentInfo.NumSegments; ++SegIndex) {
Segment = &Image->SegmentInfo.Segments[SegIndex];
HasReloc = false;
for (; RelocIndex < Image->RelocInfo.NumRelocs; ++RelocIndex) {
Reloc = &Image->RelocInfo.Relocs[RelocIndex];
EndOfReloc = Reloc->Target + ToolImageGetRelocSize (Reloc->Type);
if (EndOfReloc > Segment->ImageAddress + Segment->ImageSize) {
break;
}
HasReloc = true;
LastRelocEnd = EndOfReloc;
}
if (HasReloc) {
assert (Segment->ImageAddress <= LastRelocEnd);
EndOfRelocOffset = LastRelocEnd - Segment->ImageAddress;
if (Segment->UnpaddedSize < EndOfRelocOffset) {
Segment->UnpaddedSize = EndOfRelocOffset;
}
}
}
}
static
uint32_t
ToolImageEmitUeGetFileOffset (
const image_tool_dynamic_buffer *Buffer,
uint32_t SegmentHeadersOffset,
uint32_t SegmentsOffset,
uint16_t NumSegments,
uint32_t Address,
uint32_t *Size
)
{
CONST UE_SEGMENT *Segments;
uint16_t Index;
uint32_t SegmentAddress;
uint32_t SegmentSize;
uint32_t SegmentOffset;
uint32_t OffsetInSegment;
Segments = ImageToolBufferGetPointer (Buffer, SegmentHeadersOffset);
SegmentAddress = 0;
SegmentOffset = SegmentsOffset;
for (
Index = 0;
// LCOV_EXCL_START
Index < NumSegments;
// LCOV_EXCL_STOP
++Index
) {
// LCOV_EXCL_START
if (SegmentAddress > Address) {
break;
}
// LCOV_EXCL_STOP
SegmentSize = UE_SEGMENT_SIZE (Segments[Index].ImageInfo);
OffsetInSegment = Address - SegmentAddress;
if (OffsetInSegment < SegmentSize) {
assert (OffsetInSegment < Segments[Index].FileSize);
*Size = Segments[Index].FileSize - OffsetInSegment;
return SegmentOffset + OffsetInSegment;
}
SegmentAddress += SegmentSize;
SegmentOffset += Segments[Index].FileSize;
}
// LCOV_EXCL_START
assert (false);
return MAX_UINT32;
// LCOV_EXCL_STOP
}
static
bool
ToolImageEmitUeRelocTable (
image_tool_dynamic_buffer *Buffer,
uint32_t LoadTableOffset,
uint32_t SegmentHeadersOffset,
uint32_t SegmentsOffset,
const image_tool_image_info_t *Image,
bool Chaining
)
{
uint32_t RelocTableOffset;
uint32_t RelocTableSize;
UE_FIXUP_ROOT RelocRoot;
uint32_t EntryFileOffset;
uint32_t PrevEntryFileOffset;
uint8_t RelocType;
uint32_t RelocTarget;
uint32_t RelocFileOffset;
uint64_t RelocValue;
uint8_t RelocSize;
uint32_t RelocOffset;
uint64_t ChainRelocInfo;
uint32_t ChainRelocInfo32;
uint8_t PrevRelocType;
uint32_t PrevRelocTarget;
uint32_t PrevRelocFileOffset;
uint64_t PrevRelocValue;
uint8_t PrevRelocSize;
uint64_t PrevChainRelocInfo;
uint32_t PrevChainRelocInfo32;
bool ChainInProgress;
bool ChainSupported;
uint16_t Fixup;
UE_LOAD_TABLE LoadTable;
uint32_t Index;
uint32_t RemainingSize;
uint32_t Offset;
assert (Image->RelocInfo.NumRelocs > 0);
ChainInProgress = false;
RelocTableOffset = ImageToolBufferGetSize (Buffer);
assert (IS_ALIGNED (RelocTableOffset, UE_LOAD_TABLE_ALIGNMENT));
PrevRelocType = 0;
PrevRelocTarget = 0;
PrevRelocFileOffset = 0;
PrevRelocValue = 0;
PrevRelocSize = 0;
EntryFileOffset = 0;
PrevEntryFileOffset = 0;
for (
Index = 0;
Index < Image->RelocInfo.NumRelocs;
++Index,
PrevRelocType = RelocType,
PrevRelocTarget = RelocTarget,
PrevRelocFileOffset = RelocFileOffset,
PrevRelocValue = RelocValue,
PrevRelocSize = RelocSize,
PrevEntryFileOffset = EntryFileOffset
) {
//
// Get current UE relocation fixup info.
//
RelocTarget = Image->RelocInfo.Relocs[Index].Target;
RelocFileOffset = ToolImageEmitUeGetFileOffset (
Buffer,
SegmentHeadersOffset,
SegmentsOffset,
Image->SegmentInfo.NumSegments,
RelocTarget,
&RemainingSize
);
assert (RelocFileOffset != MAX_UINT32);
// LCOV_EXCL_START
switch (Image->RelocInfo.Relocs[Index].Type) {
// LCOV_EXCL_STOP
case EFI_IMAGE_REL_BASED_HIGHLOW:
{
RelocSize = sizeof (UINT32);
RelocType = UeReloc32;
ChainSupported = Chaining;
break;
}
case EFI_IMAGE_REL_BASED_DIR64:
{
RelocSize = sizeof (UINT64);
RelocType = UeReloc64;
ChainSupported = Chaining;
break;
}
// LCOV_EXCL_START
default:
{
assert (false);
return false;
}
// LCOV_EXCL_STOP
}
// LCOV_EXCL_START
if (RemainingSize < RelocSize) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
assert (RelocSize <= sizeof (RelocValue));
RelocValue = 0;
ImageToolBufferRead (&RelocValue, RelocSize, Buffer, RelocFileOffset);
assert (RelocTarget >= (PrevRelocTarget + PrevRelocSize));
RelocOffset = RelocTarget - (PrevRelocTarget + PrevRelocSize);
//
// Handle UE relocation fixup chaining.
//
if (ChainInProgress) {
ChainInProgress = ChainSupported && (RelocOffset <= UE_CHAINED_RELOC_FIXUP_MAX_OFFSET) && (PrevRelocType == RelocType);
if (ChainInProgress && (RelocType == UeReloc32)) {
ChainRelocInfo32 = UE_CHAINED_RELOC_FIXUP_OFFSET_END;
ChainRelocInfo32 |= RelocValue << UE_CHAINED_RELOC_FIXUP_VALUE_32_SHIFT;
if ((ChainRelocInfo32 >> UE_CHAINED_RELOC_FIXUP_VALUE_32_SHIFT) != RelocValue) {
ChainInProgress = false;
ChainSupported = false;
RelocType = UeReloc32NoMeta;
}
}
if (ChainInProgress && (RelocType == UeReloc64)) {
PrevChainRelocInfo = RelocType;
PrevChainRelocInfo |= RelocOffset << 4U;
PrevChainRelocInfo |= PrevRelocValue << 16U;
assert (UE_CHAINED_RELOC_FIXUP_NEXT_OFFSET (PrevChainRelocInfo) == RelocOffset);
assert (UE_CHAINED_RELOC_FIXUP_NEXT_TYPE (PrevChainRelocInfo) == RelocType);
assert (UE_CHAINED_RELOC_FIXUP_VALUE (PrevChainRelocInfo) == PrevRelocValue);
assert (PrevRelocSize <= sizeof (PrevChainRelocInfo));
ImageToolBufferWrite (
Buffer,
PrevRelocFileOffset,
&PrevChainRelocInfo,
PrevRelocSize
);
} else if (ChainInProgress && (RelocType == UeReloc32)) {
PrevChainRelocInfo32 = RelocOffset;
PrevChainRelocInfo32 |= PrevRelocValue << UE_CHAINED_RELOC_FIXUP_VALUE_32_SHIFT;
assert (PrevRelocSize <= sizeof (PrevChainRelocInfo32));
ImageToolBufferWrite (
Buffer,
PrevRelocFileOffset,
&PrevChainRelocInfo32,
PrevRelocSize
);
}
}
if (ChainSupported && (RelocType == UeReloc64)) {
ChainRelocInfo = UE_CHAINED_RELOC_FIXUP_OFFSET_END << 4U;
ChainRelocInfo |= RelocValue << 16U;
if ((ChainRelocInfo >> 16U) != RelocValue) {
DEBUG_RAISE ();
return false;
}
assert (UE_CHAINED_RELOC_FIXUP_NEXT_OFFSET (ChainRelocInfo) == UE_CHAINED_RELOC_FIXUP_OFFSET_END);
assert (UE_CHAINED_RELOC_FIXUP_NEXT_TYPE (ChainRelocInfo) == 0);
assert (UE_CHAINED_RELOC_FIXUP_VALUE (ChainRelocInfo) == RelocValue);
assert (RelocSize <= sizeof (ChainRelocInfo));
ImageToolBufferWrite (
Buffer,
RelocFileOffset,
&ChainRelocInfo,
RelocSize
);
if (ChainInProgress) {
continue;
}
ChainInProgress = true;
} else if (ChainSupported && (RelocType == UeReloc32)) {
ChainRelocInfo32 = UE_CHAINED_RELOC_FIXUP_OFFSET_END;
ChainRelocInfo32 |= RelocValue << UE_CHAINED_RELOC_FIXUP_VALUE_32_SHIFT;
if ((ChainRelocInfo32 >> UE_CHAINED_RELOC_FIXUP_VALUE_32_SHIFT) != RelocValue) {
ChainInProgress = false;
ChainSupported = false;
RelocType = UeReloc32NoMeta;
} else {
assert (RelocSize <= sizeof (ChainRelocInfo32));
ImageToolBufferWrite (
Buffer,
RelocFileOffset,
&ChainRelocInfo32,
RelocSize
);
if (ChainInProgress) {
continue;
}
ChainInProgress = true;
}
}
if (Index > 0 && RelocOffset <= UE_HEAD_FIXUP_MAX_OFFSET) {
Fixup = PrevRelocType;
Fixup |= RelocOffset << 4U;
assert (UE_RELOC_FIXUP_TYPE (Fixup) == PrevRelocType);
assert (UE_RELOC_FIXUP_OFFSET (Fixup) == RelocOffset);
ImageToolBufferWrite (
Buffer,
PrevEntryFileOffset,
&Fixup,
sizeof (Fixup)
);
} else {
Offset = ImageToolBufferAppendReserveAlign (
Buffer,
UE_FIXUP_ROOT_ALIGNMENT
);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
RelocRoot.FirstOffset = RelocOffset;
Offset = ImageToolBufferAppend (Buffer, &RelocRoot, sizeof (RelocRoot));
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
}
Fixup = RelocType;
Fixup |= UE_HEAD_FIXUP_OFFSET_END << 4U;
assert (UE_RELOC_FIXUP_TYPE (Fixup) == RelocType);
assert (UE_RELOC_FIXUP_OFFSET (Fixup) == UE_HEAD_FIXUP_OFFSET_END);
EntryFileOffset = ImageToolBufferAppend (Buffer, &Fixup, sizeof (Fixup));
if (EntryFileOffset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
}
Offset = ImageToolBufferAppendReserveAlign (Buffer, UE_LOAD_TABLE_ALIGNMENT);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
RelocTableSize = ImageToolBufferGetSize (Buffer) - RelocTableOffset;
LoadTable.FileInfo = RelocTableSize >> 3U;
LoadTable.FileInfo |= UeLoadTableIdReloc << 29U;
assert (UE_LOAD_TABLE_ID (LoadTable.FileInfo) == UeLoadTableIdReloc);
assert (UE_LOAD_TABLE_SIZE (LoadTable.FileInfo) == RelocTableSize);
ImageToolBufferWrite (
Buffer,
LoadTableOffset,
&LoadTable,
sizeof (LoadTable)
);
return true;
}
static
bool
ToolImageEmitUeDebugTable (
image_tool_dynamic_buffer *Buffer,
uint32_t LoadTableOffset,
const image_tool_image_info_t *Image,
uint32_t BaseAddressSubtrahend
)
{
UE_DEBUG_TABLE DebugTable;
uint8_t SymSubtrahendFactor;
uint32_t DebugTableOffset;
uint32_t DebugTableSize;
uint16_t Index;
UE_SEGMENT_NAME SegmentName;
UE_LOAD_TABLE LoadTable;
uint32_t Offset;
assert (Image->DebugInfo.SymbolsPathLen <= MAX_UINT8);
assert (IS_ALIGNED (BaseAddressSubtrahend, Image->SegmentInfo.SegmentAlignment));
SymSubtrahendFactor = (uint8_t)(BaseAddressSubtrahend / Image->SegmentInfo.SegmentAlignment);
if (SymSubtrahendFactor > 0x03) {
DEBUG_RAISE ();
return false;
}
DebugTable.ImageInfo = SymSubtrahendFactor;
assert (UE_DEBUG_TABLE_IMAGE_INFO_SYM_SUBTRAHEND_FACTOR (DebugTable.ImageInfo) == SymSubtrahendFactor);
assert ((DebugTable.ImageInfo & 0xFCU) == 0);
DebugTable.SymbolsPathLength = (uint8_t)Image->DebugInfo.SymbolsPathLen;
DebugTableOffset = ImageToolBufferAppend (
Buffer,
&DebugTable,
sizeof (DebugTable)
);
if (DebugTableOffset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
assert (IS_ALIGNED (DebugTableOffset, UE_LOAD_TABLE_ALIGNMENT));
//
// Currently, this condition is always TRUE. However, it is not clear whether
// a debug table cannot make sense when debug symbols are stripped.
//
assert (Image->DebugInfo.SymbolsPath != NULL);
Offset = ImageToolBufferAppend (
Buffer,
Image->DebugInfo.SymbolsPath,
Image->DebugInfo.SymbolsPathLen + 1
);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
memset (&SegmentName, 0, sizeof (SegmentName));
strncpy (
(char *)SegmentName,
Image->SegmentInfo.Segments[Index].Name,
sizeof (SegmentName)
);
SegmentName[ARRAY_SIZE (SegmentName) - 1] = 0;
Offset = ImageToolBufferAppend (Buffer, SegmentName, sizeof (SegmentName));
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
}
Offset = ImageToolBufferAppendReserveAlign (Buffer, UE_LOAD_TABLE_ALIGNMENT);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
DebugTableSize = ImageToolBufferGetSize (Buffer) - DebugTableOffset;
LoadTable.FileInfo = DebugTableSize >> 3U;
LoadTable.FileInfo |= UeLoadTableIdDebug << 29U;
assert (UE_LOAD_TABLE_ID (LoadTable.FileInfo) == UeLoadTableIdDebug);
assert (UE_LOAD_TABLE_SIZE (LoadTable.FileInfo) == DebugTableSize);
ImageToolBufferWrite (
Buffer,
LoadTableOffset,
&LoadTable,
sizeof (LoadTable)
);
return true;
}
static
bool
ToolImageEmitUeLoadTables (
image_tool_dynamic_buffer *Buffer,
uint32_t LoadTablesOffset,
uint32_t SegmentHeadersOffset,
uint32_t SegmentsOffset,
const image_tool_image_info_t *Image,
uint32_t BaseAddressSubtrahend,
bool Chaining
)
{
bool Success;
if (ToolImageUeRelocTableRequired (Image)) {
Success = ToolImageEmitUeRelocTable (
Buffer,
LoadTablesOffset,
SegmentHeadersOffset,
SegmentsOffset,
Image,
Chaining
);
if (!Success) {
DEBUG_RAISE ();
return false;
}
LoadTablesOffset += sizeof (UE_LOAD_TABLE);
}
if (ToolImageUeDebugTableRequired (Image)) {
Success = ToolImageEmitUeDebugTable (
Buffer,
LoadTablesOffset,
Image,
BaseAddressSubtrahend
);
if (!Success) {
DEBUG_RAISE ();
return false;
}
}
return true;
}
static
bool
ToolImageStripEmptyPrefix (
uint32_t *BaseAddressSubtrahend,
image_tool_image_info_t *Image
)
{
bool Success;
uint32_t Subtrahend;
uint32_t Index;
if (Image->RelocInfo.RelocsStripped) {
DEBUG_RAISE ();
return false;
}
Subtrahend = Image->SegmentInfo.Segments[0].ImageAddress;
Success = ToolImageRelocate (
Image,
Image->HeaderInfo.BaseAddress - Subtrahend,
Subtrahend
);
// LCOV_EXCL_START
if (!Success) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
Image->HeaderInfo.BaseAddress += Subtrahend;
Image->HeaderInfo.EntryPointAddress -= Subtrahend;
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
Image->SegmentInfo.Segments[Index].ImageAddress -= Subtrahend;
}
for (Index = 0; Index < Image->RelocInfo.NumRelocs; ++Index) {
Image->RelocInfo.Relocs[Index].Target -= Subtrahend;
}
*BaseAddressSubtrahend = Subtrahend;
return true;
}
static
bool
ToolImageEmitUeFile (
image_tool_dynamic_buffer *Buffer,
const image_tool_image_info_t *Image,
uint32_t BaseAddressSubtrahend
)
{
bool Success;
UE_HEADER UeHdr;
uint8_t AlignmentExponent;
int8_t Machine;
uint8_t Subsystem;
uint8_t LastSegmentIndex;
uint8_t NumLoadTables;
uint32_t Offset;
bool Chaining;
uint32_t SegmentHeadersOffset;
uint32_t LoadTablesOffset;
uint32_t SegmentsOffset;
assert (Image->SegmentInfo.NumSegments > 0);
if (Image->SegmentInfo.NumSegments > UE_HEADER_NUM_SEGMENTS_MAX) {
DEBUG_RAISE ();
return false;
}
if (Image->HiiInfo.DataSize > 0) {
DEBUG_RAISE ();
return false;
}
AlignmentExponent = AlignmentToExponent (Image->SegmentInfo.SegmentAlignment);
if (12 > AlignmentExponent || AlignmentExponent > 27) {
DEBUG_RAISE ();
return false;
}
Machine = InternalGetUeMachine (Image->HeaderInfo.Machine);
if (Machine < 0) {
DEBUG_RAISE ();
return false;
}
NumLoadTables = ToolImageUeRelocTableRequired (Image) ? 1U : 0U;
NumLoadTables += ToolImageUeDebugTableRequired (Image) ? 1U : 0U;
Subsystem = (uint8_t)(Image->HeaderInfo.Subsystem - 10U);
LastSegmentIndex = (uint8_t)(Image->SegmentInfo.NumSegments - 1U);
Chaining = Image->HeaderInfo.BaseAddress == 0 &&
!Image->HeaderInfo.FixedAddress;
UeHdr.Magic = UE_HEADER_MAGIC;
UeHdr.Type = (Machine << 3U) | Subsystem;
assert (UE_HEADER_SUBSYSTEM (UeHdr.Type) == Subsystem);
assert (UE_HEADER_ARCH (UeHdr.Type) == Machine);
UeHdr.TableCounts = NumLoadTables;
UeHdr.TableCounts |= LastSegmentIndex << 3U;
assert (UE_HEADER_LAST_SEGMENT_INDEX (UeHdr.TableCounts) == LastSegmentIndex);
assert (UE_HEADER_NUM_LOAD_TABLES (UeHdr.TableCounts) == NumLoadTables);
UeHdr.EntryPointAddress = Image->HeaderInfo.EntryPointAddress;
UeHdr.ImageInfo = Image->HeaderInfo.BaseAddress >> 12ULL;
UeHdr.ImageInfo |= (uint64_t)Image->HeaderInfo.FixedAddress << 57ULL;
UeHdr.ImageInfo |= (uint64_t)Image->RelocInfo.RelocsStripped << 58ULL;
UeHdr.ImageInfo |= (uint64_t)Chaining << 59ULL;
UeHdr.ImageInfo |= (uint64_t)(AlignmentExponent - 12U) << 60ULL;
assert (UE_HEADER_BASE_ADDRESS (UeHdr.ImageInfo) == Image->HeaderInfo.BaseAddress);
assert ((UeHdr.ImageInfo & (0x1FULL << 52ULL)) == 0);
assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_FIXED_ADDRESS) != 0) == Image->HeaderInfo.FixedAddress);
assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_RELOCATION_FIXUPS_STRIPPED) != 0) == Image->RelocInfo.RelocsStripped);
assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_CHAINED_FIXUPS) != 0) == Chaining);
assert (UE_HEADER_SEGMENT_ALIGNMENT (UeHdr.ImageInfo) == Image->SegmentInfo.SegmentAlignment);
Offset = ImageToolBufferAppend (Buffer, &UeHdr, sizeof (UeHdr));
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
SegmentHeadersOffset = ImageToolBufferGetSize (Buffer);
Success = ToolImageEmitUeSegmentHeaders (Buffer, Image, false);
if (!Success) {
DEBUG_RAISE ();
return false;
}
LoadTablesOffset = ImageToolBufferAppendReserve (
Buffer,
NumLoadTables * sizeof (UE_LOAD_TABLE)
);
if (LoadTablesOffset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
SegmentsOffset = ImageToolBufferGetSize (Buffer);
Success = ToolImageEmitUeSegments (Buffer, Image);
if (!Success) {
DEBUG_RAISE ();
return false;
}
Success = ToolImageEmitUeLoadTables (
Buffer,
LoadTablesOffset,
SegmentHeadersOffset,
SegmentsOffset,
Image,
BaseAddressSubtrahend,
Chaining
);
if (!Success) {
DEBUG_RAISE ();
return false;
}
return true;
}
// FIXME: Find a better solution. Separate metadata storage?
static
bool
ToolImageEmitUeXipFile (
image_tool_dynamic_buffer *Buffer,
image_tool_image_info_t *Image,
bool Strip
)
{
bool Success;
UE_HEADER UeHdr;
uint8_t AlignmentExponent;
int8_t Machine;
uint8_t Subsystem;
uint8_t LastSegmentIndex;
uint8_t NumLoadTables;
uint32_t Offset;
uint16_t Index;
uint32_t SegmentHeadersOffset;
uint32_t LoadTablesOffset;
uint32_t SegmentsOffset;
assert (Image->SegmentInfo.NumSegments > 0);
if (Image->SegmentInfo.NumSegments > UE_HEADER_NUM_SEGMENTS_MAX) {
DEBUG_RAISE ();
return false;
}
if (Image->HiiInfo.DataSize > 0) {
DEBUG_RAISE ();
return false;
}
AlignmentExponent = AlignmentToExponent (Image->SegmentInfo.SegmentAlignment);
if (12 > AlignmentExponent || AlignmentExponent > 27) {
DEBUG_RAISE ();
return false;
}
Machine = InternalGetUeMachine (Image->HeaderInfo.Machine);
if (Machine < 0) {
DEBUG_RAISE ();
return false;
}
NumLoadTables = (!Strip && ToolImageUeRelocTableRequired (Image)) ? 1U : 0U;
Subsystem = (uint8_t)(Image->HeaderInfo.Subsystem - 10U);
LastSegmentIndex = (uint8_t)(Image->SegmentInfo.NumSegments - 1U);
UeHdr.Magic = UE_HEADER_MAGIC;
UeHdr.Type = (Machine << 3U) | Subsystem;
assert (UE_HEADER_SUBSYSTEM (UeHdr.Type) == Subsystem);
assert (UE_HEADER_ARCH (UeHdr.Type) == Machine);
UeHdr.TableCounts = NumLoadTables;
UeHdr.TableCounts |= LastSegmentIndex << 3U;
assert (UE_HEADER_LAST_SEGMENT_INDEX (UeHdr.TableCounts) == LastSegmentIndex);
assert (UE_HEADER_NUM_LOAD_TABLES (UeHdr.TableCounts) == NumLoadTables);
UeHdr.ImageInfo = Image->HeaderInfo.BaseAddress >> 12ULL;
UeHdr.ImageInfo |= UE_HEADER_IMAGE_INFO_XIP;
UeHdr.ImageInfo |= (uint64_t)Image->HeaderInfo.FixedAddress << 57ULL;
UeHdr.ImageInfo |= (uint64_t)Strip << 58ULL;
UeHdr.ImageInfo |= (uint64_t)(AlignmentExponent - 12U) << 60ULL;
assert (UE_HEADER_BASE_ADDRESS (UeHdr.ImageInfo) == Image->HeaderInfo.BaseAddress);
assert ((UeHdr.ImageInfo & (0xFULL << 52ULL)) == 0);
assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_FIXED_ADDRESS) != 0) == Image->HeaderInfo.FixedAddress);
assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_RELOCATION_FIXUPS_STRIPPED) != 0) == Strip);
assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_CHAINED_FIXUPS) != 0) == FALSE);
assert (((UeHdr.ImageInfo & UE_HEADER_IMAGE_INFO_XIP) != 0) == TRUE);
assert (UE_HEADER_SEGMENT_ALIGNMENT (UeHdr.ImageInfo) == Image->SegmentInfo.SegmentAlignment);
Offset = ImageToolBufferAppendReserve (Buffer, sizeof (UeHdr));
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
SegmentHeadersOffset = ImageToolBufferGetSize (Buffer);
Success = ToolImageEmitUeSegmentHeaders (Buffer, Image, true);
if (!Success) {
DEBUG_RAISE ();
return false;
}
LoadTablesOffset = ImageToolBufferAppendReserve (
Buffer,
NumLoadTables * sizeof (UE_LOAD_TABLE)
);
if (LoadTablesOffset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
Offset = ImageToolBufferAppendReserveAlign (
Buffer,
Image->SegmentInfo.SegmentAlignment
);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
SegmentsOffset = ImageToolBufferGetSize (Buffer);
UeHdr.EntryPointAddress = Image->HeaderInfo.EntryPointAddress + SegmentsOffset;
ImageToolBufferWrite (Buffer, 0, &UeHdr, sizeof (UeHdr));
Success = ToolImageRelocate (Image, Image->HeaderInfo.BaseAddress + SegmentsOffset, 0);
if (!Success) {
DEBUG_RAISE ();
return false;
}
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
Offset = ImageToolBufferAppend (
Buffer,
Image->SegmentInfo.Segments[Index].Data,
Image->SegmentInfo.Segments[Index].UnpaddedSize
);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
Offset = ImageToolBufferAppendReserve (
Buffer,
Image->SegmentInfo.Segments[Index].ImageSize - Image->SegmentInfo.Segments[Index].UnpaddedSize
);
if (Offset == MAX_UINT32) {
DEBUG_RAISE ();
return false;
}
}
if (!Strip && ToolImageUeRelocTableRequired (Image)) {
Success = ToolImageEmitUeRelocTable (
Buffer,
LoadTablesOffset,
SegmentHeadersOffset,
SegmentsOffset,
Image,
false
);
if (!Success) {
DEBUG_RAISE ();
return false;
}
}
return true;
}
void *
ToolImageEmitUe (
image_tool_image_info_t *Image,
uint32_t *FileSize,
bool Xip,
bool Strip
)
{
bool Success;
image_tool_dynamic_buffer Buffer;
uint32_t BaseAddressSubtrahend;
void *FileBuffer;
ImageToolBufferInit (&Buffer);
ImageInitUnpaddedSize (Image);
ToolImageEmitUePadChainedRelocs (Image);
if (Xip) {
Success = ToolImageEmitUeXipFile (&Buffer, Image, Strip);
} else {
Success = ToolImageStripEmptyPrefix (&BaseAddressSubtrahend, Image);
if (!Success) {
DEBUG_RAISE ();
return NULL;
}
if (Strip) {
ToolImageStripRelocs (Image);
}
Success = ToolImageEmitUeFile (&Buffer, Image, BaseAddressSubtrahend);
}
if (!Success) {
DEBUG_RAISE ();
ImageToolBufferFree (&Buffer);
return NULL;
}
FileBuffer = ImageToolBufferDump (FileSize, &Buffer);
ImageToolBufferFree (&Buffer);
if (FileBuffer == NULL) {
DEBUG_RAISE ();
return NULL;
}
return FileBuffer;
}