/** @file Copyright (c) 2022, Mikhail Krichanov. All rights reserved. SPDX-License-Identifier: BSD-3-Clause **/ #include "ImageTool.h" #include "ElfScanCommon.h" #include "../../UefiPayloadPkg/PayloadLoaderPeim/ElfLib/ElfCommon.h" #if ELF_ARCH == 32 #include "../../UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf32.h" #define ELFCLASS ELFCLASS32 #define Elf_Ehdr Elf32_Ehdr #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Rel Elf32_Rel #define Elf_Rela Elf32_Rela #define Elf_Size Elf32_Size #define Elf_Addr Elf32_Addr #define ELF_R_TYPE ELF32_R_TYPE #define ELF_R_SYM ELF32_R_SYM #elif ELF_ARCH == 64 #include "../../UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64.h" #define ELFCLASS ELFCLASS64 #define Elf_Ehdr Elf64_Ehdr #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Rel Elf64_Rel #define Elf_Rela Elf64_Rela #define Elf_Size Elf64_Size #define Elf_Addr Elf64_Addr #define ELF_R_TYPE ELF64_R_TYPE #define ELF_R_SYM ELF64_R_SYM #endif #define ELF_SUFFIX__(Name, Arch) Name##Arch #define ELF_SUFFIX_(Name, Arch) ELF_SUFFIX__ (Name, Arch) #define ELF_SUFFIX(Name) ELF_SUFFIX_ (Name, ELF_ARCH) typedef struct { const Elf_Ehdr *Ehdr; uint32_t Alignment; Elf_Addr BaseAddress; bool HasPieRelocs; } tool_elf_scan_context; #define GetShrbyIndex ELF_SUFFIX(GetShrbyIndex) static Elf_Shdr * GetShdrByIndex ( IN const Elf_Ehdr *Ehdr, IN UINT32 Index ) { UINTN Offset; assert (Index < Ehdr->e_shnum); Offset = (UINTN)Ehdr->e_shoff + Index * Ehdr->e_shentsize; return (Elf_Shdr *)((UINT8 *)Ehdr + Offset); } #define GetString ELF_SUFFIX(GetString) static const char * GetString ( IN const Elf_Ehdr *Ehdr, IN UINT32 Offset, IN UINT32 Index ) { const Elf_Shdr *Shdr; if (Index == 0) { Shdr = GetShdrByIndex (Ehdr, Ehdr->e_shstrndx); } else { Shdr = GetShdrByIndex (Ehdr, Index); } if (Offset >= Shdr->sh_size) { fprintf (stderr, "ImageTool: Invalid ELF string offset\n"); return NULL; } return (const char *)Ehdr + Shdr->sh_offset + Offset; } #define IsShdrLoadable ELF_SUFFIX(IsShdrLoadable) static BOOLEAN IsShdrLoadable ( IN const Elf_Ehdr *Ehdr, IN const Elf_Shdr *Shdr ) { return (Shdr->sh_flags & SHF_ALLOC) != 0; } #define ParseElfFile ELF_SUFFIX(ParseElfFile) static RETURN_STATUS ParseElfFile ( OUT tool_elf_scan_context *Context, IN const void *File, IN uint32_t FileSize ) { static const unsigned char Ident[] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS, ELFDATA2LSB }; const Elf_Ehdr *Ehdr; bool AlignmentSet; Elf_Size Alignment; bool BaseAddressSet; Elf_Addr BaseAddress; bool HasPieRelocs; const Elf_Shdr *Shdr; UINTN Offset; UINT32 Index; char *Last; assert (File != NULL || FileSize == 0); Ehdr = (const Elf_Ehdr *)File; // // Check header // if ((FileSize < sizeof (*Ehdr)) || (memcmp (Ident, Ehdr->e_ident, sizeof (Ident)) != 0)) { return RETURN_UNSUPPORTED; } if ((Ehdr->e_type != ET_EXEC) && (Ehdr->e_type != ET_DYN)) { fprintf (stderr, "ImageTool: ELF e_type not ET_EXEC or ET_DYN\n"); return RETURN_INCOMPATIBLE_VERSION; } // // Check section headers // BaseAddressSet = false; BaseAddress = 0; Alignment = 0; AlignmentSet = false; HasPieRelocs = false; for (Index = 0; Index < Ehdr->e_shnum; ++Index) { Offset = (UINTN)Ehdr->e_shoff + Index * Ehdr->e_shentsize; if (FileSize < (Offset + sizeof (*Shdr))) { fprintf (stderr, "ImageTool: ELF section header is outside file\n"); return RETURN_VOLUME_CORRUPTED; } Shdr = (const Elf_Shdr *)((const char *)Ehdr + Offset); if ((Shdr->sh_type != SHT_NOBITS) && ((FileSize < Shdr->sh_offset) || ((FileSize - Shdr->sh_offset) < Shdr->sh_size))) { fprintf (stderr, "ImageTool: ELF section %d points outside file\n", Index); return RETURN_VOLUME_CORRUPTED; } if (Shdr->sh_link >= Ehdr->e_shnum) { fprintf (stderr, "ImageTool: ELF %d-th section's sh_link is out of range\n", Index); return RETURN_VOLUME_CORRUPTED; } if ((Shdr->sh_type == SHT_RELA) || (Shdr->sh_type == SHT_REL)) { if (Shdr->sh_info >= Ehdr->e_shnum) { fprintf (stderr, "ImageTool: ELF %d-th section's sh_info is out of range\n", Index); return RETURN_VOLUME_CORRUPTED; } if (Shdr->sh_info == 0) { HasPieRelocs = TRUE; } } if (!BaseAddressSet || Shdr->sh_addr < BaseAddress) { BaseAddressSet = TRUE; BaseAddress = Shdr->sh_addr; } if (!IsShdrLoadable (Ehdr, Shdr)) { continue; } if (!AlignmentSet || Alignment < Shdr->sh_addralign) { AlignmentSet = TRUE; Alignment = Shdr->sh_addralign; } } if (Ehdr->e_shstrndx >= Ehdr->e_shnum) { fprintf (stderr, "ImageTool: Invalid section name string table\n"); return RETURN_VOLUME_CORRUPTED; } Shdr = GetShdrByIndex (Ehdr, Ehdr->e_shstrndx); if (Shdr->sh_type != SHT_STRTAB) { fprintf (stderr, "ImageTool: ELF string table section has wrong type\n"); return RETURN_VOLUME_CORRUPTED; } Last = (char *)((UINT8 *)Ehdr + Shdr->sh_offset + Shdr->sh_size - 1); if (*Last != '\0') { fprintf (stderr, "ImageTool: ELF string table section is not NUL-terminated\n"); return RETURN_VOLUME_CORRUPTED; } if (!AlignmentSet || (!IS_POW2(Alignment)) || (Alignment > MAX_PE_ALIGNMENT)) { fprintf (stderr, "ImageTool: Invalid section alignment\n"); return RETURN_VOLUME_CORRUPTED; } if (!BaseAddressSet || !IS_ALIGNED (BaseAddress, Alignment)) { fprintf (stderr, "ImageTool: Invalid base address %llx\n", (unsigned long long)BaseAddress); return RETURN_VOLUME_CORRUPTED; } memset (Context, 0, sizeof (*Context)); Context->Ehdr = Ehdr; Context->Alignment = (uint32_t)Alignment; Context->BaseAddress = BaseAddress; Context->HasPieRelocs = HasPieRelocs; return RETURN_SUCCESS; } #define ProcessRelocSection ELF_SUFFIX(ProcessRelocSection) static bool ProcessRelocSection ( const tool_elf_scan_context *Context, const Elf_Shdr *Shdr ) { const Elf_Ehdr *Ehdr; bool HasPieRelocs; const Elf_Shdr *SecShdr; Ehdr = Context->Ehdr; HasPieRelocs = Context->HasPieRelocs; // // PIE relocations will target dummy section 0. // if (Shdr->sh_info != 0) { // // If PIE relocations exist, prefer them. // if (HasPieRelocs) { return FALSE; } // // Only translate relocations targetting sections that are translated. // SecShdr = GetShdrByIndex (Ehdr, Shdr->sh_info); if (!IsShdrLoadable (Ehdr, SecShdr)) { return FALSE; } } else { assert (HasPieRelocs); } return TRUE; } #define SetRelocs ELF_SUFFIX(SetRelocs) static RETURN_STATUS SetRelocs ( OUT image_tool_image_info_t *ImageInfo, IN const tool_elf_scan_context *Context ) { const Elf_Ehdr *Ehdr; Elf_Addr BaseAddress; UINT32 Index; const Elf_Shdr *RelShdr; UINTN RelIdx; const Elf_Rela *Rel; UINT32 RelNum; Ehdr = Context->Ehdr; BaseAddress = Context->BaseAddress; RelNum = 0; for (Index = 0; Index < Ehdr->e_shnum; Index++) { RelShdr = GetShdrByIndex (Ehdr, Index); if ((RelShdr->sh_type != SHT_REL) && (RelShdr->sh_type != SHT_RELA)) { continue; } if (!ProcessRelocSection (Context, RelShdr)) { continue; } for (RelIdx = 0; RelIdx < RelShdr->sh_size; RelIdx += (UINTN)RelShdr->sh_entsize) { Rel = (const Elf_Rela *)((const char *)Ehdr + RelShdr->sh_offset + RelIdx); // // Assume ELF virtual addresses match corresponding PE virtual adresses one to one, // so we don't need to recalculate relocations computed by the linker at all r_offset's. // We only need to transform ELF relocations' format into PE one. // #if ELF_ARCH == 64 if (Ehdr->e_machine == EM_X86_64) { switch (ELF_R_TYPE(Rel->r_info)) { case R_X86_64_NONE: break; case R_X86_64_RELATIVE: case R_X86_64_64: ImageInfo->RelocInfo.Relocs[RelNum].Type = EFI_IMAGE_REL_BASED_DIR64; ImageInfo->RelocInfo.Relocs[RelNum].Target = (uint32_t)(Rel->r_offset - BaseAddress); ++RelNum; break; case R_X86_64_32: ImageInfo->RelocInfo.Relocs[RelNum].Type = EFI_IMAGE_REL_BASED_HIGHLOW; ImageInfo->RelocInfo.Relocs[RelNum].Target = (uint32_t)(Rel->r_offset - BaseAddress); ++RelNum; break; case R_X86_64_32S: case R_X86_64_PLT32: case R_X86_64_PC32: break; case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: // // Relocations of these types point to instructions' arguments containing // offsets relative to RIP leading to .got entries. As sections' virtual // addresses do not change during ELF->PE transform, we don't need to // add them to relocations' list. But .got entries contain virtual // addresses which must be updated. // // At r_offset the following value is stored: G + GOT + A - P. // To derive .got entry address (G + GOT) compute: value - A + P. // // Such a method of finding relocatable .got entries can not be used, // due to a BUG in clang compiler, which sometimes generates // R_X86_64_REX_GOTPCRELX relocations instead of R_X86_64_PC32. // break; default: fprintf ( stderr, "ImageTool: Unsupported ELF EM_X86_64 relocation 0x%llx in %s\n", (unsigned long long)ELF_R_TYPE(Rel->r_info), ImageInfo->DebugInfo.SymbolsPath ); return RETURN_INCOMPATIBLE_VERSION; } } else if (Ehdr->e_machine == EM_AARCH64) { switch (ELF_R_TYPE(Rel->r_info)) { case R_AARCH64_NONE0: case R_AARCH64_NONE: case R_AARCH64_LD64_GOTOFF_LO15: case R_AARCH64_LD64_GOTPAGE_LO15: case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_ADR_GOT_PAGE: case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_ADD_ABS_LO12_NC: case R_AARCH64_LDST8_ABS_LO12_NC: case R_AARCH64_LDST16_ABS_LO12_NC: case R_AARCH64_LDST32_ABS_LO12_NC: case R_AARCH64_LDST64_ABS_LO12_NC: case R_AARCH64_LDST128_ABS_LO12_NC: case R_AARCH64_ADR_PREL_LO21: case R_AARCH64_CONDBR19: case R_AARCH64_LD_PREL_LO19: case R_AARCH64_CALL26: case R_AARCH64_JUMP26: case R_AARCH64_PREL64: case R_AARCH64_PREL32: case R_AARCH64_PREL16: break; case R_AARCH64_ABS64: case R_AARCH64_RELATIVE: ImageInfo->RelocInfo.Relocs[RelNum].Type = EFI_IMAGE_REL_BASED_DIR64; ImageInfo->RelocInfo.Relocs[RelNum].Target = (uint32_t)(Rel->r_offset - BaseAddress); ++RelNum; break; case R_AARCH64_ABS32: ImageInfo->RelocInfo.Relocs[RelNum].Type = EFI_IMAGE_REL_BASED_HIGHLOW; ImageInfo->RelocInfo.Relocs[RelNum].Target = (uint32_t)(Rel->r_offset - BaseAddress); ++RelNum; break; default: fprintf ( stderr, "ImageTool: Unsupported ELF EM_AARCH64 relocation 0x%llx in %s\n", (unsigned long long)ELF_R_TYPE(Rel->r_info), ImageInfo->DebugInfo.SymbolsPath ); return RETURN_INCOMPATIBLE_VERSION; } } #elif ELF_ARCH == 32 if (Ehdr->e_machine == EM_386) { switch (ELF_R_TYPE(Rel->r_info)) { case R_386_NONE: break; case R_386_32: ImageInfo->RelocInfo.Relocs[RelNum].Type = EFI_IMAGE_REL_BASED_HIGHLOW; ImageInfo->RelocInfo.Relocs[RelNum].Target = (Rel->r_offset - BaseAddress); ++RelNum; break; case R_386_PLT32: case R_386_PC32: break; default: fprintf (stderr, "ImageTool: Unsupported ELF EM_386 relocation 0x%x in %s\n", ELF_R_TYPE(Rel->r_info), ImageInfo->DebugInfo.SymbolsPath); return RETURN_INCOMPATIBLE_VERSION; } } else if (Ehdr->e_machine == EM_ARM) { switch (ELF32_R_TYPE(Rel->r_info)) { case R_ARM_NONE: case R_ARM_PC24: case R_ARM_REL32: case R_ARM_XPC25: case R_ARM_THM_PC22: case R_ARM_THM_JUMP19: case R_ARM_CALL: case R_ARM_JMP24: case R_ARM_THM_JUMP24: case R_ARM_PREL31: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: case R_ARM_THM_JMP6: case R_ARM_THM_ALU_PREL_11_0: case R_ARM_THM_PC12: case R_ARM_REL32_NOI: case R_ARM_ALU_PC_G0_NC: case R_ARM_ALU_PC_G0: case R_ARM_ALU_PC_G1_NC: case R_ARM_ALU_PC_G1: case R_ARM_ALU_PC_G2: case R_ARM_LDR_PC_G1: case R_ARM_LDR_PC_G2: case R_ARM_LDRS_PC_G0: case R_ARM_LDRS_PC_G1: case R_ARM_LDRS_PC_G2: case R_ARM_LDC_PC_G0: case R_ARM_LDC_PC_G1: case R_ARM_LDC_PC_G2: case R_ARM_THM_JUMP11: case R_ARM_THM_JUMP8: case R_ARM_TLS_GD32: case R_ARM_TLS_LDM32: case R_ARM_TLS_IE32: break; case R_ARM_ABS32: ImageInfo->RelocInfo.Relocs[RelNum].Type = EFI_IMAGE_REL_BASED_HIGHLOW; ImageInfo->RelocInfo.Relocs[RelNum].Target = (Rel->r_offset - BaseAddress); ++RelNum; break; default: fprintf (stderr, "ImageTool: Unsupported ELF EM_ARM relocation 0x%x in %s\n", ELF_R_TYPE(Rel->r_info), ImageInfo->DebugInfo.SymbolsPath); return RETURN_INCOMPATIBLE_VERSION; } } #endif } } ImageInfo->RelocInfo.NumRelocs = RelNum; return RETURN_SUCCESS; } #define CreateIntermediate ELF_SUFFIX(CreateIntermediate) static RETURN_STATUS CreateIntermediate ( OUT image_tool_image_info_t *ImageInfo, IN const tool_elf_scan_context *Context ) { const Elf_Ehdr *Ehdr; Elf_Addr BaseAddress; const Elf_Shdr *Shdr; UINT32 Index; image_tool_segment_t *Segments; image_tool_reloc_t *Relocs; UINT32 SIndex; const Elf_Rel *Rel; UINTN RIndex; const char *Name; UINT32 NumRelocs; Ehdr = Context->Ehdr; BaseAddress = Context->BaseAddress; Segments = NULL; SIndex = 0; Relocs = NULL; NumRelocs = 0; for (Index = 0; Index < Ehdr->e_shnum; ++Index) { Shdr = GetShdrByIndex (Ehdr, Index); if ((Shdr->sh_type == SHT_REL) || (Shdr->sh_type == SHT_RELA)) { if ((Shdr->sh_flags & SHF_ALLOC) != 0) { fprintf (stderr, "ImageTool: Loadable reloc sections are unsupported\n"); return RETURN_VOLUME_CORRUPTED; } if (!ProcessRelocSection (Context, Shdr)) { continue; } for (RIndex = 0; RIndex < Shdr->sh_size; RIndex += (UINTN)Shdr->sh_entsize) { Rel = (Elf_Rel *)((UINT8 *)Ehdr + Shdr->sh_offset + RIndex); #if ELF_ARCH == 64 if (Ehdr->e_machine == EM_X86_64) { if ((ELF_R_TYPE(Rel->r_info) == R_X86_64_RELATIVE) || (ELF_R_TYPE(Rel->r_info) == R_X86_64_64) || (ELF_R_TYPE(Rel->r_info) == R_X86_64_32) || (ELF_R_TYPE(Rel->r_info) == R_X86_64_GOTPCREL) || (ELF_R_TYPE(Rel->r_info) == R_X86_64_GOTPCRELX) || (ELF_R_TYPE(Rel->r_info) == R_X86_64_REX_GOTPCRELX)) { ++NumRelocs; } } else if (Ehdr->e_machine == EM_AARCH64) { if ((ELF_R_TYPE(Rel->r_info) == R_AARCH64_ABS64) || (ELF_R_TYPE(Rel->r_info) == R_AARCH64_RELATIVE) || (ELF_R_TYPE(Rel->r_info) == R_AARCH64_ABS32)) { ++NumRelocs; } } #elif ELF_ARCH == 32 if (Ehdr->e_machine == EM_386) { if (ELF_R_TYPE(Rel->r_info) == R_386_32) { ++NumRelocs; } } else if (Ehdr->e_machine == EM_ARM) { if (ELF_R_TYPE(Rel->r_info) == R_ARM_ABS32) { ++NumRelocs; } } #endif } } if (!IsShdrLoadable (Ehdr, Shdr)) { continue; } ++ImageInfo->SegmentInfo.NumSegments; } if (ImageInfo->SegmentInfo.NumSegments == 0) { fprintf (stderr, "ImageTool: No loadable sections\n"); return RETURN_VOLUME_CORRUPTED; } Segments = AllocateZeroPool (sizeof (*Segments) * ImageInfo->SegmentInfo.NumSegments); if (Segments == NULL) { fprintf (stderr, "ImageTool: Could not allocate memory for Segments\n"); return RETURN_OUT_OF_RESOURCES; }; ImageInfo->SegmentInfo.Segments = Segments; if (NumRelocs != 0) { Relocs = AllocateZeroPool (sizeof (*Relocs) * NumRelocs); if (Relocs == NULL) { fprintf (stderr, "ImageTool: Could not allocate memory for Relocs\n"); return RETURN_OUT_OF_RESOURCES; }; ImageInfo->RelocInfo.Relocs = Relocs; } for (Index = 0; Index < Ehdr->e_shnum; ++Index) { Shdr = GetShdrByIndex (Ehdr, Index); if ((Shdr->sh_flags & SHF_ALLOC) == 0) { continue; } if (Shdr->sh_type != SHT_PROGBITS && Shdr->sh_type != SHT_NOBITS) { fprintf (stderr, "ImageTool: Segment #%d type %x unsupported\n", Index, Shdr->sh_type); return RETURN_VOLUME_CORRUPTED; } Name = GetString (Ehdr, Shdr->sh_name, 0); if (Name == NULL) { return RETURN_VOLUME_CORRUPTED; } Segments[SIndex].Name = AllocateZeroPool (strlen (Name) + 1); if (Segments[SIndex].Name == NULL) { fprintf (stderr, "ImageTool: Could not allocate memory for Segment #%d Name\n", Index); return RETURN_OUT_OF_RESOURCES; }; memcpy (Segments[SIndex].Name, Name, strlen (Name)); Segments[SIndex].ImageAddress = (uint32_t)(Shdr->sh_addr - BaseAddress); Segments[SIndex].ImageSize = ALIGN_VALUE ((uint32_t)Shdr->sh_size, Context->Alignment); Segments[SIndex].Read = true; Segments[SIndex].Write = (Shdr->sh_flags & SHF_WRITE) != 0; Segments[SIndex].Execute = (Shdr->sh_flags & SHF_EXECINSTR) != 0; Segments[SIndex].Data = AllocateZeroPool (Segments[SIndex].ImageSize); if (Segments[SIndex].Data == NULL) { fprintf (stderr, "ImageTool: Could not allocate memory for Segment #%d Data\n", Index); return RETURN_OUT_OF_RESOURCES; }; if (Shdr->sh_type == SHT_PROGBITS) { memcpy (Segments[SIndex].Data, (const char *)Ehdr + Shdr->sh_offset, (size_t)Shdr->sh_size); } ++SIndex; } assert (SIndex == ImageInfo->SegmentInfo.NumSegments); return SetRelocs (ImageInfo, Context); } #define ScanElf ELF_SUFFIX(ScanElf) RETURN_STATUS ScanElf ( OUT image_tool_image_info_t *ImageInfo, IN const void *File, IN uint32_t FileSize, IN const char *SymbolsPath OPTIONAL ) { RETURN_STATUS Status; tool_elf_scan_context Context; const Elf_Ehdr *Ehdr; Elf_Addr BaseAddress; assert (File != NULL || FileSize == 0); Status = ParseElfFile (&Context, File, FileSize); if (RETURN_ERROR (Status)) { return Status; } Ehdr = Context.Ehdr; BaseAddress = Context.BaseAddress; memset (ImageInfo, 0, sizeof (*ImageInfo)); ImageInfo->HeaderInfo.BaseAddress = BaseAddress; ImageInfo->HeaderInfo.EntryPointAddress = (uint32_t)(Ehdr->e_entry - BaseAddress); ImageInfo->SegmentInfo.SegmentAlignment = (uint32_t)Context.Alignment; ImageInfo->RelocInfo.RelocsStripped = false; if (SymbolsPath != NULL) { ImageInfo->DebugInfo.SymbolsPathLen = strlen (SymbolsPath); } else { assert (ImageInfo->DebugInfo.SymbolsPathLen == 0); } switch (Ehdr->e_machine) { #if ELF_ARCH == 64 case EM_X86_64: ImageInfo->HeaderInfo.Machine = IMAGE_FILE_MACHINE_X64; break; case EM_AARCH64: ImageInfo->HeaderInfo.Machine = IMAGE_FILE_MACHINE_ARM64; break; #elif ELF_ARCH == 32 case EM_386: ImageInfo->HeaderInfo.Machine = IMAGE_FILE_MACHINE_I386; break; case EM_ARM: ImageInfo->HeaderInfo.Machine = IMAGE_FILE_MACHINE_ARMTHUMB_MIXED; break; #endif default: fprintf (stderr, "ImageTool: Unknown ELF architecture %d\n", Ehdr->e_machine); return RETURN_INCOMPATIBLE_VERSION; } ImageInfo->DebugInfo.SymbolsPath = AllocatePool (ImageInfo->DebugInfo.SymbolsPathLen + 1); if (ImageInfo->DebugInfo.SymbolsPath == NULL) { fprintf (stderr, "ImageTool: Could not allocate memory for Debug Data\n"); return RETURN_OUT_OF_RESOURCES; } if (SymbolsPath != NULL) { memmove (ImageInfo->DebugInfo.SymbolsPath, SymbolsPath, ImageInfo->DebugInfo.SymbolsPathLen + 1); } else { *ImageInfo->DebugInfo.SymbolsPath = '\0'; } // // There is no corresponding ELF property. // ImageInfo->HeaderInfo.Subsystem = 0; Status = CreateIntermediate (ImageInfo, &Context); if (RETURN_ERROR (Status)) { ToolImageDestruct (ImageInfo); } return Status; }