mirror of https://github.com/acidanthera/audk.git
650 lines
14 KiB
C
650 lines
14 KiB
C
|
/** @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;
|
||
|
|
||
|
assert (Segment != NULL);
|
||
|
assert (PreviousEndAddress != NULL);
|
||
|
|
||
|
if (!IS_ALIGNED (Segment->ImageSize, SegmentInfo->SegmentAlignment)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (Segment->Write && Segment->Execute) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// FIXME: Expand prior segment
|
||
|
if (Segment->ImageAddress != *PreviousEndAddress) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Overflow = BaseOverflowAddU32 (
|
||
|
Segment->ImageAddress,
|
||
|
Segment->ImageSize,
|
||
|
PreviousEndAddress
|
||
|
);
|
||
|
if (Overflow) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
bool
|
||
|
CheckToolImageSegmentInfo (
|
||
|
const image_tool_segment_info_t *SegmentInfo,
|
||
|
uint32_t *ImageSize
|
||
|
)
|
||
|
{
|
||
|
uint32_t Index;
|
||
|
bool Result;
|
||
|
|
||
|
assert (SegmentInfo != NULL);
|
||
|
assert (ImageSize != NULL);
|
||
|
|
||
|
if (!IS_POW2 (SegmentInfo->SegmentAlignment)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (SegmentInfo->NumSegments == 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!IS_ALIGNED (SegmentInfo->Segments[0].ImageAddress, SegmentInfo->SegmentAlignment)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
*ImageSize = SegmentInfo->Segments[0].ImageAddress;
|
||
|
for (Index = 0; Index < SegmentInfo->NumSegments; ++Index) {
|
||
|
Result = CheckToolImageSegment (
|
||
|
SegmentInfo,
|
||
|
&SegmentInfo->Segments[Index],
|
||
|
ImageSize
|
||
|
);
|
||
|
if (!Result) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
const image_tool_segment_t *
|
||
|
ImageGetSegmentByAddress (
|
||
|
uint32_t *Address,
|
||
|
uint32_t *RemainingSize,
|
||
|
const image_tool_segment_info_t *SegmentInfo
|
||
|
)
|
||
|
{
|
||
|
uint32_t Index;
|
||
|
|
||
|
assert (Address != NULL);
|
||
|
assert (SegmentInfo != NULL);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
bool
|
||
|
CheckToolImageReloc (
|
||
|
const image_tool_image_info_t *Image,
|
||
|
uint32_t ImageSize,
|
||
|
const image_tool_reloc_t *Reloc
|
||
|
)
|
||
|
{
|
||
|
uint32_t RelocOffset;
|
||
|
uint32_t RemainingSize;
|
||
|
const image_tool_segment_t *Segment;
|
||
|
uint16_t MovHigh;
|
||
|
uint16_t MovLow;
|
||
|
|
||
|
assert (Image != NULL);
|
||
|
assert (Reloc != NULL);
|
||
|
|
||
|
RelocOffset = Reloc->Target;
|
||
|
Segment = ImageGetSegmentByAddress (
|
||
|
&RelocOffset,
|
||
|
&RemainingSize,
|
||
|
&Image->SegmentInfo
|
||
|
);
|
||
|
if (Segment == NULL) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (Reloc->Type) {
|
||
|
case EFI_IMAGE_REL_BASED_HIGHLOW:
|
||
|
{
|
||
|
if (RemainingSize < sizeof (UINT32)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case EFI_IMAGE_REL_BASED_DIR64:
|
||
|
{
|
||
|
if (RemainingSize < sizeof (UINT64)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
|
||
|
{
|
||
|
if (RemainingSize < sizeof (UINT32)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!IS_ALIGNED (Reloc->Target, ALIGNOF (UINT16))) {
|
||
|
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) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*if (Segment->Write) {
|
||
|
printf("!!! writable reloc at %x !!!\n", Reloc->Target);
|
||
|
}*/
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
bool
|
||
|
CheckToolImageRelocInfo (
|
||
|
const image_tool_image_info_t *Image,
|
||
|
uint32_t ImageSize
|
||
|
)
|
||
|
{
|
||
|
const image_tool_reloc_info_t *RelocInfo;
|
||
|
uint32_t Index;
|
||
|
bool Result;
|
||
|
|
||
|
assert (Image != NULL);
|
||
|
|
||
|
RelocInfo = &Image->RelocInfo;
|
||
|
|
||
|
if (RelocInfo->NumRelocs == 0) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (RelocInfo->RelocsStripped && (RelocInfo->NumRelocs > 0)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (RelocInfo->NumRelocs > (MAX_UINT32 / sizeof (UINT16))) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Result = CheckToolImageReloc (Image, ImageSize, &RelocInfo->Relocs[0]);
|
||
|
if (!Result) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (Index = 1; Index < RelocInfo->NumRelocs; ++Index) {
|
||
|
if (RelocInfo->Relocs[Index].Target < RelocInfo->Relocs[Index - 1].Target) {
|
||
|
assert (false);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Result = CheckToolImageReloc (Image, ImageSize, &RelocInfo->Relocs[Index]);
|
||
|
if (!Result) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
bool
|
||
|
CheckToolImageDebugInfo (
|
||
|
const image_tool_debug_info_t *DebugInfo
|
||
|
)
|
||
|
{
|
||
|
assert (DebugInfo != NULL);
|
||
|
|
||
|
if (DebugInfo->SymbolsPath != NULL) {
|
||
|
// FIXME: UE-only?
|
||
|
if (DebugInfo->SymbolsPathLen > MAX_UINT8) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
CheckToolImage (
|
||
|
const image_tool_image_info_t *Image
|
||
|
)
|
||
|
{
|
||
|
bool Result;
|
||
|
uint32_t ImageSize;
|
||
|
|
||
|
assert (Image != NULL);
|
||
|
|
||
|
Result = CheckToolImageSegmentInfo (&Image->SegmentInfo, &ImageSize);
|
||
|
if (!Result) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Result = CheckToolImageRelocInfo (Image, ImageSize);
|
||
|
if (!Result) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Result = CheckToolImageDebugInfo (&Image->DebugInfo);
|
||
|
if (!Result) {
|
||
|
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;
|
||
|
|
||
|
if (Image->HeaderInfo.IsXip) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
for (; Segment->UnpaddedSize > 0; --Segment->UnpaddedSize) {
|
||
|
if (Segment->Data[Segment->UnpaddedSize - 1] != 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ToolImageDestruct (
|
||
|
image_tool_image_info_t *Image
|
||
|
)
|
||
|
{
|
||
|
uint8_t Index;
|
||
|
|
||
|
assert (Image != NULL);
|
||
|
|
||
|
if (Image->SegmentInfo.Segments != NULL) {
|
||
|
for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) {
|
||
|
free (Image->SegmentInfo.Segments[Index].Name);
|
||
|
free (Image->SegmentInfo.Segments[Index].Data);
|
||
|
}
|
||
|
|
||
|
free (Image->SegmentInfo.Segments);
|
||
|
}
|
||
|
|
||
|
free (Image->HiiInfo.Data);
|
||
|
free (Image->RelocInfo.Relocs);
|
||
|
free (Image->DebugInfo.SymbolsPath);
|
||
|
|
||
|
memset (Image, 0, sizeof (*Image));
|
||
|
}
|
||
|
|
||
|
bool
|
||
|
ToolImageRelocate (
|
||
|
image_tool_image_info_t *Image,
|
||
|
uint64_t BaseAddress
|
||
|
)
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
Adjust = BaseAddress - Image->HeaderInfo.BaseAddress;
|
||
|
|
||
|
if (Adjust == 0) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
for (Index = 0; Index < Image->RelocInfo.NumRelocs; ++Index) {
|
||
|
Reloc = &Image->RelocInfo.Relocs[Index];
|
||
|
|
||
|
RelocOffset = Reloc->Target;
|
||
|
Segment = ImageGetSegmentByAddress (
|
||
|
&RelocOffset,
|
||
|
&RemainingSize,
|
||
|
&Image->SegmentInfo
|
||
|
);
|
||
|
if (Segment == NULL) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (Reloc->Type) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
|
||
|
{
|
||
|
assert (RemainingSize >= sizeof (UINT32));
|
||
|
assert (IS_ALIGNED (Reloc->Target, ALIGNOF (UINT16)));
|
||
|
|
||
|
PeCoffThumbMovwMovtImmediateFixup (&Segment->Data[RelocOffset], Adjust);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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 *Image1,
|
||
|
const image_tool_image_info_t *Image2
|
||
|
)
|
||
|
{
|
||
|
int CmpResult;
|
||
|
uint32_t SegIndex;
|
||
|
const char *Name1;
|
||
|
const char *Name2;
|
||
|
uint32_t NameIndex;
|
||
|
|
||
|
//
|
||
|
// Compare HeaderInfo.
|
||
|
//
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
&Image1->HeaderInfo,
|
||
|
&Image2->HeaderInfo,
|
||
|
sizeof (Image1->HeaderInfo)
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Compare SegmentInfo.
|
||
|
// UnpaddedSize is deliberately omitted, as it's implicit by the equality of
|
||
|
// ImageSize and Data.
|
||
|
//
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
&Image1->SegmentInfo,
|
||
|
&Image2->SegmentInfo,
|
||
|
OFFSET_OF (image_tool_segment_info_t, Segments)
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (SegIndex = 0; SegIndex < Image1->SegmentInfo.NumSegments; ++SegIndex) {
|
||
|
CmpResult = memcmp (
|
||
|
&Image1->SegmentInfo.Segments[SegIndex],
|
||
|
&Image2->SegmentInfo.Segments[SegIndex],
|
||
|
OFFSET_OF (image_tool_segment_t, Name)
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Don't assume images generally support arbitrarily long names or names in
|
||
|
// general. Check prefix equiality as a best effort.
|
||
|
//
|
||
|
Name1 = Image1->SegmentInfo.Segments[SegIndex].Name;
|
||
|
Name2 = Image2->SegmentInfo.Segments[SegIndex].Name;
|
||
|
if (Name1 != NULL && Name2 != NULL) {
|
||
|
for (
|
||
|
NameIndex = 0;
|
||
|
Name1[NameIndex] != '\0' && Name2[NameIndex] != '\0';
|
||
|
++NameIndex
|
||
|
) {
|
||
|
if (Name1[NameIndex] != Name2[NameIndex]) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
Image1->SegmentInfo.Segments[SegIndex].Data,
|
||
|
Image2->SegmentInfo.Segments[SegIndex].Data,
|
||
|
Image1->SegmentInfo.Segments[SegIndex].ImageSize
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Compare RelocInfo.
|
||
|
//
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
&Image1->RelocInfo,
|
||
|
&Image2->RelocInfo,
|
||
|
OFFSET_OF (image_tool_reloc_info_t, Relocs)
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
Image1->RelocInfo.Relocs,
|
||
|
Image2->RelocInfo.Relocs,
|
||
|
Image1->RelocInfo.NumRelocs * sizeof (*Image1->RelocInfo.Relocs)
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Compare HiiInfo.
|
||
|
//
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
&Image1->HiiInfo,
|
||
|
&Image2->HiiInfo,
|
||
|
OFFSET_OF (image_tool_hii_info_t, Data)
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
Image1->HiiInfo.Data,
|
||
|
Image2->HiiInfo.Data,
|
||
|
Image1->HiiInfo.DataSize
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Compare DebugInfo.
|
||
|
//
|
||
|
|
||
|
CmpResult = memcmp (
|
||
|
&Image1->DebugInfo,
|
||
|
&Image2->DebugInfo,
|
||
|
OFFSET_OF (image_tool_debug_info_t, SymbolsPath)
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ((Image1->DebugInfo.SymbolsPath != NULL) != (Image2->DebugInfo.SymbolsPath != NULL)) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (Image1->DebugInfo.SymbolsPath != NULL) {
|
||
|
CmpResult = strcmp (
|
||
|
Image1->DebugInfo.SymbolsPath,
|
||
|
Image2->DebugInfo.SymbolsPath
|
||
|
);
|
||
|
if (CmpResult != 0) {
|
||
|
raise ();
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ToolImageStripRelocs (
|
||
|
image_tool_image_info_t *Image
|
||
|
)
|
||
|
{
|
||
|
Image->RelocInfo.NumRelocs = 0;
|
||
|
free (Image->RelocInfo.Relocs);
|
||
|
Image->RelocInfo.Relocs = NULL;
|
||
|
|
||
|
Image->RelocInfo.RelocsStripped = TRUE;
|
||
|
}
|