mirror of
https://github.com/acidanthera/audk.git
synced 2025-04-08 17:05:09 +02:00
1119 lines
30 KiB
C
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;
|
|
}
|