audk/BaseTools/ImageTool/Image.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

798 lines
18 KiB
C
Raw Normal View History

/** @file
Copyright (c) 2021, Marvin Häuser. All rights reserved.
Copyright (c) 2022, Mikhail Krichanov. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
**/
#include "ImageTool.h"
static
bool
CheckToolImageSegment (
const image_tool_segment_info_t *SegmentInfo,
const image_tool_segment_t *Segment,
uint32_t *PreviousEndAddress
)
{
bool Overflow;
// LCOV_EXCL_START
if (!IS_ALIGNED (Segment->ImageSize, SegmentInfo->SegmentAlignment)) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
if (Segment->Write && Segment->Execute) {
DEBUG_RAISE ();
return false;
}
if (Segment->ImageAddress != *PreviousEndAddress) {
DEBUG_RAISE ();
return false;
}
Overflow = BaseOverflowAddU32 (
Segment->ImageAddress,
Segment->ImageSize,
PreviousEndAddress
);
// LCOV_EXCL_START
if (Overflow) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
return true;
}
static
bool
CheckToolImageSegmentInfo (
const image_tool_segment_info_t *SegmentInfo,
uint32_t *ImageSize
)
{
uint32_t Index;
bool Result;
// LCOV_EXCL_START
if (!IS_POW2 (SegmentInfo->SegmentAlignment)) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
if (SegmentInfo->NumSegments == 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_START
if (!IS_ALIGNED (SegmentInfo->Segments[0].ImageAddress, SegmentInfo->SegmentAlignment)) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
*ImageSize = SegmentInfo->Segments[0].ImageAddress;
for (Index = 0; Index < SegmentInfo->NumSegments; ++Index) {
Result = CheckToolImageSegment (
SegmentInfo,
&SegmentInfo->Segments[Index],
ImageSize
);
if (!Result) {
DEBUG_RAISE ();
return false;
}
}
return true;
}
static
bool
CheckToolImageHeaderInfo (
const image_tool_header_info_t *HeaderInfo,
const image_tool_segment_info_t *SegmentInfo,
uint32_t ImageSize
)
{
if (SegmentInfo->Segments[0].ImageAddress > HeaderInfo->EntryPointAddress ||
HeaderInfo->EntryPointAddress > ImageSize) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_START
if (!IS_ALIGNED (HeaderInfo->BaseAddress, SegmentInfo->SegmentAlignment)) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
// LCOV_EXCL_START
if (HeaderInfo->BaseAddress + ImageSize < HeaderInfo->BaseAddress) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
return true;
}
const image_tool_segment_t *
ImageGetSegmentByAddress (
uint32_t *Address,
uint32_t *RemainingSize,
const image_tool_segment_info_t *SegmentInfo
)
{
uint32_t Index;
for (Index = 0; Index < SegmentInfo->NumSegments; ++Index) {
if ((SegmentInfo->Segments[Index].ImageAddress <= *Address)
&& (*Address < SegmentInfo->Segments[Index].ImageAddress + SegmentInfo->Segments[Index].ImageSize)) {
*Address -= SegmentInfo->Segments[Index].ImageAddress;
*RemainingSize = SegmentInfo->Segments[Index].ImageSize - *Address;
return &SegmentInfo->Segments[Index];
}
}
return NULL;
}
uint8_t
ToolImageGetRelocSize (
uint8_t Type
)
{
// LCOV_EXCL_START
switch (Type) {
// LCOV_EXCL_STOP
case EFI_IMAGE_REL_BASED_HIGHLOW:
{
return sizeof (UINT32);
}
case EFI_IMAGE_REL_BASED_DIR64:
{
return sizeof (UINT64);
}
#if 0
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
{
return sizeof (UINT32);
}
#endif
// LCOV_EXCL_START
default:
{
break;
}
// LCOV_EXCL_STOP
}
// LCOV_EXCL_START
assert (false);
return 0;
// LCOV_EXCL_STOP
}
static
bool
CheckToolImageReloc (
const image_tool_image_info_t *Image,
const image_tool_reloc_t *Reloc,
uint8_t RelocSize
)
{
uint32_t RelocOffset;
uint32_t RemainingSize;
const image_tool_segment_t *Segment;
#if 0
uint16_t MovHigh;
uint16_t MovLow;
#endif
RelocOffset = Reloc->Target;
Segment = ImageGetSegmentByAddress (
&RelocOffset,
&RemainingSize,
&Image->SegmentInfo
);
if (Segment == NULL) {
DEBUG_RAISE ();
return false;
}
if (RelocSize > RemainingSize) {
DEBUG_RAISE ();
return false;
}
#if 0
if (Reloc->Type == EFI_IMAGE_REL_BASED_ARM_MOV32T) {
if (!IS_ALIGNED (Reloc->Target, ALIGNOF (UINT16))) {
DEBUG_RAISE ();
return false;
}
MovHigh = *(const uint16_t *)&Segment->Data[RelocOffset];
MovLow = *(const uint16_t *)&Segment->Data[RelocOffset + 2];
if (((MovHigh & 0xFBF0U) != 0xF200U && (MovHigh & 0xFBF0U) != 0xF2C0U) ||
(MovLow & 0x8000U) != 0) {
DEBUG_RAISE ();
return false;
}
}
#endif
// FIXME: Update drivers?
if (Image->HeaderInfo.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER &&
Segment->Write) {
printf("!!! writable reloc at %x !!!\n", Reloc->Target);
//DEBUG_RAISE ();
//return false;
}
return true;
}
static
bool
CheckToolImageRelocInfo (
const image_tool_image_info_t *Image
)
{
const image_tool_reloc_info_t *RelocInfo;
uint8_t RelocSize;
uint32_t MinRelocTarget;
uint32_t Index;
bool Result;
RelocInfo = &Image->RelocInfo;
if (RelocInfo->NumRelocs == 0) {
return true;
}
// LCOV_EXCL_START
if (RelocInfo->RelocsStripped) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
if (RelocInfo->NumRelocs > (MAX_UINT32 / sizeof (UINT16))) {
DEBUG_RAISE ();
return false;
}
MinRelocTarget = 0;
for (Index = 0; Index < RelocInfo->NumRelocs; ++Index) {
if (RelocInfo->Relocs[Index].Target < MinRelocTarget) {
DEBUG_RAISE ();
return false;
}
RelocSize = ToolImageGetRelocSize (RelocInfo->Relocs[Index].Type);
// LCOV_EXCL_START
if (RelocSize == 0) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
Result = CheckToolImageReloc (Image, &RelocInfo->Relocs[Index], RelocSize);
if (!Result) {
DEBUG_RAISE ();
return false;
}
MinRelocTarget = RelocInfo->Relocs[Index].Target + RelocSize;
}
return true;
}
static
bool
CheckToolImageDebugInfo (
const image_tool_debug_info_t *DebugInfo
)
{
if (DebugInfo->SymbolsPathLen > MAX_UINT8) {
DEBUG_RAISE ();
return false;
}
return true;
}
bool
CheckToolImage (
const image_tool_image_info_t *Image
)
{
bool Result;
uint32_t ImageSize;
Result = CheckToolImageSegmentInfo (&Image->SegmentInfo, &ImageSize);
if (!Result) {
DEBUG_RAISE ();
return false;
}
Result = CheckToolImageHeaderInfo (
&Image->HeaderInfo,
&Image->SegmentInfo,
ImageSize
);
if (!Result) {
DEBUG_RAISE ();
return false;
}
Result = CheckToolImageRelocInfo (Image);
if (!Result) {
DEBUG_RAISE ();
return false;
}
Result = CheckToolImageDebugInfo (&Image->DebugInfo);
if (!Result) {
DEBUG_RAISE ();
return false;
}
return true;
}
void
ImageInitUnpaddedSize (
const image_tool_image_info_t *Image
)
{
uint32_t Index;
image_tool_segment_t *Segment;
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
Segment = &Image->SegmentInfo.Segments[Index];
Segment->UnpaddedSize = Segment->ImageSize;
for (; Segment->UnpaddedSize > 0; --Segment->UnpaddedSize) {
if (Segment->Data[Segment->UnpaddedSize - 1] != 0) {
break;
}
}
}
}
void
ToolImageDestruct (
image_tool_image_info_t *Image
)
{
uint16_t Index;
// LCOV_EXCL_START
if (Image->SegmentInfo.Segments != NULL) {
// LCOV_EXCL_STOP
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
// LCOV_EXCL_START
if (Image->SegmentInfo.Segments[Index].Name != NULL) {
// LCOV_EXCL_STOP
FreePool (Image->SegmentInfo.Segments[Index].Name);
}
// LCOV_EXCL_START
if (Image->SegmentInfo.Segments[Index].Data != NULL) {
// LCOV_EXCL_STOP
FreePool (Image->SegmentInfo.Segments[Index].Data);
}
}
// LCOV_EXCL_START
if (Image->SegmentInfo.Segments != NULL) {
// LCOV_EXCL_STOP
FreePool (Image->SegmentInfo.Segments);
}
}
// LCOV_EXCL_START
if (Image->HiiInfo.Data != NULL) {
// LCOV_EXCL_STOP
FreePool (Image->HiiInfo.Data);
}
// LCOV_EXCL_START
if (Image->RelocInfo.Relocs != NULL) {
// LCOV_EXCL_STOP
FreePool (Image->RelocInfo.Relocs);
}
// LCOV_EXCL_START
if (Image->DebugInfo.SymbolsPath != NULL) {
// LCOV_EXCL_STOP
FreePool (Image->DebugInfo.SymbolsPath);
}
memset (Image, 0, sizeof (*Image));
}
bool
ToolImageRelocate (
image_tool_image_info_t *Image,
uint64_t BaseAddress,
uint32_t IgnorePrefix
)
{
const image_tool_segment_t *LastSegment;
uint32_t ImageSize;
uint64_t Adjust;
const image_tool_reloc_t *Reloc;
uint32_t RelocOffset;
uint32_t RemainingSize;
const image_tool_segment_t *Segment;
uint32_t Index;
uint32_t RelocTarget32;
uint64_t RelocTarget64;
if (!IS_ALIGNED (BaseAddress, Image->SegmentInfo.SegmentAlignment)) {
DEBUG_RAISE ();
return false;
}
Adjust = BaseAddress - Image->HeaderInfo.BaseAddress;
if (Adjust == 0) {
return true;
}
LastSegment = &Image->SegmentInfo.Segments[Image->SegmentInfo.NumSegments - 1];
ImageSize = LastSegment->ImageAddress + LastSegment->ImageSize;
//
// When removing the image header prefix, BaseAddress + ImageSize may indeed
// overflow. The important part is that the address starting from the first
// image segment does not.
//
if (BaseAddress + ImageSize < BaseAddress + IgnorePrefix) {
DEBUG_RAISE ();
return false;
}
for (Index = 0; Index < Image->RelocInfo.NumRelocs; ++Index) {
Reloc = &Image->RelocInfo.Relocs[Index];
RelocOffset = Reloc->Target;
Segment = ImageGetSegmentByAddress (
&RelocOffset,
&RemainingSize,
&Image->SegmentInfo
);
assert (Segment != NULL);
// LCOV_EXCL_START
switch (Reloc->Type) {
// LCOV_EXCL_STOP
case EFI_IMAGE_REL_BASED_HIGHLOW:
{
assert (RemainingSize >= sizeof (UINT32));
RelocTarget32 = ReadUnaligned32 ((CONST VOID *)&Segment->Data[RelocOffset]);
RelocTarget32 += (uint32_t)Adjust;
WriteUnaligned32 ((VOID *)&Segment->Data[RelocOffset], RelocTarget32);
break;
}
case EFI_IMAGE_REL_BASED_DIR64:
{
assert (RemainingSize >= sizeof (UINT64));
RelocTarget64 = ReadUnaligned64 ((CONST VOID *)&Segment->Data[RelocOffset]);
RelocTarget64 += Adjust;
WriteUnaligned64 ((VOID *)&Segment->Data[RelocOffset], RelocTarget64);
break;
}
#if 0
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
{
assert (RemainingSize >= sizeof (UINT32));
assert (IS_ALIGNED (Reloc->Target, ALIGNOF (UINT16)));
PeCoffThumbMovwMovtImmediateFixup (&Segment->Data[RelocOffset], Adjust);
break;
}
#endif
// LCOV_EXCL_START
default:
{
assert (false);
return false;
}
// LCOV_EXCL_STOP
}
}
Image->HeaderInfo.BaseAddress = BaseAddress;
return true;
}
static
int
ToolImageRelocCompare (
IN const void *Buffer1,
IN const void *Buffer2
)
{
const image_tool_reloc_t *Reloc1;
const image_tool_reloc_t *Reloc2;
Reloc1 = (const image_tool_reloc_t *)Buffer1;
Reloc2 = (const image_tool_reloc_t *)Buffer2;
if (Reloc1->Target < Reloc2->Target) {
return -1;
}
if (Reloc1->Target > Reloc2->Target) {
return 1;
}
return 0;
}
void
ToolImageSortRelocs (
image_tool_image_info_t *Image
)
{
if (Image->RelocInfo.Relocs == NULL) {
return;
}
qsort (
Image->RelocInfo.Relocs,
Image->RelocInfo.NumRelocs,
sizeof (*Image->RelocInfo.Relocs),
ToolImageRelocCompare
);
}
bool
ToolImageCompare (
const image_tool_image_info_t *DestImage,
const image_tool_image_info_t *SourceImage
)
{
int CmpResult;
uint32_t SegIndex;
const char *DestName;
const char *SourceName;
uint32_t NameIndex;
//
// Compare HeaderInfo.
//
CmpResult = memcmp (
&DestImage->HeaderInfo,
&SourceImage->HeaderInfo,
sizeof (DestImage->HeaderInfo)
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
//
// Compare SegmentInfo.
// UnpaddedSize is deliberately omitted, as it's implicit by the equality of
// ImageSize and Data.
//
CmpResult = memcmp (
&DestImage->SegmentInfo,
&SourceImage->SegmentInfo,
OFFSET_OF (image_tool_segment_info_t, Segments)
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
for (SegIndex = 0; SegIndex < DestImage->SegmentInfo.NumSegments; ++SegIndex) {
CmpResult = memcmp (
&DestImage->SegmentInfo.Segments[SegIndex],
&SourceImage->SegmentInfo.Segments[SegIndex],
OFFSET_OF (image_tool_segment_t, Name)
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
//
// Don't assume images generally support arbitrarily long names or names in
// general. Check prefix equiality as a best effort.
//
DestName = DestImage->SegmentInfo.Segments[SegIndex].Name;
SourceName = SourceImage->SegmentInfo.Segments[SegIndex].Name;
// LCOV_EXCL_START
if (DestName != NULL && SourceName == NULL) {
assert (false);
return false;
}
// LCOV_EXCL_STOP
//
// When omitting the debug info, some file formats (e.g., UE) may not
// contain segment names.
//
if (DestName != NULL) {
for (
NameIndex = 0;
DestName[NameIndex] != '\0' &&
// LCOV_EXCL_START
SourceName[NameIndex] != '\0';
// LCOV_EXCL_STOP
++NameIndex
) {
// LCOV_EXCL_START
if (DestName[NameIndex] != SourceName[NameIndex]) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
}
}
if (DestImage->SegmentInfo.Segments[SegIndex].ImageSize != 0) {
CmpResult = memcmp (
DestImage->SegmentInfo.Segments[SegIndex].Data,
SourceImage->SegmentInfo.Segments[SegIndex].Data,
DestImage->SegmentInfo.Segments[SegIndex].ImageSize
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
}
}
//
// Compare RelocInfo.
//
CmpResult = memcmp (
&DestImage->RelocInfo,
&SourceImage->RelocInfo,
OFFSET_OF (image_tool_reloc_info_t, Relocs)
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
if (DestImage->RelocInfo.NumRelocs != 0) {
CmpResult = memcmp (
DestImage->RelocInfo.Relocs,
SourceImage->RelocInfo.Relocs,
DestImage->RelocInfo.NumRelocs * sizeof (*DestImage->RelocInfo.Relocs)
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
}
//
// Compare HiiInfo.
//
CmpResult = memcmp (
&DestImage->HiiInfo,
&SourceImage->HiiInfo,
OFFSET_OF (image_tool_hii_info_t, Data)
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
// LCOV_EXCL_START
if (DestImage->HiiInfo.DataSize != 0) {
CmpResult = memcmp (
DestImage->HiiInfo.Data,
SourceImage->HiiInfo.Data,
DestImage->HiiInfo.DataSize
);
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
}
// LCOV_EXCL_STOP
//
// Compare DebugInfo.
//
CmpResult = memcmp (
&DestImage->DebugInfo,
&SourceImage->DebugInfo,
OFFSET_OF (image_tool_debug_info_t, SymbolsPath)
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
// LCOV_EXCL_START
if ((DestImage->DebugInfo.SymbolsPath != NULL) != (SourceImage->DebugInfo.SymbolsPath != NULL)) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
if (DestImage->DebugInfo.SymbolsPath != NULL) {
CmpResult = strcmp (
DestImage->DebugInfo.SymbolsPath,
SourceImage->DebugInfo.SymbolsPath
);
// LCOV_EXCL_START
if (CmpResult != 0) {
DEBUG_RAISE ();
return false;
}
// LCOV_EXCL_STOP
}
return true;
}
void
ToolImageStripRelocs (
image_tool_image_info_t *Image
)
{
Image->RelocInfo.NumRelocs = 0;
if (Image->RelocInfo.Relocs != NULL) {
FreePool (Image->RelocInfo.Relocs);
}
Image->RelocInfo.Relocs = NULL;
Image->RelocInfo.RelocsStripped = TRUE;
}