summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/gpxe/src/util/elf2efi.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/gpxe/src/util/elf2efi.c')
-rw-r--r--contrib/syslinux-4.02/gpxe/src/util/elf2efi.c808
1 files changed, 808 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/gpxe/src/util/elf2efi.c b/contrib/syslinux-4.02/gpxe/src/util/elf2efi.c
new file mode 100644
index 0000000..bb766bd
--- /dev/null
+++ b/contrib/syslinux-4.02/gpxe/src/util/elf2efi.c
@@ -0,0 +1,808 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define _GNU_SOURCE
+#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 <gpxe/efi/efi.h>
+#include <gpxe/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(MDE_CPU_IA32)
+ EFI_IMAGE_NT_HEADERS32 nt;
+#elif defined(MDE_CPU_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(MDE_CPU_IA32)
+ .Machine = EFI_IMAGE_MACHINE_IA32,
+#elif defined(MDE_CPU_X64)
+ .Machine = EFI_IMAGE_MACHINE_X64,
+#endif
+ .TimeDateStamp = 0x10d1a884,
+ .SizeOfOptionalHeader =
+ sizeof ( efi_pe_header.nt.OptionalHeader ),
+ .Characteristics = ( EFI_IMAGE_FILE_DLL |
+#if defined(MDE_CPU_IA32)
+ EFI_IMAGE_FILE_32BIT_MACHINE |
+#endif
+ EFI_IMAGE_FILE_EXECUTABLE_IMAGE ),
+ },
+ .OptionalHeader = {
+#if defined(MDE_CPU_IA32)
+ .Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC,
+#elif defined(MDE_CPU_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 ) < 0 ) {
+ eprintf ( "%s is not an object file\n", filename );
+ 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(MDE_CPU_IA32)
+ data_start = pe_header->nt.OptionalHeader.BaseOfData;
+#elif defined(MDE_CPU_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;
+ }
+
+ /* 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(MDE_CPU_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, 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;
+
+ /* Assign raw data pointers */
+ fpos = efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders );
+ 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 ( &section->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 %lx: %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,
+ };
+ unsigned 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;
+}