/** @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" #if PE_ARCH == 32 #define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS32 #define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC #elif PE_ARCH == 64 #define EFI_IMAGE_NT_HEADERS EFI_IMAGE_NT_HEADERS64 #define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC #endif #define PE_SUFFIX__(Name, Arch) Name##Arch #define PE_SUFFIX_(Name, Arch) PE_SUFFIX__ (Name, Arch) #define PE_SUFFIX(Name) PE_SUFFIX_ (Name, PE_ARCH) typedef struct { uint8_t NumExtraSections; uint32_t SizeOfHeaders; uint8_t NumberOfRvaAndSizes; uint16_t SizeOfOptionalHeader; uint32_t SectionHeadersSize; uint32_t ExtraSectionHeadersSize; } image_tool_emit_pe_hdr_info_t; typedef struct { const image_tool_image_info_t *Image; EFI_IMAGE_NT_HEADERS *PeHdr; image_tool_emit_pe_hdr_info_t HdrInfo; uint32_t SectionsSize; uint32_t HiiSectionAddress; uint32_t ExtraSectionsSize; uint32_t UnsignedFileSize; uint32_t RelocTableSize; uint32_t DebugTableSize; uint32_t FileAlignment; } image_tool_pe_emit_context_t; #define EmitPeGetHeaderSizes PE_SUFFIX (EmitPeGetHeaderSizes) static bool EmitPeGetHeaderSizes ( const image_tool_image_info_t *Image, image_tool_emit_pe_hdr_info_t *HdrInfo ) { assert (Image != NULL); assert (HdrInfo != NULL); if (Image->RelocInfo.NumRelocs > 0) { HdrInfo->ExtraSectionHeadersSize += sizeof (EFI_IMAGE_SECTION_HEADER); } if (Image->HiiInfo.DataSize > 0) { HdrInfo->ExtraSectionHeadersSize += sizeof (EFI_IMAGE_SECTION_HEADER); } if (Image->DebugInfo.SymbolsPath != NULL) { HdrInfo->ExtraSectionHeadersSize += sizeof (EFI_IMAGE_SECTION_HEADER); } HdrInfo->SectionHeadersSize = (uint32_t)Image->SegmentInfo.NumSegments * sizeof (EFI_IMAGE_SECTION_HEADER); HdrInfo->NumberOfRvaAndSizes = EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; HdrInfo->SizeOfOptionalHeader = sizeof (EFI_IMAGE_NT_HEADERS) - sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR) + HdrInfo->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY); HdrInfo->SizeOfHeaders = sizeof (EFI_IMAGE_DOS_HEADER) + sizeof (EFI_IMAGE_NT_HEADERS) + HdrInfo->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY) + HdrInfo->SectionHeadersSize + HdrInfo->ExtraSectionHeadersSize; return true; } #define EmitPeGetSectionsSize PE_SUFFIX (EmitPeGetSectionsSize) static bool EmitPeGetSectionsSize ( const image_tool_pe_emit_context_t *Context, uint32_t *SectionsSize ) { const image_tool_image_info_t *Image; uint8_t Index; uint32_t DataSize; bool Overflow; assert (Context != NULL); assert (SectionsSize != NULL); Image = Context->Image; *SectionsSize = 0; for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) { Overflow = BaseOverflowAlignUpU32 ( Image->SegmentInfo.Segments[Index].UnpaddedSize, Context->FileAlignment, &DataSize ); if (Overflow) { raise (); return false; } Overflow = BaseOverflowAddU32 (*SectionsSize, DataSize, SectionsSize); if (Overflow) { raise (); return false; } } return true; } #define EmitPeGetRelocSectionSize PE_SUFFIX (EmitPeGetRelocSectionSize) static bool EmitPeGetRelocSectionSize ( const image_tool_image_info_t *Image, uint32_t *RelocsSize ) { uint32_t RelocTableSize; uint32_t BlockAddress; uint32_t BlockSize; uint32_t Index; uint32_t RelocAddress; assert (Image != NULL); assert (RelocsSize != NULL); if (Image->RelocInfo.NumRelocs == 0) { *RelocsSize = 0; return true; } assert (Image->RelocInfo.NumRelocs <= MAX_UINT32); RelocTableSize = 0; BlockAddress = PAGE(Image->RelocInfo.Relocs[0].Target); BlockSize = sizeof (EFI_IMAGE_BASE_RELOCATION_BLOCK); for (Index = 0; Index < Image->RelocInfo.NumRelocs; ++Index) { RelocAddress = PAGE(Image->RelocInfo.Relocs[Index].Target); if (RelocAddress != BlockAddress) { BlockSize = ALIGN_VALUE (BlockSize, 4); RelocTableSize += BlockSize; BlockAddress = RelocAddress; BlockSize = sizeof (EFI_IMAGE_BASE_RELOCATION_BLOCK); } BlockSize += sizeof (UINT16); } BlockSize = ALIGN_VALUE (BlockSize, 4); RelocTableSize += BlockSize; *RelocsSize = RelocTableSize; return true; } #define EmitPeGetDebugSectionSize PE_SUFFIX (EmitPeGetDebugSectionSize) static void EmitPeGetDebugSectionSize ( const image_tool_image_info_t *Image, uint32_t *DebugSize ) { uint32_t Size; assert (Image != NULL); assert (DebugSize != NULL); Size = 0; if (Image->DebugInfo.SymbolsPath != NULL) { Size = sizeof (DebugData) + Image->DebugInfo.SymbolsPathLen + 1; } *DebugSize = Size; } #define EmitPeGetExtraSectionsSize PE_SUFFIX (EmitPeGetExtraSectionsSize) static bool EmitPeGetExtraSectionsSize ( image_tool_pe_emit_context_t *Context, uint32_t *SectionsSize ) { const image_tool_image_info_t *Image; bool Result; uint32_t AlignedRelocTableSize; bool Overflow; uint32_t AlignedDebugTableSize; uint32_t AlignedHiiTableSize; assert (Context != NULL); assert (SectionsSize != NULL); Image = Context->Image; Result = EmitPeGetRelocSectionSize (Image, &Context->RelocTableSize); if (!Result) { raise (); return false; } EmitPeGetDebugSectionSize (Image, &Context->DebugTableSize); Overflow = BaseOverflowAlignUpU32 ( Context->RelocTableSize, Context->FileAlignment, &AlignedRelocTableSize ); if (Overflow) { raise (); return false; } Overflow = BaseOverflowAlignUpU32 ( Context->DebugTableSize, Context->FileAlignment, &AlignedDebugTableSize ); if (Overflow) { raise (); return false; } AlignedHiiTableSize = 0; if (Context->Image->HiiInfo.DataSize > 0) { Overflow = BaseOverflowAddU32 ( mHiiResourceSectionHeaderSize, Context->Image->HiiInfo.DataSize, &AlignedHiiTableSize ); Overflow |= BaseOverflowAlignUpU32 ( AlignedHiiTableSize, Context->FileAlignment, &AlignedHiiTableSize ); if (Overflow) { raise (); return false; } } Overflow = BaseOverflowAddU32 ( AlignedRelocTableSize, AlignedDebugTableSize, SectionsSize ); if (Overflow) { raise (); return false; } Overflow = BaseOverflowAddU32 ( *SectionsSize, AlignedHiiTableSize, SectionsSize ); if (Overflow) { raise (); return false; } return true; } #define ToolImageEmitPeSectionHeaders PE_SUFFIX (ToolImageEmitPeSectionHeaders) static bool ToolImageEmitPeSectionHeaders ( const image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize, uint32_t AlignedHeaderSize ) { const image_tool_image_info_t *Image; uint16_t SectionHeadersSize; uint32_t SectionOffset; EFI_IMAGE_SECTION_HEADER *Sections; uint8_t Index; assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); Image = Context->Image; SectionHeadersSize = (uint16_t) (Image->SegmentInfo.NumSegments * sizeof (EFI_IMAGE_SECTION_HEADER)); SectionOffset = AlignedHeaderSize; Sections = (void *) *Buffer; assert (SectionHeadersSize <= *BufferSize); for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) { assert (Sections[Index].Characteristics == 0); if (Image->SegmentInfo.Segments[Index].Read) { Sections[Index].Characteristics |= EFI_IMAGE_SCN_MEM_READ; } if (Image->SegmentInfo.Segments[Index].Write) { Sections[Index].Characteristics |= EFI_IMAGE_SCN_MEM_WRITE; } if (Image->SegmentInfo.Segments[Index].Execute) { Sections[Index].Characteristics |= EFI_IMAGE_SCN_MEM_EXECUTE | EFI_IMAGE_SCN_CNT_CODE; } else { Sections[Index].Characteristics |= EFI_IMAGE_SCN_CNT_INITIALIZED_DATA; } Sections[Index].PointerToRawData = SectionOffset; Sections[Index].VirtualAddress = SectionOffset; Sections[Index].SizeOfRawData = ALIGN_VALUE (Image->SegmentInfo.Segments[Index].UnpaddedSize, Context->FileAlignment); Sections[Index].VirtualSize = Image->SegmentInfo.Segments[Index].ImageSize; strncpy ( (char *) Sections[Index].Name, Image->SegmentInfo.Segments[Index].Name, sizeof (Sections[Index].Name) ); SectionOffset += Sections[Index].SizeOfRawData; } if (Image->HeaderInfo.FixedAddress) { for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) { if ((Sections[Index].Characteristics & EFI_IMAGE_SCN_MEM_EXECUTE) == 0) { WriteUnaligned64 ( (VOID *)&Sections[Index].PointerToRelocations, Image->HeaderInfo.BaseAddress ); break; } } if (Index == Image->SegmentInfo.NumSegments) { raise (); return false; } } *BufferSize -= SectionHeadersSize; *Buffer += SectionHeadersSize; assert (SectionHeadersSize == Context->HdrInfo.SectionHeadersSize); return true; } #define ToolImageEmitPeExtraSectionHeaders PE_SUFFIX (ToolImageEmitPeExtraSectionHeaders) static bool ToolImageEmitPeExtraSectionHeaders ( image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize, uint32_t AlignedHeaderSize ) { uint32_t SectionHeadersSize; EFI_IMAGE_SECTION_HEADER *Section; uint32_t SectionOffset; uint32_t HiiSectionSize; assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); SectionHeadersSize = 0; SectionOffset = AlignedHeaderSize + Context->SectionsSize; if (Context->Image->HiiInfo.DataSize > 0) { ++Context->PeHdr->CommonHeader.FileHeader.NumberOfSections; assert (sizeof (EFI_IMAGE_SECTION_HEADER) <= *BufferSize); HiiSectionSize = mHiiResourceSectionHeaderSize + Context->Image->HiiInfo.DataSize; Section = (void *) *Buffer; strncpy ((char *)Section->Name, ".rsrc", sizeof (Section->Name)); Section->SizeOfRawData = ALIGN_VALUE (HiiSectionSize, Context->FileAlignment); Section->VirtualSize = ALIGN_VALUE (HiiSectionSize, Context->Image->SegmentInfo.SegmentAlignment); Section->Characteristics = EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | EFI_IMAGE_SCN_MEM_READ; Section->PointerToRawData = SectionOffset; Section->VirtualAddress = Section->PointerToRawData; Context->HiiSectionAddress = SectionOffset; SectionOffset += Section->SizeOfRawData; Context->PeHdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = HiiSectionSize; Context->PeHdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = Section->PointerToRawData; *BufferSize -= sizeof (EFI_IMAGE_SECTION_HEADER); *Buffer += sizeof (EFI_IMAGE_SECTION_HEADER); SectionHeadersSize += sizeof (EFI_IMAGE_SECTION_HEADER); } if (Context->RelocTableSize > 0) { ++Context->PeHdr->CommonHeader.FileHeader.NumberOfSections; assert (sizeof (EFI_IMAGE_SECTION_HEADER) <= *BufferSize); Section = (void *) *Buffer; strncpy ((char *)Section->Name, ".reloc", sizeof (Section->Name)); Section->SizeOfRawData = ALIGN_VALUE (Context->RelocTableSize, Context->FileAlignment); Section->VirtualSize = Section->SizeOfRawData; Section->Characteristics = EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | EFI_IMAGE_SCN_MEM_DISCARDABLE | EFI_IMAGE_SCN_MEM_READ; Section->PointerToRawData = SectionOffset; Section->VirtualAddress = Section->PointerToRawData; SectionOffset += Section->SizeOfRawData; Context->PeHdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = Context->RelocTableSize; Context->PeHdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = Section->PointerToRawData; *BufferSize -= sizeof (EFI_IMAGE_SECTION_HEADER); *Buffer += sizeof (EFI_IMAGE_SECTION_HEADER); SectionHeadersSize += sizeof (EFI_IMAGE_SECTION_HEADER); } if (Context->DebugTableSize > 0) { ++Context->PeHdr->CommonHeader.FileHeader.NumberOfSections; assert (sizeof (EFI_IMAGE_SECTION_HEADER) <= *BufferSize); Section = (void *) *Buffer; strncpy ((char *)Section->Name, ".debug", sizeof (Section->Name)); Section->SizeOfRawData = ALIGN_VALUE (Context->DebugTableSize, Context->FileAlignment); Section->VirtualSize = Section->SizeOfRawData; Section->Characteristics = EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | EFI_IMAGE_SCN_MEM_READ; Section->PointerToRawData = SectionOffset; Section->VirtualAddress = Section->PointerToRawData; SectionOffset += Section->SizeOfRawData; Context->PeHdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY); Context->PeHdr->DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress = Section->VirtualAddress; *BufferSize -= sizeof (EFI_IMAGE_SECTION_HEADER); *Buffer += sizeof (EFI_IMAGE_SECTION_HEADER); SectionHeadersSize += sizeof (EFI_IMAGE_SECTION_HEADER); } assert (SectionHeadersSize == Context->HdrInfo.ExtraSectionHeadersSize); assert (SectionOffset == Context->UnsignedFileSize); return true; } #define ToolImageEmitPeHeaders PE_SUFFIX (ToolImageEmitPeHeaders) static bool ToolImageEmitPeHeaders ( image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize, uint32_t AlignedHeaderSize ) { const image_tool_image_info_t *Image; EFI_IMAGE_DOS_HEADER *DosHdr; EFI_IMAGE_NT_HEADERS *PePlusHdr; bool Result; uint32_t HeaderPadding; assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); assert (sizeof (EFI_IMAGE_DOS_HEADER) <= *BufferSize); Image = Context->Image; DosHdr = (void *) *Buffer; DosHdr->e_magic = EFI_IMAGE_DOS_SIGNATURE; DosHdr->e_lfanew = sizeof (EFI_IMAGE_DOS_HEADER); *BufferSize -= sizeof (EFI_IMAGE_DOS_HEADER); *Buffer += sizeof (EFI_IMAGE_DOS_HEADER); assert (sizeof (EFI_IMAGE_NT_HEADERS) <= *BufferSize); PePlusHdr = (EFI_IMAGE_NT_HEADERS *)(VOID *) *Buffer; PePlusHdr->CommonHeader.Signature = EFI_IMAGE_NT_SIGNATURE; PePlusHdr->CommonHeader.FileHeader.Machine = Image->HeaderInfo.Machine; PePlusHdr->CommonHeader.FileHeader.NumberOfSections = (UINT16)Image->SegmentInfo.NumSegments; PePlusHdr->CommonHeader.FileHeader.SizeOfOptionalHeader = Context->HdrInfo.SizeOfOptionalHeader; PePlusHdr->CommonHeader.FileHeader.Characteristics = EFI_IMAGE_FILE_EXECUTABLE_IMAGE | EFI_IMAGE_FILE_LINE_NUMS_STRIPPED | EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED | EFI_IMAGE_FILE_LARGE_ADDRESS_AWARE; if (Image->RelocInfo.RelocsStripped) { PePlusHdr->CommonHeader.FileHeader.Characteristics |= EFI_IMAGE_FILE_RELOCS_STRIPPED; } PePlusHdr->Magic = EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC; PePlusHdr->AddressOfEntryPoint = Image->HeaderInfo.EntryPointAddress; PePlusHdr->SectionAlignment = Image->SegmentInfo.SegmentAlignment; PePlusHdr->FileAlignment = Context->FileAlignment; PePlusHdr->SizeOfHeaders = AlignedHeaderSize; PePlusHdr->SizeOfImage = AlignedHeaderSize; PePlusHdr->ImageBase = (UINTN)Image->HeaderInfo.BaseAddress; PePlusHdr->Subsystem = Image->HeaderInfo.Subsystem; PePlusHdr->NumberOfRvaAndSizes = Context->HdrInfo.NumberOfRvaAndSizes; STATIC_ASSERT( OFFSET_OF(EFI_IMAGE_NT_HEADERS, DataDirectory) == sizeof (EFI_IMAGE_NT_HEADERS), "The following code needs to be updated to consider padding." ); Context->PeHdr = PePlusHdr; *BufferSize -= sizeof (EFI_IMAGE_NT_HEADERS) + PePlusHdr->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY); *Buffer += sizeof (EFI_IMAGE_NT_HEADERS) + PePlusHdr->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY); Result = ToolImageEmitPeSectionHeaders (Context, Buffer, BufferSize, AlignedHeaderSize); if (!Result) { return false; } Result = ToolImageEmitPeExtraSectionHeaders (Context, Buffer, BufferSize, AlignedHeaderSize); if (!Result) { return false; } HeaderPadding = AlignedHeaderSize - Context->HdrInfo.SizeOfHeaders; assert (HeaderPadding <= *BufferSize); *BufferSize -= HeaderPadding; *Buffer += HeaderPadding; return true; } #define ToolImageEmitPeSections PE_SUFFIX (ToolImageEmitPeSections) static bool ToolImageEmitPeSections ( const image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize ) { const image_tool_image_info_t *Image; uint32_t SectionsSize; uint8_t Index; const image_tool_segment_t *Segment; uint32_t SectionPadding; bool FirstCode; #if PE_ARCH == 32 bool FirstData; #endif assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); assert (Context->SectionsSize <= *BufferSize); Image = Context->Image; SectionsSize = 0; FirstCode = true; #if PE_ARCH == 32 FirstData = true; #endif for (Index = 0; Index < Image->SegmentInfo.NumSegments; ++Index) { Segment = &Image->SegmentInfo.Segments[Index]; Context->PeHdr->SizeOfImage += Segment->ImageSize; if (Segment->Execute) { if (FirstCode) { Context->PeHdr->BaseOfCode = Segment->ImageAddress; FirstCode = false; } } #if PE_ARCH == 32 else { if (FirstData) { Context->PeHdr->BaseOfData = Segment->ImageAddress; FirstData = false; } } #endif assert (Segment->UnpaddedSize <= *BufferSize); memmove (*Buffer, Segment->Data, Segment->UnpaddedSize); *BufferSize -= Segment->UnpaddedSize; *Buffer += Segment->UnpaddedSize; SectionsSize += Segment->UnpaddedSize; SectionPadding = ALIGN_VALUE_ADDEND ( Segment->UnpaddedSize, Context->FileAlignment ); assert (SectionPadding <= *BufferSize); *BufferSize -= SectionPadding; *Buffer += SectionPadding; SectionsSize += SectionPadding; if (Segment->Execute) { Context->PeHdr->BaseOfCode = MIN(Context->PeHdr->BaseOfCode, Segment->ImageAddress); Context->PeHdr->SizeOfCode += Segment->ImageSize; } else { #if PE_ARCH == 32 Context->PeHdr->BaseOfData = MIN(Context->PeHdr->BaseOfData, Segment->ImageAddress); #endif Context->PeHdr->SizeOfInitializedData += Segment->ImageSize; } } assert (SectionsSize == Context->SectionsSize); return true; } #define ToolImageEmitPeRelocTable PE_SUFFIX (ToolImageEmitPeRelocTable) static bool ToolImageEmitPeRelocTable ( const image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize ) { const image_tool_image_info_t *Image; uint32_t RelocTableSize; EFI_IMAGE_BASE_RELOCATION_BLOCK *RelocBlock; uint32_t BlockAddress; uint32_t BlockNumRelocs; uint32_t Index; uint32_t RelocAddress; uint32_t RelocTablePadding; assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); if (Context->RelocTableSize == 0) { return true; } Image = Context->Image; Context->PeHdr->SizeOfImage += ALIGN_VALUE (Context->RelocTableSize, Image->SegmentInfo.SegmentAlignment); assert (Image->RelocInfo.NumRelocs > 0); assert (Image->RelocInfo.NumRelocs <= MAX_UINT32); assert (sizeof (EFI_IMAGE_BASE_RELOCATION_BLOCK) <= *BufferSize); RelocTableSize = 0; RelocBlock = (void *) *Buffer; BlockAddress = PAGE(Image->RelocInfo.Relocs[0].Target); BlockNumRelocs = 0; RelocBlock->VirtualAddress = BlockAddress; RelocBlock->SizeOfBlock = sizeof (*RelocBlock); for (Index = 0; Index < Image->RelocInfo.NumRelocs; ++Index) { RelocAddress = PAGE(Image->RelocInfo.Relocs[Index].Target); if (RelocAddress != BlockAddress) { RelocBlock->SizeOfBlock = ALIGN_VALUE (RelocBlock->SizeOfBlock, 4); assert (RelocBlock->SizeOfBlock <= *BufferSize); *BufferSize -= RelocBlock->SizeOfBlock; *Buffer += RelocBlock->SizeOfBlock; RelocTableSize += RelocBlock->SizeOfBlock; RelocBlock = (void *) *Buffer; BlockAddress = RelocAddress; BlockNumRelocs = 0; RelocBlock->VirtualAddress = BlockAddress; RelocBlock->SizeOfBlock = sizeof (*RelocBlock); } RelocBlock->SizeOfBlock += sizeof (*RelocBlock->Relocations); assert (RelocBlock->SizeOfBlock <= *BufferSize); RelocBlock->Relocations[BlockNumRelocs] = PAGE_OFF(Image->RelocInfo.Relocs[Index].Target); RelocBlock->Relocations[BlockNumRelocs] |= ((uint16_t)Image->RelocInfo.Relocs[Index].Type) << 12U; ++BlockNumRelocs; } RelocBlock->SizeOfBlock = ALIGN_VALUE (RelocBlock->SizeOfBlock, 4); assert (RelocBlock->SizeOfBlock <= *BufferSize); *BufferSize -= RelocBlock->SizeOfBlock; *Buffer += RelocBlock->SizeOfBlock; RelocTableSize += RelocBlock->SizeOfBlock; assert (RelocTableSize == Context->RelocTableSize); RelocTablePadding = ALIGN_VALUE_ADDEND ( RelocTableSize, Context->FileAlignment ); assert (RelocTablePadding <= *BufferSize); *BufferSize -= RelocTablePadding; *Buffer += RelocTablePadding; return true; } typedef struct { EFI_IMAGE_DEBUG_DIRECTORY_ENTRY CodeViewDirEntry; EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY CodeViewEntry; uint8_t PdbPath[]; } image_tool_debug_dir_t; STATIC_ASSERT( OFFSET_OF(image_tool_debug_dir_t, PdbPath) == sizeof (image_tool_debug_dir_t), "Flexible array aliases padding." ); #define ToolImageEmitPeDebugTable PE_SUFFIX (ToolImageEmitPeDebugTable) static bool ToolImageEmitPeDebugTable ( const image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize ) { const image_tool_image_info_t *Image; uint32_t DebugTablePadding; DebugData *Data; assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); Image = Context->Image; if (Context->DebugTableSize == 0) { return true; } Context->PeHdr->SizeOfImage += ALIGN_VALUE (Context->DebugTableSize, Image->SegmentInfo.SegmentAlignment); assert (Image->DebugInfo.SymbolsPathLen + 1 <= Context->DebugTableSize); assert (Context->DebugTableSize <= *BufferSize); Data = (DebugData *) *Buffer; Data->Dir.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW; Data->Dir.SizeOfData = sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY) + Image->DebugInfo.SymbolsPathLen + 1; Data->Dir.RVA = (Context->UnsignedFileSize - ALIGN_VALUE (Context->DebugTableSize, Context->FileAlignment)) + sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY); Data->Dir.FileOffset = Data->Dir.RVA; Data->Nb10.Signature = CODEVIEW_SIGNATURE_NB10; memmove (Data->Name, Image->DebugInfo.SymbolsPath, Image->DebugInfo.SymbolsPathLen + 1); assert (sizeof (*Data) + Image->DebugInfo.SymbolsPathLen + 1 == Context->DebugTableSize); *BufferSize -= Context->DebugTableSize; *Buffer += Context->DebugTableSize; DebugTablePadding = ALIGN_VALUE_ADDEND( Context->DebugTableSize, Context->FileAlignment ); assert (DebugTablePadding <= *BufferSize); *BufferSize -= DebugTablePadding; *Buffer += DebugTablePadding; return true; } #define ToolImageEmitPeHiiTable PE_SUFFIX (ToolImageEmitPeHiiTable) static bool ToolImageEmitPeHiiTable ( const image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize ) { const image_tool_image_info_t *Image; uint32_t HiiTablePadding; assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); Image = Context->Image; if (Image->HiiInfo.DataSize == 0) { return true; } Context->PeHdr->SizeOfImage += ALIGN_VALUE (mHiiResourceSectionHeaderSize + Image->HiiInfo.DataSize, Image->SegmentInfo.SegmentAlignment); assert (mHiiResourceSectionHeaderSize <= *BufferSize); InitializeHiiResouceSectionHeader ( *Buffer, Context->HiiSectionAddress, Context->Image->HiiInfo.DataSize ); *BufferSize -= mHiiResourceSectionHeaderSize; *Buffer += mHiiResourceSectionHeaderSize; assert (Image->HiiInfo.DataSize <= *BufferSize); memmove (*Buffer, Image->HiiInfo.Data, Image->HiiInfo.DataSize); *BufferSize -= Image->HiiInfo.DataSize; *Buffer += Image->HiiInfo.DataSize; HiiTablePadding = ALIGN_VALUE_ADDEND( mHiiResourceSectionHeaderSize + Image->HiiInfo.DataSize, Context->FileAlignment ); assert (HiiTablePadding <= *BufferSize); *BufferSize -= HiiTablePadding; *Buffer += HiiTablePadding; return true; } #define ToolImageEmitPeExtraSections PE_SUFFIX (ToolImageEmitPeExtraSections) static bool ToolImageEmitPeExtraSections ( const image_tool_pe_emit_context_t *Context, uint8_t **Buffer, uint32_t *BufferSize ) { uint32_t OldBufferSize; bool Result; assert (Context != NULL); assert (Buffer != NULL); assert (BufferSize != NULL); OldBufferSize = *BufferSize; Result = ToolImageEmitPeHiiTable (Context, Buffer, BufferSize); if (!Result) { return false; } Result = ToolImageEmitPeRelocTable (Context, Buffer, BufferSize); if (!Result) { return false; } Result = ToolImageEmitPeDebugTable (Context, Buffer, BufferSize); if (!Result) { return false; } assert ((OldBufferSize - *BufferSize) == Context->ExtraSectionsSize); return true; } #define ToolImageEmitPe PE_SUFFIX (ToolImageEmitPe) void * ToolImageEmitPe ( image_tool_image_info_t *Image, uint32_t *FileSize ) { image_tool_pe_emit_context_t Context; bool Result; uint32_t AlignedHeaderSize; bool Overflow; uint32_t SectionsOffset; void *FileBuffer; uint8_t *Buffer; uint32_t RemainingSize; uint32_t ExpectedSize; assert (Image != NULL); assert (FileSize != NULL); memset (&Context, 0, sizeof (Context)); // FIXME: Non-XIP is not well-supported right now. Context.FileAlignment = Image->SegmentInfo.SegmentAlignment; Image->HeaderInfo.IsXip = true; ImageInitUnpaddedSize (Image); Context.Image = Image; Result = EmitPeGetHeaderSizes (Image, &Context.HdrInfo); if (!Result) { raise (); return NULL; } Overflow = BaseOverflowAlignUpU32 ( Context.HdrInfo.SizeOfHeaders, Context.FileAlignment, &AlignedHeaderSize ); if (Overflow) { raise (); return NULL; } if (AlignedHeaderSize > Image->SegmentInfo.Segments[0].ImageAddress) { raise (); return NULL; } AlignedHeaderSize = (uint32_t)Image->SegmentInfo.Segments[0].ImageAddress; Result = EmitPeGetSectionsSize (&Context, &Context.SectionsSize); if (!Result) { raise (); return NULL; } Overflow = BaseOverflowAddU32 ( AlignedHeaderSize, Context.SectionsSize, &SectionsOffset ); if (Overflow) { raise (); return NULL; } Result = EmitPeGetExtraSectionsSize (&Context, &Context.ExtraSectionsSize); if (!Result) { raise (); return NULL; } Overflow = BaseOverflowAddU32 ( SectionsOffset, Context.ExtraSectionsSize, &Context.UnsignedFileSize ); if (Overflow) { raise (); return NULL; } FileBuffer = calloc (1, Context.UnsignedFileSize); if (FileBuffer == NULL) { raise (); return NULL; } Buffer = FileBuffer; RemainingSize = Context.UnsignedFileSize; ExpectedSize = Context.UnsignedFileSize; Result = ToolImageEmitPeHeaders (&Context, &Buffer, &RemainingSize, AlignedHeaderSize); if (!Result) { raise (); free (FileBuffer); return NULL; } ExpectedSize -= AlignedHeaderSize; assert (RemainingSize == ExpectedSize); Result = ToolImageEmitPeSections (&Context, &Buffer, &RemainingSize); if (!Result) { raise (); free (FileBuffer); return NULL; } ExpectedSize -= Context.SectionsSize; assert (RemainingSize == ExpectedSize); Result = ToolImageEmitPeExtraSections (&Context, &Buffer, &RemainingSize); if (!Result) { raise (); free (FileBuffer); return NULL; } ExpectedSize -= Context.ExtraSectionsSize; assert (RemainingSize == ExpectedSize); assert (RemainingSize == 0); Context.PeHdr->SizeOfInitializedData += Context.ExtraSectionsSize; *FileSize = Context.UnsignedFileSize; return FileBuffer; }