From: tgingold Date: Sat, 6 Jan 2007 14:54:24 +0000 (+0000) Subject: automagically convert ELF to PE/COFF (i386 only) X-Git-Tag: edk2-stable201903~23696 X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=commitdiff_plain;h=8ba7afaf2e9c682a5d17760e6dd5463b3a2b2d67;hp=0dce6dc1b743bc990df92a516bce9a9a2f0e5f1c automagically convert ELF to PE/COFF (i386 only) git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@2181 6f19259b-4bc3-4df7-8a09-765794883524 --- diff --git a/Tools/CCode/Source/FwImage/fwimage.c b/Tools/CCode/Source/FwImage/fwimage.c index 0f231488a3..fb3f1a7ecd 100644 --- a/Tools/CCode/Source/FwImage/fwimage.c +++ b/Tools/CCode/Source/FwImage/fwimage.c @@ -21,6 +21,15 @@ Abstract: #include "WinNtInclude.h" +// +// List of OS and CPU which support ELF to PE conversion +// +#if defined(linux) +#if defined(i386) +#define HAVE_ELF +#endif +#endif + #ifndef __GNUC__ #include #endif @@ -28,6 +37,10 @@ Abstract: #include #include #include + +#ifdef HAVE_ELF +#include +#endif #include #include @@ -49,6 +62,8 @@ typedef unsigned char *PUCHAR; typedef unsigned short USHORT; #endif +PUCHAR InImageName; + static void Version ( @@ -69,7 +84,7 @@ Usage ( printf ("\nUsage: " UTILITY_NAME " {-t time-date} {-h|--help|-?|/?|-V|--version} \n\ [BASE|SEC|PEI_CORE|PEIM|DXE_CORE|DXE_DRIVER|DXE_RUNTIME_DRIVER|\n\ DXE_SAL_DRIVER|DXE_SMM_DRIVER|TOOL|UEFI_DRIVER|UEFI_APPLICATION|\n\ - USER_DEFINED] peimage [outimage]"); + USER_DEFINED] peimage [outimage]\n"); } static @@ -144,6 +159,548 @@ FWriteFile ( return STATUS_SUCCESS; } +#ifdef HAVE_ELF +INTN +IsElfHeader( + UINT8 *FileBuffer +) +{ + return (FileBuffer[EI_MAG0] == ELFMAG0 + && FileBuffer[EI_MAG1] == ELFMAG1 + && FileBuffer[EI_MAG2] == ELFMAG2 + && FileBuffer[EI_MAG3] == ELFMAG3); +} + +typedef Elf32_Shdr Elf_Shdr; +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Rel Elf_Rel; +typedef Elf32_Sym Elf_Sym; +#define ELFCLASS ELFCLASS32 +#define ELF_R_TYPE(r) ELF32_R_TYPE(r) +#define ELF_R_SYM(r) ELF32_R_SYM(r) + +// +// Well known ELF structures. +// +Elf_Ehdr *Ehdr; +Elf_Shdr *ShdrBase; + +// +// PE section alignment. +// +const UINT32 CoffAlignment = 0x20; +const UINT32 CoffNbrSections = 4; + +// +// Current offset in coff file. +// +UINT32 CoffOffset; + +// +// Result Coff file in memory. +// +UINT8 *CoffFile; + +// +// Offset in Coff file of headers and sections. +// +UINT32 NtHdrOffset; +UINT32 TableOffset; +UINT32 TextOffset; +UINT32 DataOffset; +UINT32 RelocOffset; + +// +// ELF sections to offset in Coff file. +// +UINT32 *CoffSectionsOffset; + +EFI_IMAGE_BASE_RELOCATION *CoffBaseRel; +UINT16 *CoffEntryRel; + +UINT32 +CoffAlign( + UINT32 Offset + ) +{ + return (Offset + CoffAlignment - 1) & ~(CoffAlignment - 1); +} + +Elf_Shdr * +GetShdrByIndex( + UINT32 Num + ) +{ + if (Num >= Ehdr->e_shnum) + return NULL; + return (Elf_Shdr*)((UINT8*)ShdrBase + Num * Ehdr->e_shentsize); +} + +INTN +CheckElfHeader( + VOID + ) +{ + // + // Note: Magic has already been tested. + // + if (Ehdr->e_ident[EI_CLASS] != ELFCLASS) + return 0; + if (Ehdr->e_ident[EI_DATA] != ELFDATA2LSB) + return 0; + if (Ehdr->e_type != ET_EXEC) + return 0; + if (Ehdr->e_machine != EM_386) + return 0; + if (Ehdr->e_version != EV_CURRENT) + return 0; + + ShdrBase = (Elf_Shdr *)((UINT8 *)Ehdr + Ehdr->e_shoff); + + CoffSectionsOffset = (UINT32 *)malloc(Ehdr->e_shnum * sizeof (UINT32)); + memset(CoffSectionsOffset, 0, Ehdr->e_shnum * sizeof(UINT32)); + return 1; +} + +int +IsTextShdr( + Elf_Shdr *Shdr + ) +{ + return (Shdr->sh_flags & (SHF_WRITE | SHF_ALLOC)) == SHF_ALLOC; +} + +int +IsDataShdr( + Elf_Shdr *Shdr + ) +{ + return (Shdr->sh_flags & (SHF_WRITE | SHF_ALLOC)) == (SHF_ALLOC | SHF_WRITE); +} + +void +CreateSectionHeader( + const char *Name, + UINT32 Offset, + UINT32 Size, + UINT32 Flags + ) +{ + EFI_IMAGE_SECTION_HEADER *Hdr; + Hdr = (EFI_IMAGE_SECTION_HEADER*)(CoffFile + TableOffset); + + strcpy(Hdr->Name, Name); + Hdr->Misc.VirtualSize = Size; + Hdr->VirtualAddress = Offset; + Hdr->SizeOfRawData = Size; + Hdr->PointerToRawData = Offset; + Hdr->PointerToRelocations = 0; + Hdr->PointerToLinenumbers = 0; + Hdr->NumberOfRelocations = 0; + Hdr->NumberOfLinenumbers = 0; + Hdr->Characteristics = Flags; + + TableOffset += sizeof (EFI_IMAGE_SECTION_HEADER); +} + +void +ScanSections( + VOID + ) +{ + UINT32 i; + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_IMAGE_NT_HEADERS *NtHdr; + UINT32 CoffEntry = 0; + + CoffOffset = 0; + + // + // Coff file start with a DOS header. + // + CoffOffset = sizeof(EFI_IMAGE_DOS_HEADER) + 0x40; + NtHdrOffset = CoffOffset; + CoffOffset += sizeof(EFI_IMAGE_NT_HEADERS); + TableOffset = CoffOffset; + CoffOffset += CoffNbrSections * sizeof(EFI_IMAGE_SECTION_HEADER); + + // + // First text sections. + // + CoffOffset = CoffAlign(CoffOffset); + TextOffset = CoffOffset; + for (i = 0; i < Ehdr->e_shnum; i++) { + Elf_Shdr *shdr = GetShdrByIndex(i); + if (IsTextShdr(shdr)) { + /* Relocate entry. */ + if (Ehdr->e_entry >= shdr->sh_addr + && Ehdr->e_entry < shdr->sh_addr + shdr->sh_size) { + CoffEntry = CoffOffset + Ehdr->e_entry - shdr->sh_addr; + } + CoffSectionsOffset[i] = CoffOffset; + CoffOffset += shdr->sh_size; + } + } + CoffOffset = CoffAlign(CoffOffset); + + // + // Then data sections. + // + DataOffset = CoffOffset; + for (i = 0; i < Ehdr->e_shnum; i++) { + Elf_Shdr *shdr = GetShdrByIndex(i); + if (IsDataShdr(shdr)) { + CoffSectionsOffset[i] = CoffOffset; + CoffOffset += shdr->sh_size; + } + } + CoffOffset = CoffAlign(CoffOffset); + + RelocOffset = CoffOffset; + + // + // Allocate base Coff file. Will be expanded later for relocations. + // + CoffFile = (UINT8 *)malloc(CoffOffset); + memset(CoffFile, 0, CoffOffset); + + // + // Fill headers. + // + DosHdr = (EFI_IMAGE_DOS_HEADER *)CoffFile; + DosHdr->e_magic = EFI_IMAGE_DOS_SIGNATURE; + DosHdr->e_lfanew = NtHdrOffset; + + NtHdr = (EFI_IMAGE_NT_HEADERS*)(CoffFile + NtHdrOffset); + + NtHdr->Signature = EFI_IMAGE_NT_SIGNATURE; + + NtHdr->FileHeader.Machine = EFI_IMAGE_MACHINE_IA32; + NtHdr->FileHeader.NumberOfSections = CoffNbrSections; + NtHdr->FileHeader.TimeDateStamp = time(NULL); + NtHdr->FileHeader.PointerToSymbolTable = 0; + NtHdr->FileHeader.NumberOfSymbols = 0; + NtHdr->FileHeader.SizeOfOptionalHeader = sizeof(NtHdr->OptionalHeader); + NtHdr->FileHeader.Characteristics = EFI_IMAGE_FILE_EXECUTABLE_IMAGE + | EFI_IMAGE_FILE_LINE_NUMS_STRIPPED + | EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED + | EFI_IMAGE_FILE_32BIT_MACHINE; + + NtHdr->OptionalHeader.Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC; + NtHdr->OptionalHeader.SizeOfCode = DataOffset - TextOffset; + NtHdr->OptionalHeader.SizeOfInitializedData = RelocOffset - DataOffset; + NtHdr->OptionalHeader.SizeOfUninitializedData = 0; + NtHdr->OptionalHeader.AddressOfEntryPoint = CoffEntry; + NtHdr->OptionalHeader.BaseOfCode = TextOffset; + + NtHdr->OptionalHeader.BaseOfData = DataOffset; + NtHdr->OptionalHeader.ImageBase = 0; + NtHdr->OptionalHeader.SectionAlignment = CoffAlignment; + NtHdr->OptionalHeader.FileAlignment = CoffAlignment; + NtHdr->OptionalHeader.SizeOfImage = 0; + + NtHdr->OptionalHeader.SizeOfHeaders = TextOffset; + NtHdr->OptionalHeader.NumberOfRvaAndSizes = EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; + + // + // Section headers. + // + CreateSectionHeader (".text", TextOffset, DataOffset - TextOffset, + EFI_IMAGE_SCN_CNT_CODE + | EFI_IMAGE_SCN_MEM_EXECUTE + | EFI_IMAGE_SCN_MEM_READ); + CreateSectionHeader (".data", DataOffset, RelocOffset - DataOffset, + EFI_IMAGE_SCN_CNT_INITIALIZED_DATA + | EFI_IMAGE_SCN_MEM_WRITE + | EFI_IMAGE_SCN_MEM_READ); +} + +void +WriteSections( + int (*Filter)(Elf_Shdr *) + ) +{ + UINT32 Idx; + + // + // First: copy sections. + // + for (Idx = 0; Idx < Ehdr->e_shnum; Idx++) { + Elf_Shdr *Shdr = GetShdrByIndex(Idx); + if ((*Filter)(Shdr)) { + switch (Shdr->sh_type) { + case SHT_PROGBITS: + /* Copy. */ + memcpy(CoffFile + CoffSectionsOffset[Idx], + (UINT8*)Ehdr + Shdr->sh_offset, + Shdr->sh_size); + break; + case SHT_NOBITS: + memset(CoffFile + CoffSectionsOffset[Idx], 0, Shdr->sh_size); + break; + default: + Error (NULL, 0, 0, InImageName, "unhandle section type %x", + (UINTN)Shdr->sh_type); + } + } + } + + // + // Second: apply relocations. + // + for (Idx = 0; Idx < Ehdr->e_shnum; Idx++) { + Elf_Shdr *RelShdr = GetShdrByIndex(Idx); + if (RelShdr->sh_type != SHT_REL) + continue; + Elf_Shdr *SecShdr = GetShdrByIndex(RelShdr->sh_info); + UINT32 SecOffset = CoffSectionsOffset[RelShdr->sh_info]; + if (RelShdr->sh_type == SHT_REL && (*Filter)(SecShdr)) { + UINT32 RelIdx; + Elf_Shdr *SymtabShdr = GetShdrByIndex(RelShdr->sh_link); + UINT8 *Symtab = (UINT8*)Ehdr + SymtabShdr->sh_offset; + + for (RelIdx = 0; RelIdx < RelShdr->sh_size; RelIdx += RelShdr->sh_entsize) { + Elf_Rel *Rel = (Elf_Rel *)((UINT8*)Ehdr + RelShdr->sh_offset + RelIdx); + Elf_Sym *Sym = (Elf_Sym *) + (Symtab + ELF_R_SYM(Rel->r_info) * SymtabShdr->sh_entsize); + Elf_Shdr *SymShdr; + UINT8 *Targ; + + if (Sym->st_shndx == SHN_UNDEF + || Sym->st_shndx == SHN_ABS + || Sym->st_shndx > Ehdr->e_shnum) { + Error (NULL, 0, 0, InImageName, "bad symbol definition"); + } + SymShdr = GetShdrByIndex(Sym->st_shndx); + + // + // Note: r_offset in a memory address. + // Convert it to a pointer in the coff file. + // + Targ = CoffFile + SecOffset + (Rel->r_offset - SecShdr->sh_addr); + + switch (ELF_R_TYPE(Rel->r_info)) { + case R_386_NONE: + break; + case R_386_32: + // + // Absolute relocation. + // + *(UINT32 *)Targ = *(UINT32 *)Targ - SymShdr->sh_addr + + CoffSectionsOffset[Sym->st_shndx]; + break; + case R_386_PC32: + // + // Relative relocation: Symbol - Ip + Addend + // + *(UINT32 *)Targ = *(UINT32 *)Targ + + (CoffSectionsOffset[Sym->st_shndx] - SymShdr->sh_addr) + - (SecOffset - SecShdr->sh_addr); + break; + default: + Error (NULL, 0, 0, InImageName, "unhandled relocation type %x", + ELF_R_TYPE(Rel->r_info)); + } + } + } + } +} + +void +CoffAddFixupEntry( + UINT16 Val + ) +{ + *CoffEntryRel = Val; + CoffEntryRel++; + CoffBaseRel->SizeOfBlock += 2; + CoffOffset += 2; +} + +void +CoffAddFixup( + UINT32 Offset, + UINT8 Type + ) +{ + if (CoffBaseRel == NULL + || CoffBaseRel->VirtualAddress != (Offset & ~0xfff)) { + if (CoffBaseRel != NULL) { + // + // Add a null entry (is it required ?) + // + CoffAddFixupEntry (0); + // + // Pad for alignment. + // + if (CoffOffset % 4 != 0) + CoffAddFixupEntry (0); + } + + CoffFile = realloc + (CoffFile, + CoffOffset + sizeof(EFI_IMAGE_BASE_RELOCATION) + 2*0x1000); + memset(CoffFile + CoffOffset, 0, + sizeof(EFI_IMAGE_BASE_RELOCATION) + 2*0x1000); + + CoffBaseRel = (EFI_IMAGE_BASE_RELOCATION*)(CoffFile + CoffOffset); + CoffBaseRel->VirtualAddress = Offset & ~0xfff; + CoffBaseRel->SizeOfBlock = sizeof(EFI_IMAGE_BASE_RELOCATION); + + CoffEntryRel = (UINT16 *)(CoffBaseRel + 1); + CoffOffset += sizeof(EFI_IMAGE_BASE_RELOCATION); + } + + // + // Fill the entry. + // + CoffAddFixupEntry((Type << 12) | (Offset & 0xfff)); +} + +void +WriteRelocations( + VOID + ) +{ + UINT32 Idx; + EFI_IMAGE_NT_HEADERS *NtHdr; + EFI_IMAGE_DATA_DIRECTORY *Dir; + + for (Idx = 0; Idx < Ehdr->e_shnum; Idx++) { + Elf_Shdr *RelShdr = GetShdrByIndex(Idx); + if (RelShdr->sh_type == SHT_REL) { + Elf_Shdr *SecShdr = GetShdrByIndex(RelShdr->sh_info); + if (IsTextShdr(SecShdr) || IsDataShdr(SecShdr)) { + UINT32 RelIdx; + for (RelIdx = 0; RelIdx < RelShdr->sh_size; RelIdx += RelShdr->sh_entsize) { + Elf_Rel *Rel = (Elf_Rel *) + ((UINT8*)Ehdr + RelShdr->sh_offset + RelIdx); + switch (ELF_R_TYPE(Rel->r_info)) { + case R_386_NONE: + case R_386_PC32: + break; + case R_386_32: + CoffAddFixup(CoffSectionsOffset[RelShdr->sh_info] + + (Rel->r_offset - SecShdr->sh_addr), + EFI_IMAGE_REL_BASED_HIGHLOW); + break; + default: + Error (NULL, 0, 0, InImageName, "unhandled relocation type %x", + ELF_R_TYPE(Rel->r_info)); + } + } + } + } + } + + // + // Pad by adding empty entries. + // + while (CoffOffset & (CoffAlignment - 1)) { + CoffAddFixupEntry(0); + } + + CreateSectionHeader (".reloc", RelocOffset, CoffOffset - RelocOffset, + EFI_IMAGE_SCN_CNT_INITIALIZED_DATA + | EFI_IMAGE_SCN_MEM_DISCARDABLE + | EFI_IMAGE_SCN_MEM_READ); + + NtHdr = (EFI_IMAGE_NT_HEADERS *)(CoffFile + NtHdrOffset); + Dir = &NtHdr->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]; + Dir->VirtualAddress = RelocOffset; + Dir->Size = CoffOffset - RelocOffset; +} + +void +WriteDebug( + VOID + ) +{ + UINT32 Len = strlen(InImageName) + 1; + UINT32 DebugOffset = CoffOffset; + EFI_IMAGE_NT_HEADERS *NtHdr; + EFI_IMAGE_DATA_DIRECTORY *DataDir; + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *Dir; + EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY *Nb10; + + CoffOffset += sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY) + + sizeof(EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY) + + Len; + CoffOffset = CoffAlign(CoffOffset); + + CoffFile = realloc + (CoffFile, CoffOffset); + memset(CoffFile + DebugOffset, 0, CoffOffset - DebugOffset); + + Dir = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY*)(CoffFile + DebugOffset); + Dir->Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW; + Dir->SizeOfData = sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY) + Len; + Dir->RVA = DebugOffset + sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY); + Dir->FileOffset = DebugOffset + sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY); + + Nb10 = (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY*)(Dir + 1); + Nb10->Signature = CODEVIEW_SIGNATURE_NB10; + strcpy ((PUCHAR)(Nb10 + 1), InImageName); + + CreateSectionHeader (".debug", DebugOffset, CoffOffset - DebugOffset, + EFI_IMAGE_SCN_CNT_INITIALIZED_DATA + | EFI_IMAGE_SCN_MEM_DISCARDABLE + | EFI_IMAGE_SCN_MEM_READ); + + NtHdr = (EFI_IMAGE_NT_HEADERS *)(CoffFile + NtHdrOffset); + DataDir = &NtHdr->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]; + DataDir->VirtualAddress = DebugOffset; + DataDir->Size = CoffOffset - DebugOffset; +} + +void +ConvertElf ( + UINT8 **FileBuffer, + UINTN *FileLength + ) +{ + EFI_IMAGE_NT_HEADERS *NtHdr; + + // + // Check header, read section table. + // + Ehdr = (Elf32_Ehdr*)*FileBuffer; + if (!CheckElfHeader()) + return; + + // + // Compute sections new address. + // + ScanSections(); + + // + // Write and relocate sections. + // + WriteSections(IsTextShdr); + WriteSections(IsDataShdr); + + // + // Translate and write relocations. + // + WriteRelocations(); + + // + // Write debug info. + // + WriteDebug(); + + NtHdr = (EFI_IMAGE_NT_HEADERS *)(CoffFile + NtHdrOffset); + NtHdr->OptionalHeader.SizeOfImage = CoffOffset; + + // + // Replace. + // + free(*FileBuffer); + *FileBuffer = CoffFile; + *FileLength = CoffOffset; +} +#endif // HAVE_ELF + int main ( int argc, @@ -273,6 +830,8 @@ Returns: return STATUS_ERROR; } + InImageName = argv[2]; + if (argc == 4) { OutImageName = argv[3]; } @@ -323,27 +882,32 @@ Returns: // // open source file // - fpIn = fopen (argv[2], "rb"); + fpIn = fopen (InImageName, "rb"); if (!fpIn) { - Error (NULL, 0, 0, argv[2], "failed to open input file for reading"); + Error (NULL, 0, 0, InImageName, "failed to open input file for reading"); return STATUS_ERROR; } FReadFile (fpIn, (VOID **)&FileBuffer, &FileLength); +#ifdef HAVE_ELF + if (IsElfHeader(FileBuffer)) { + ConvertElf(&FileBuffer, &FileLength); + } +#endif // // Read the dos & pe hdrs of the image // DosHdr = (EFI_IMAGE_DOS_HEADER *)FileBuffer; if (DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) { - Error (NULL, 0, 0, argv[2], "DOS header signature not found in source image"); + Error (NULL, 0, 0, InImageName, "DOS header signature not found in source image"); fclose (fpIn); return STATUS_ERROR; } PeHdr = (EFI_IMAGE_NT_HEADERS *)(FileBuffer + DosHdr->e_lfanew); if (PeHdr->Signature != EFI_IMAGE_NT_SIGNATURE) { - Error (NULL, 0, 0, argv[2], "PE header signature not found in source image"); + Error (NULL, 0, 0, InImageName, "PE header signature not found in source image"); fclose (fpIn); return STATUS_ERROR; } @@ -351,7 +915,7 @@ Returns: // // open output file // - strcpy (outname, argv[2]); + strcpy (outname, InImageName); pe = NULL; for (p = outname; *p; p++) { if (*p == '.') { @@ -389,7 +953,7 @@ Returns: } // - // Path the PE header + // Patch the PE header // PeHdr->OptionalHeader.Subsystem = (USHORT) Type; if (TimeStampPresent) {