diff options
author | 2015-08-28 09:58:54 +0800 | |
---|---|---|
committer | 2015-09-01 12:44:00 +0800 | |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/util/elf2efi.c | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/util/elf2efi.c')
-rw-r--r-- | qemu/roms/ipxe/src/util/elf2efi.c | 820 |
1 files changed, 820 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/util/elf2efi.c b/qemu/roms/ipxe/src/util/elf2efi.c new file mode 100644 index 000000000..45d539574 --- /dev/null +++ b/qemu/roms/ipxe/src/util/elf2efi.c @@ -0,0 +1,820 @@ +/* + * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#define _GNU_SOURCE +#define PACKAGE "elf2efi" +#define PACKAGE_VERSION "1" +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <getopt.h> +#include <bfd.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/IndustryStandard/PeImage.h> +#include <libgen.h> + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +#define EFI_FILE_ALIGN 0x20 + +struct pe_section { + struct pe_section *next; + EFI_IMAGE_SECTION_HEADER hdr; + uint8_t contents[0]; +}; + +struct pe_relocs { + struct pe_relocs *next; + unsigned long start_rva; + unsigned int used_relocs; + unsigned int total_relocs; + uint16_t *relocs; +}; + +struct pe_header { + EFI_IMAGE_DOS_HEADER dos; + uint8_t padding[128]; +#if defined(EFI_TARGET_IA32) + EFI_IMAGE_NT_HEADERS32 nt; +#elif defined(EFI_TARGET_X64) + EFI_IMAGE_NT_HEADERS64 nt; +#endif +}; + +static struct pe_header efi_pe_header = { + .dos = { + .e_magic = EFI_IMAGE_DOS_SIGNATURE, + .e_lfanew = offsetof ( typeof ( efi_pe_header ), nt ), + }, + .nt = { + .Signature = EFI_IMAGE_NT_SIGNATURE, + .FileHeader = { +#if defined(EFI_TARGET_IA32) + .Machine = EFI_IMAGE_MACHINE_IA32, +#elif defined(EFI_TARGET_X64) + .Machine = EFI_IMAGE_MACHINE_X64, +#endif + .TimeDateStamp = 0x10d1a884, + .SizeOfOptionalHeader = + sizeof ( efi_pe_header.nt.OptionalHeader ), + .Characteristics = ( EFI_IMAGE_FILE_DLL | +#if defined(EFI_TARGET_IA32) + EFI_IMAGE_FILE_32BIT_MACHINE | +#endif + EFI_IMAGE_FILE_EXECUTABLE_IMAGE ), + }, + .OptionalHeader = { +#if defined(EFI_TARGET_IA32) + .Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC, +#elif defined(EFI_TARGET_X64) + .Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC, +#endif + .SectionAlignment = EFI_FILE_ALIGN, + .FileAlignment = EFI_FILE_ALIGN, + .SizeOfImage = sizeof ( efi_pe_header ), + .SizeOfHeaders = sizeof ( efi_pe_header ), + .NumberOfRvaAndSizes = + EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, + }, + }, +}; + +/** Command-line options */ +struct options { + unsigned int subsystem; +}; + +/** + * Allocate memory + * + * @v len Length of memory to allocate + * @ret ptr Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { + void *ptr; + + ptr = malloc ( len ); + if ( ! ptr ) { + eprintf ( "Could not allocate %zd bytes\n", len ); + exit ( 1 ); + } + + return ptr; +} + +/** + * Align section within PE file + * + * @v offset Unaligned offset + * @ret aligned_offset Aligned offset + */ +static unsigned long efi_file_align ( unsigned long offset ) { + return ( ( offset + EFI_FILE_ALIGN - 1 ) & ~( EFI_FILE_ALIGN - 1 ) ); +} + +/** + * Generate entry in PE relocation table + * + * @v pe_reltab PE relocation table + * @v rva RVA + * @v size Size of relocation entry + */ +static void generate_pe_reloc ( struct pe_relocs **pe_reltab, + unsigned long rva, size_t size ) { + unsigned long start_rva; + uint16_t reloc; + struct pe_relocs *pe_rel; + uint16_t *relocs; + + /* Construct */ + start_rva = ( rva & ~0xfff ); + reloc = ( rva & 0xfff ); + switch ( size ) { + case 8: + reloc |= 0xa000; + break; + case 4: + reloc |= 0x3000; + break; + case 2: + reloc |= 0x2000; + break; + default: + eprintf ( "Unsupported relocation size %zd\n", size ); + exit ( 1 ); + } + + /* Locate or create PE relocation table */ + for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { + if ( pe_rel->start_rva == start_rva ) + break; + } + if ( ! pe_rel ) { + pe_rel = xmalloc ( sizeof ( *pe_rel ) ); + memset ( pe_rel, 0, sizeof ( *pe_rel ) ); + pe_rel->next = *pe_reltab; + *pe_reltab = pe_rel; + pe_rel->start_rva = start_rva; + } + + /* Expand relocation list if necessary */ + if ( pe_rel->used_relocs < pe_rel->total_relocs ) { + relocs = pe_rel->relocs; + } else { + pe_rel->total_relocs = ( pe_rel->total_relocs ? + ( pe_rel->total_relocs * 2 ) : 256 ); + relocs = xmalloc ( pe_rel->total_relocs * + sizeof ( pe_rel->relocs[0] ) ); + memset ( relocs, 0, + pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) ); + memcpy ( relocs, pe_rel->relocs, + pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) ); + free ( pe_rel->relocs ); + pe_rel->relocs = relocs; + } + + /* Store relocation */ + pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc; +} + +/** + * Calculate size of binary PE relocation table + * + * @v pe_reltab PE relocation table + * @v buffer Buffer to contain binary table, or NULL + * @ret size Size of binary table + */ +static size_t output_pe_reltab ( struct pe_relocs *pe_reltab, + void *buffer ) { + struct pe_relocs *pe_rel; + unsigned int num_relocs; + size_t size; + size_t total_size = 0; + + for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { + num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 ); + size = ( sizeof ( uint32_t ) /* VirtualAddress */ + + sizeof ( uint32_t ) /* SizeOfBlock */ + + ( num_relocs * sizeof ( uint16_t ) ) ); + if ( buffer ) { + *( (uint32_t *) ( buffer + total_size + 0 ) ) + = pe_rel->start_rva; + *( (uint32_t *) ( buffer + total_size + 4 ) ) = size; + memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs, + ( num_relocs * sizeof ( uint16_t ) ) ); + } + total_size += size; + } + + return total_size; +} + +/** + * Open input BFD file + * + * @v filename File name + * @ret ibfd BFD file + */ +static bfd * open_input_bfd ( const char *filename ) { + bfd *bfd; + + /* Open the file */ + bfd = bfd_openr ( filename, NULL ); + if ( ! bfd ) { + eprintf ( "Cannot open %s: ", filename ); + bfd_perror ( NULL ); + exit ( 1 ); + } + + /* The call to bfd_check_format() must be present, otherwise + * we get a segfault from later BFD calls. + */ + if ( ! bfd_check_format ( bfd, bfd_object ) ) { + eprintf ( "%s is not an object file: ", filename ); + bfd_perror ( NULL ); + exit ( 1 ); + } + + return bfd; +} + +/** + * Read symbol table + * + * @v bfd BFD file + */ +static asymbol ** read_symtab ( bfd *bfd ) { + long symtab_size; + asymbol **symtab; + long symcount; + + /* Get symbol table size */ + symtab_size = bfd_get_symtab_upper_bound ( bfd ); + if ( symtab_size < 0 ) { + bfd_perror ( "Could not get symbol table upper bound" ); + exit ( 1 ); + } + + /* Allocate and read symbol table */ + symtab = xmalloc ( symtab_size ); + symcount = bfd_canonicalize_symtab ( bfd, symtab ); + if ( symcount < 0 ) { + bfd_perror ( "Cannot read symbol table" ); + exit ( 1 ); + } + + return symtab; +} + +/** + * Read relocation table + * + * @v bfd BFD file + * @v symtab Symbol table + * @v section Section + * @v symtab Symbol table + * @ret reltab Relocation table + */ +static arelent ** read_reltab ( bfd *bfd, asymbol **symtab, + asection *section ) { + long reltab_size; + arelent **reltab; + long numrels; + + /* Get relocation table size */ + reltab_size = bfd_get_reloc_upper_bound ( bfd, section ); + if ( reltab_size < 0 ) { + bfd_perror ( "Could not get relocation table upper bound" ); + exit ( 1 ); + } + + /* Allocate and read relocation table */ + reltab = xmalloc ( reltab_size ); + numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab ); + if ( numrels < 0 ) { + bfd_perror ( "Cannot read relocation table" ); + exit ( 1 ); + } + + return reltab; +} + +/** + * Process section + * + * @v bfd BFD file + * @v pe_header PE file header + * @v section Section + * @ret new New PE section + */ +static struct pe_section * process_section ( bfd *bfd, + struct pe_header *pe_header, + asection *section ) { + struct pe_section *new; + size_t section_memsz; + size_t section_filesz; + unsigned long flags = bfd_get_section_flags ( bfd, section ); + unsigned long code_start; + unsigned long code_end; + unsigned long data_start; + unsigned long data_mid; + unsigned long data_end; + unsigned long start; + unsigned long end; + unsigned long *applicable_start; + unsigned long *applicable_end; + + /* Extract current RVA limits from file header */ + code_start = pe_header->nt.OptionalHeader.BaseOfCode; + code_end = ( code_start + pe_header->nt.OptionalHeader.SizeOfCode ); +#if defined(EFI_TARGET_IA32) + data_start = pe_header->nt.OptionalHeader.BaseOfData; +#elif defined(EFI_TARGET_X64) + data_start = code_end; +#endif + data_mid = ( data_start + + pe_header->nt.OptionalHeader.SizeOfInitializedData ); + data_end = ( data_mid + + pe_header->nt.OptionalHeader.SizeOfUninitializedData ); + + /* Allocate PE section */ + section_memsz = bfd_section_size ( bfd, section ); + section_filesz = ( ( flags & SEC_LOAD ) ? + efi_file_align ( section_memsz ) : 0 ); + new = xmalloc ( sizeof ( *new ) + section_filesz ); + memset ( new, 0, sizeof ( *new ) + section_filesz ); + + /* Fill in section header details */ + strncpy ( ( char * ) new->hdr.Name, section->name, + sizeof ( new->hdr.Name ) ); + new->hdr.Misc.VirtualSize = section_memsz; + new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section ); + new->hdr.SizeOfRawData = section_filesz; + + /* Fill in section characteristics and update RVA limits */ + if ( flags & SEC_CODE ) { + /* .text-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_CODE | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_EXECUTE | + EFI_IMAGE_SCN_MEM_READ ); + applicable_start = &code_start; + applicable_end = &code_end; + } else if ( flags & SEC_DATA ) { + /* .data-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ | + EFI_IMAGE_SCN_MEM_WRITE ); + applicable_start = &data_start; + applicable_end = &data_mid; + } else if ( flags & SEC_READONLY ) { + /* .rodata-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ ); + applicable_start = &data_start; + applicable_end = &data_mid; + } else if ( ! ( flags & SEC_LOAD ) ) { + /* .bss-type section */ + new->hdr.Characteristics = + ( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ | + EFI_IMAGE_SCN_MEM_WRITE ); + applicable_start = &data_mid; + applicable_end = &data_end; + } else { + eprintf ( "Unrecognised characteristics %#lx for section %s\n", + flags, section->name ); + exit ( 1 ); + } + + /* Copy in section contents */ + if ( flags & SEC_LOAD ) { + if ( ! bfd_get_section_contents ( bfd, section, new->contents, + 0, section_memsz ) ) { + eprintf ( "Cannot read section %s: ", section->name ); + bfd_perror ( NULL ); + exit ( 1 ); + } + } + + /* Update RVA limits */ + start = new->hdr.VirtualAddress; + end = ( start + new->hdr.Misc.VirtualSize ); + if ( ( ! *applicable_start ) || ( *applicable_start >= start ) ) + *applicable_start = start; + if ( *applicable_end < end ) + *applicable_end = end; + if ( data_start < code_end ) + data_start = code_end; + if ( data_mid < data_start ) + data_mid = data_start; + if ( data_end < data_mid ) + data_end = data_mid; + + /* Write RVA limits back to file header */ + pe_header->nt.OptionalHeader.BaseOfCode = code_start; + pe_header->nt.OptionalHeader.SizeOfCode = ( code_end - code_start ); +#if defined(EFI_TARGET_IA32) + pe_header->nt.OptionalHeader.BaseOfData = data_start; +#endif + pe_header->nt.OptionalHeader.SizeOfInitializedData = + ( data_mid - data_start ); + pe_header->nt.OptionalHeader.SizeOfUninitializedData = + ( data_end - data_mid ); + + /* Update remaining file header fields */ + pe_header->nt.FileHeader.NumberOfSections++; + pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr ); + pe_header->nt.OptionalHeader.SizeOfImage = + efi_file_align ( data_end ); + + return new; +} + +/** + * Process relocation record + * + * @v bfd BFD file + * @v section Section + * @v rel Relocation entry + * @v pe_reltab PE relocation table to fill in + */ +static void process_reloc ( bfd *bfd __attribute__ (( unused )), + asection *section, arelent *rel, + struct pe_relocs **pe_reltab ) { + reloc_howto_type *howto = rel->howto; + asymbol *sym = *(rel->sym_ptr_ptr); + unsigned long offset = ( bfd_get_section_vma ( bfd, section ) + + rel->address ); + + if ( bfd_is_abs_section ( sym->section ) ) { + /* Skip absolute symbols; the symbol value won't + * change when the object is loaded. + */ + } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { + /* Generate an 8-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 8 ); + } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) { + /* Generate a 4-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 4 ); + } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { + /* Generate a 2-byte PE relocation */ + generate_pe_reloc ( pe_reltab, offset, 2 ); + } else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) { + /* Skip PC-relative relocations; all relative offsets + * remain unaltered when the object is loaded. + */ + } else { + eprintf ( "Unrecognised relocation type %s\n", howto->name ); + exit ( 1 ); + } +} + +/** + * Create relocations section + * + * @v pe_header PE file header + * @v pe_reltab PE relocation table + * @ret section Relocation section + */ +static struct pe_section * +create_reloc_section ( struct pe_header *pe_header, + struct pe_relocs *pe_reltab ) { + struct pe_section *reloc; + size_t section_memsz; + size_t section_filesz; + EFI_IMAGE_DATA_DIRECTORY *relocdir; + + /* Allocate PE section */ + section_memsz = output_pe_reltab ( pe_reltab, NULL ); + section_filesz = efi_file_align ( section_memsz ); + reloc = xmalloc ( sizeof ( *reloc ) + section_filesz ); + memset ( reloc, 0, sizeof ( *reloc ) + section_filesz ); + + /* Fill in section header details */ + strncpy ( ( char * ) reloc->hdr.Name, ".reloc", + sizeof ( reloc->hdr.Name ) ); + reloc->hdr.Misc.VirtualSize = section_memsz; + reloc->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; + reloc->hdr.SizeOfRawData = section_filesz; + reloc->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ ); + + /* Copy in section contents */ + output_pe_reltab ( pe_reltab, reloc->contents ); + + /* Update file header details */ + pe_header->nt.FileHeader.NumberOfSections++; + pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr ); + pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; + relocdir = &(pe_header->nt.OptionalHeader.DataDirectory + [EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]); + relocdir->VirtualAddress = reloc->hdr.VirtualAddress; + relocdir->Size = reloc->hdr.Misc.VirtualSize; + + return reloc; +} + +/** + * Create debug section + * + * @v pe_header PE file header + * @ret section Debug section + */ +static struct pe_section * +create_debug_section ( struct pe_header *pe_header, const char *filename ) { + struct pe_section *debug; + size_t section_memsz; + size_t section_filesz; + EFI_IMAGE_DATA_DIRECTORY *debugdir; + struct { + EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug; + EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds; + char name[ strlen ( filename ) + 1 ]; + } *contents; + + /* Allocate PE section */ + section_memsz = sizeof ( *contents ); + section_filesz = efi_file_align ( section_memsz ); + debug = xmalloc ( sizeof ( *debug ) + section_filesz ); + memset ( debug, 0, sizeof ( *debug ) + section_filesz ); + contents = ( void * ) debug->contents; + + /* Fill in section header details */ + strncpy ( ( char * ) debug->hdr.Name, ".debug", + sizeof ( debug->hdr.Name ) ); + debug->hdr.Misc.VirtualSize = section_memsz; + debug->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; + debug->hdr.SizeOfRawData = section_filesz; + debug->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | + EFI_IMAGE_SCN_MEM_NOT_PAGED | + EFI_IMAGE_SCN_MEM_READ ); + + /* Create section contents */ + contents->debug.TimeDateStamp = 0x10d1a884; + contents->debug.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW; + contents->debug.SizeOfData = + ( sizeof ( *contents ) - sizeof ( contents->debug ) ); + contents->debug.RVA = ( debug->hdr.VirtualAddress + + offsetof ( typeof ( *contents ), rsds ) ); + contents->rsds.Signature = CODEVIEW_SIGNATURE_RSDS; + snprintf ( contents->name, sizeof ( contents->name ), "%s", + filename ); + + /* Update file header details */ + pe_header->nt.FileHeader.NumberOfSections++; + pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( debug->hdr ); + pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; + debugdir = &(pe_header->nt.OptionalHeader.DataDirectory + [EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); + debugdir->VirtualAddress = debug->hdr.VirtualAddress; + debugdir->Size = debug->hdr.Misc.VirtualSize; + + return debug; +} + +/** + * Write out PE file + * + * @v pe_header PE file header + * @v pe_sections List of PE sections + * @v pe Output file + */ +static void write_pe_file ( struct pe_header *pe_header, + struct pe_section *pe_sections, + FILE *pe ) { + struct pe_section *section; + unsigned long fpos = 0; + + /* Align length of headers */ + fpos = pe_header->nt.OptionalHeader.SizeOfHeaders = + efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders ); + + /* Assign raw data pointers */ + for ( section = pe_sections ; section ; section = section->next ) { + if ( section->hdr.SizeOfRawData ) { + section->hdr.PointerToRawData = fpos; + fpos += section->hdr.SizeOfRawData; + fpos = efi_file_align ( fpos ); + } + } + + /* Write file header */ + if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 1 ) { + perror ( "Could not write PE header" ); + exit ( 1 ); + } + + /* Write section headers */ + for ( section = pe_sections ; section ; section = section->next ) { + if ( fwrite ( §ion->hdr, sizeof ( section->hdr ), + 1, pe ) != 1 ) { + perror ( "Could not write section header" ); + exit ( 1 ); + } + } + + /* Write sections */ + for ( section = pe_sections ; section ; section = section->next ) { + if ( fseek ( pe, section->hdr.PointerToRawData, + SEEK_SET ) != 0 ) { + eprintf ( "Could not seek to %x: %s\n", + section->hdr.PointerToRawData, + strerror ( errno ) ); + exit ( 1 ); + } + if ( section->hdr.SizeOfRawData && + ( fwrite ( section->contents, section->hdr.SizeOfRawData, + 1, pe ) != 1 ) ) { + eprintf ( "Could not write section %.8s: %s\n", + section->hdr.Name, strerror ( errno ) ); + exit ( 1 ); + } + } +} + +/** + * Convert ELF to PE + * + * @v elf_name ELF file name + * @v pe_name PE file name + */ +static void elf2pe ( const char *elf_name, const char *pe_name, + struct options *opts ) { + char pe_name_tmp[ strlen ( pe_name ) + 1 ]; + bfd *bfd; + asymbol **symtab; + asection *section; + arelent **reltab; + arelent **rel; + struct pe_relocs *pe_reltab = NULL; + struct pe_section *pe_sections = NULL; + struct pe_section **next_pe_section = &pe_sections; + struct pe_header pe_header; + FILE *pe; + + /* Create a modifiable copy of the PE name */ + memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) ); + + /* Open the file */ + bfd = open_input_bfd ( elf_name ); + symtab = read_symtab ( bfd ); + + /* Initialise the PE header */ + memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); + pe_header.nt.OptionalHeader.AddressOfEntryPoint = + bfd_get_start_address ( bfd ); + pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; + + /* For each input section, build an output section and create + * the appropriate relocation records + */ + for ( section = bfd->sections ; section ; section = section->next ) { + /* Discard non-allocatable sections */ + if ( ! ( bfd_get_section_flags ( bfd, section ) & SEC_ALLOC ) ) + continue; + /* Create output section */ + *(next_pe_section) = process_section ( bfd, &pe_header, + section ); + next_pe_section = &(*next_pe_section)->next; + /* Add relocations from this section */ + reltab = read_reltab ( bfd, symtab, section ); + for ( rel = reltab ; *rel ; rel++ ) + process_reloc ( bfd, section, *rel, &pe_reltab ); + free ( reltab ); + } + + /* Create the .reloc section */ + *(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab ); + next_pe_section = &(*next_pe_section)->next; + + /* Create the .reloc section */ + *(next_pe_section) = create_debug_section ( &pe_header, + basename ( pe_name_tmp ) ); + next_pe_section = &(*next_pe_section)->next; + + /* Write out PE file */ + pe = fopen ( pe_name, "w" ); + if ( ! pe ) { + eprintf ( "Could not open %s for writing: %s\n", + pe_name, strerror ( errno ) ); + exit ( 1 ); + } + write_pe_file ( &pe_header, pe_sections, pe ); + fclose ( pe ); + + /* Close BFD file */ + bfd_close ( bfd ); +} + +/** + * Print help + * + * @v program_name Program name + */ +static void print_help ( const char *program_name ) { + eprintf ( "Syntax: %s [--subsystem=<number>] infile outfile\n", + program_name ); +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v opts Options structure to populate + */ +static int parse_options ( const int argc, char **argv, + struct options *opts ) { + char *end; + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + { "subsystem", required_argument, NULL, 's' }, + { "help", 0, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + if ( ( c = getopt_long ( argc, argv, "s:h", + long_options, + &option_index ) ) == -1 ) { + break; + } + + switch ( c ) { + case 's': + opts->subsystem = strtoul ( optarg, &end, 0 ); + if ( *end ) { + eprintf ( "Invalid subsytem \"%s\"\n", + optarg ); + exit ( 2 ); + } + break; + case 'h': + print_help ( argv[0] ); + exit ( 0 ); + case '?': + default: + exit ( 2 ); + } + } + return optind; +} + +int main ( int argc, char **argv ) { + struct options opts = { + .subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION, + }; + int infile_index; + const char *infile; + const char *outfile; + + /* Initialise libbfd */ + bfd_init(); + + /* Parse command-line arguments */ + infile_index = parse_options ( argc, argv, &opts ); + if ( argc != ( infile_index + 2 ) ) { + print_help ( argv[0] ); + exit ( 2 ); + } + infile = argv[infile_index]; + outfile = argv[infile_index + 1]; + + /* Convert file */ + elf2pe ( infile, outfile, &opts ); + + return 0; +} |