syslinux/gpxe/src/util/elf2efi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
   3 *
   4 * This program is free software; you can redistribute it and/or
   5 * modify it under the terms of the GNU General Public License as
   6 * published by the Free Software Foundation; either version 2 of the
   7 * License, or any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12 * General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the Free Software
  16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17 */
  18
  19#define _GNU_SOURCE
  20#include <stdint.h>
  21#include <stddef.h>
  22#include <stdlib.h>
  23#include <stdio.h>
  24#include <string.h>
  25#include <unistd.h>
  26#include <errno.h>
  27#include <assert.h>
  28#include <getopt.h>
  29#include <bfd.h>
  30#include <gpxe/efi/efi.h>
  31#include <gpxe/efi/IndustryStandard/PeImage.h>
  32#include <libgen.h>
  33
  34#define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
  35
  36#define EFI_FILE_ALIGN 0x20
  37
  38struct pe_section {
  39        struct pe_section *next;
  40        EFI_IMAGE_SECTION_HEADER hdr;
  41        uint8_t contents[0];
  42};
  43
  44struct pe_relocs {
  45        struct pe_relocs *next;
  46        unsigned long start_rva;
  47        unsigned int used_relocs;
  48        unsigned int total_relocs;
  49        uint16_t *relocs;
  50};
  51
  52struct pe_header {
  53        EFI_IMAGE_DOS_HEADER dos;
  54        uint8_t padding[128];
  55#if defined(MDE_CPU_IA32)
  56        EFI_IMAGE_NT_HEADERS32 nt;
  57#elif defined(MDE_CPU_X64)
  58        EFI_IMAGE_NT_HEADERS64 nt;
  59#endif
  60};
  61
  62static struct pe_header efi_pe_header = {
  63        .dos = {
  64                .e_magic = EFI_IMAGE_DOS_SIGNATURE,
  65                .e_lfanew = offsetof ( typeof ( efi_pe_header ), nt ),
  66        },
  67        .nt = {
  68                .Signature = EFI_IMAGE_NT_SIGNATURE,
  69                .FileHeader = {
  70#if defined(MDE_CPU_IA32)
  71                        .Machine = EFI_IMAGE_MACHINE_IA32,
  72#elif defined(MDE_CPU_X64)
  73                        .Machine = EFI_IMAGE_MACHINE_X64,
  74#endif
  75                        .TimeDateStamp = 0x10d1a884,
  76                        .SizeOfOptionalHeader =
  77                                sizeof ( efi_pe_header.nt.OptionalHeader ),
  78                        .Characteristics = ( EFI_IMAGE_FILE_DLL |
  79#if defined(MDE_CPU_IA32)
  80                                             EFI_IMAGE_FILE_32BIT_MACHINE |
  81#endif
  82                                             EFI_IMAGE_FILE_EXECUTABLE_IMAGE ),
  83                },
  84                .OptionalHeader = {
  85#if defined(MDE_CPU_IA32)
  86                        .Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC,
  87#elif defined(MDE_CPU_X64)
  88                        .Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC,
  89#endif
  90                        .SectionAlignment = EFI_FILE_ALIGN,
  91                        .FileAlignment = EFI_FILE_ALIGN,
  92                        .SizeOfImage = sizeof ( efi_pe_header ),
  93                        .SizeOfHeaders = sizeof ( efi_pe_header ),
  94                        .NumberOfRvaAndSizes =
  95                                EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES,
  96                },
  97        },
  98};
  99
 100/** Command-line options */
 101struct options {
 102        unsigned int subsystem;
 103};
 104
 105/**
 106 * Allocate memory
 107 *
 108 * @v len               Length of memory to allocate
 109 * @ret ptr             Pointer to allocated memory
 110 */
 111static void * xmalloc ( size_t len ) {
 112        void *ptr;
 113
 114        ptr = malloc ( len );
 115        if ( ! ptr ) {
 116                eprintf ( "Could not allocate %zd bytes\n", len );
 117                exit ( 1 );
 118        }
 119
 120        return ptr;
 121}
 122
 123/**
 124 * Align section within PE file
 125 *
 126 * @v offset            Unaligned offset
 127 * @ret aligned_offset  Aligned offset
 128 */
 129static unsigned long efi_file_align ( unsigned long offset ) {
 130        return ( ( offset + EFI_FILE_ALIGN - 1 ) & ~( EFI_FILE_ALIGN - 1 ) );
 131}
 132
 133/**
 134 * Generate entry in PE relocation table
 135 *
 136 * @v pe_reltab         PE relocation table
 137 * @v rva               RVA
 138 * @v size              Size of relocation entry
 139 */
 140static void generate_pe_reloc ( struct pe_relocs **pe_reltab,
 141                                unsigned long rva, size_t size ) {
 142        unsigned long start_rva;
 143        uint16_t reloc;
 144        struct pe_relocs *pe_rel;
 145        uint16_t *relocs;
 146
 147        /* Construct */
 148        start_rva = ( rva & ~0xfff );
 149        reloc = ( rva & 0xfff );
 150        switch ( size ) {
 151        case 8:
 152                reloc |= 0xa000;
 153                break;
 154        case 4:
 155                reloc |= 0x3000;
 156                break;
 157        case 2:
 158                reloc |= 0x2000;
 159                break;
 160        default:
 161                eprintf ( "Unsupported relocation size %zd\n", size );
 162                exit ( 1 );
 163        }
 164
 165        /* Locate or create PE relocation table */
 166        for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
 167                if ( pe_rel->start_rva == start_rva )
 168                        break;
 169        }
 170        if ( ! pe_rel ) {
 171                pe_rel = xmalloc ( sizeof ( *pe_rel ) );
 172                memset ( pe_rel, 0, sizeof ( *pe_rel ) );
 173                pe_rel->next = *pe_reltab;
 174                *pe_reltab = pe_rel;
 175                pe_rel->start_rva = start_rva;
 176        }
 177
 178        /* Expand relocation list if necessary */
 179        if ( pe_rel->used_relocs < pe_rel->total_relocs ) {
 180                relocs = pe_rel->relocs;
 181        } else {
 182                pe_rel->total_relocs = ( pe_rel->total_relocs ?
 183                                         ( pe_rel->total_relocs * 2 ) : 256 );
 184                relocs = xmalloc ( pe_rel->total_relocs *
 185                                   sizeof ( pe_rel->relocs[0] ) );
 186                memset ( relocs, 0,
 187                         pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) );
 188                memcpy ( relocs, pe_rel->relocs,
 189                         pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) );
 190                free ( pe_rel->relocs );
 191                pe_rel->relocs = relocs;
 192        }
 193
 194        /* Store relocation */
 195        pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc;
 196}
 197
 198/**
 199 * Calculate size of binary PE relocation table
 200 *
 201 * @v pe_reltab         PE relocation table
 202 * @v buffer            Buffer to contain binary table, or NULL
 203 * @ret size            Size of binary table
 204 */
 205static size_t output_pe_reltab ( struct pe_relocs *pe_reltab,
 206                                 void *buffer ) {
 207        struct pe_relocs *pe_rel;
 208        unsigned int num_relocs;
 209        size_t size;
 210        size_t total_size = 0;
 211
 212        for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
 213                num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 );
 214                size = ( sizeof ( uint32_t ) /* VirtualAddress */ +
 215                         sizeof ( uint32_t ) /* SizeOfBlock */ +
 216                         ( num_relocs * sizeof ( uint16_t ) ) );
 217                if ( buffer ) {
 218                        *( (uint32_t *) ( buffer + total_size + 0 ) )
 219                                = pe_rel->start_rva;
 220                        *( (uint32_t *) ( buffer + total_size + 4 ) ) = size;
 221                        memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs,
 222                                 ( num_relocs * sizeof ( uint16_t ) ) );
 223                }
 224                total_size += size;
 225        }
 226
 227        return total_size;
 228}
 229
 230/**
 231 * Open input BFD file
 232 *
 233 * @v filename          File name
 234 * @ret ibfd            BFD file
 235 */
 236static bfd * open_input_bfd ( const char *filename ) {
 237        bfd *bfd;
 238
 239        /* Open the file */
 240        bfd = bfd_openr ( filename, NULL );
 241        if ( ! bfd ) {
 242                eprintf ( "Cannot open %s: ", filename );
 243                bfd_perror ( NULL );
 244                exit ( 1 );
 245        }
 246
 247        /* The call to bfd_check_format() must be present, otherwise
 248         * we get a segfault from later BFD calls.
 249         */
 250        if ( bfd_check_format ( bfd, bfd_object ) < 0 ) {
 251                eprintf ( "%s is not an object file\n", filename );
 252                exit ( 1 );
 253        }
 254
 255        return bfd;
 256}
 257
 258/**
 259 * Read symbol table
 260 *
 261 * @v bfd               BFD file
 262 */
 263static asymbol ** read_symtab ( bfd *bfd ) {
 264        long symtab_size;
 265        asymbol **symtab;
 266        long symcount;
 267
 268        /* Get symbol table size */
 269        symtab_size = bfd_get_symtab_upper_bound ( bfd );
 270        if ( symtab_size < 0 ) {
 271                bfd_perror ( "Could not get symbol table upper bound" );
 272                exit ( 1 );
 273        }
 274
 275        /* Allocate and read symbol table */
 276        symtab = xmalloc ( symtab_size );
 277        symcount = bfd_canonicalize_symtab ( bfd, symtab );
 278        if ( symcount < 0 ) {
 279                bfd_perror ( "Cannot read symbol table" );
 280                exit ( 1 );
 281        }
 282
 283        return symtab;
 284}
 285
 286/**
 287 * Read relocation table
 288 *
 289 * @v bfd               BFD file
 290 * @v symtab            Symbol table
 291 * @v section           Section
 292 * @v symtab            Symbol table
 293 * @ret reltab          Relocation table
 294 */
 295static arelent ** read_reltab ( bfd *bfd, asymbol **symtab,
 296                                asection *section ) {
 297        long reltab_size;
 298        arelent **reltab;
 299        long numrels;
 300
 301        /* Get relocation table size */
 302        reltab_size = bfd_get_reloc_upper_bound ( bfd, section );
 303        if ( reltab_size < 0 ) {
 304                bfd_perror ( "Could not get relocation table upper bound" );
 305                exit ( 1 );
 306        }
 307
 308        /* Allocate and read relocation table */
 309        reltab = xmalloc ( reltab_size );
 310        numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab );
 311        if ( numrels < 0 ) {
 312                bfd_perror ( "Cannot read relocation table" );
 313                exit ( 1 );
 314        }
 315
 316        return reltab;
 317}
 318
 319/**
 320 * Process section
 321 *
 322 * @v bfd               BFD file
 323 * @v pe_header         PE file header
 324 * @v section           Section
 325 * @ret new             New PE section
 326 */
 327static struct pe_section * process_section ( bfd *bfd,
 328                                             struct pe_header *pe_header,
 329                                             asection *section ) {
 330        struct pe_section *new;
 331        size_t section_memsz;
 332        size_t section_filesz;
 333        unsigned long flags = bfd_get_section_flags ( bfd, section );
 334        unsigned long code_start;
 335        unsigned long code_end;
 336        unsigned long data_start;
 337        unsigned long data_mid;
 338        unsigned long data_end;
 339        unsigned long start;
 340        unsigned long end;
 341        unsigned long *applicable_start;
 342        unsigned long *applicable_end;
 343
 344        /* Extract current RVA limits from file header */
 345        code_start = pe_header->nt.OptionalHeader.BaseOfCode;
 346        code_end = ( code_start + pe_header->nt.OptionalHeader.SizeOfCode );
 347#if defined(MDE_CPU_IA32)
 348        data_start = pe_header->nt.OptionalHeader.BaseOfData;
 349#elif defined(MDE_CPU_X64)
 350        data_start = code_end;
 351#endif
 352        data_mid = ( data_start +
 353                     pe_header->nt.OptionalHeader.SizeOfInitializedData );
 354        data_end = ( data_mid +
 355                     pe_header->nt.OptionalHeader.SizeOfUninitializedData );
 356
 357        /* Allocate PE section */
 358        section_memsz = bfd_section_size ( bfd, section );
 359        section_filesz = ( ( flags & SEC_LOAD ) ?
 360                           efi_file_align ( section_memsz ) : 0 );
 361        new = xmalloc ( sizeof ( *new ) + section_filesz );
 362        memset ( new, 0, sizeof ( *new ) + section_filesz );
 363
 364        /* Fill in section header details */
 365        strncpy ( ( char * ) new->hdr.Name, section->name,
 366                  sizeof ( new->hdr.Name ) );
 367        new->hdr.Misc.VirtualSize = section_memsz;
 368        new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section );
 369        new->hdr.SizeOfRawData = section_filesz;
 370
 371        /* Fill in section characteristics and update RVA limits */
 372        if ( flags & SEC_CODE ) {
 373                /* .text-type section */
 374                new->hdr.Characteristics =
 375                        ( EFI_IMAGE_SCN_CNT_CODE |
 376                          EFI_IMAGE_SCN_MEM_NOT_PAGED |
 377                          EFI_IMAGE_SCN_MEM_EXECUTE |
 378                          EFI_IMAGE_SCN_MEM_READ );
 379                applicable_start = &code_start;
 380                applicable_end = &code_end;
 381        } else if ( flags & SEC_DATA ) {
 382                /* .data-type section */
 383                new->hdr.Characteristics =
 384                        ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
 385                          EFI_IMAGE_SCN_MEM_NOT_PAGED |
 386                          EFI_IMAGE_SCN_MEM_READ |
 387                          EFI_IMAGE_SCN_MEM_WRITE );
 388                applicable_start = &data_start;
 389                applicable_end = &data_mid;
 390        } else if ( flags & SEC_READONLY ) {
 391                /* .rodata-type section */
 392                new->hdr.Characteristics =
 393                        ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
 394                          EFI_IMAGE_SCN_MEM_NOT_PAGED |
 395                          EFI_IMAGE_SCN_MEM_READ );
 396                applicable_start = &data_start;
 397                applicable_end = &data_mid;
 398        } else if ( ! ( flags & SEC_LOAD ) ) {
 399                /* .bss-type section */
 400                new->hdr.Characteristics =
 401                        ( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA |
 402                          EFI_IMAGE_SCN_MEM_NOT_PAGED |
 403                          EFI_IMAGE_SCN_MEM_READ |
 404                          EFI_IMAGE_SCN_MEM_WRITE );
 405                applicable_start = &data_mid;
 406                applicable_end = &data_end;
 407        }
 408
 409        /* Copy in section contents */
 410        if ( flags & SEC_LOAD ) {
 411                if ( ! bfd_get_section_contents ( bfd, section, new->contents,
 412                                                  0, section_memsz ) ) {
 413                        eprintf ( "Cannot read section %s: ", section->name );
 414                        bfd_perror ( NULL );
 415                        exit ( 1 );
 416                }
 417        }
 418
 419        /* Update RVA limits */
 420        start = new->hdr.VirtualAddress;
 421        end = ( start + new->hdr.Misc.VirtualSize );
 422        if ( ( ! *applicable_start ) || ( *applicable_start >= start ) )
 423                *applicable_start = start;
 424        if ( *applicable_end < end )
 425                *applicable_end = end;
 426        if ( data_start < code_end )
 427                data_start = code_end;
 428        if ( data_mid < data_start )
 429                data_mid = data_start;
 430        if ( data_end < data_mid )
 431                data_end = data_mid;
 432
 433        /* Write RVA limits back to file header */
 434        pe_header->nt.OptionalHeader.BaseOfCode = code_start;
 435        pe_header->nt.OptionalHeader.SizeOfCode = ( code_end - code_start );
 436#if defined(MDE_CPU_IA32)
 437        pe_header->nt.OptionalHeader.BaseOfData = data_start;
 438#endif
 439        pe_header->nt.OptionalHeader.SizeOfInitializedData =
 440                ( data_mid - data_start );
 441        pe_header->nt.OptionalHeader.SizeOfUninitializedData =
 442                ( data_end - data_mid );
 443
 444        /* Update remaining file header fields */
 445        pe_header->nt.FileHeader.NumberOfSections++;
 446        pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr );
 447        pe_header->nt.OptionalHeader.SizeOfImage =
 448                efi_file_align ( data_end );
 449
 450        return new;
 451}
 452
 453/**
 454 * Process relocation record
 455 *
 456 * @v bfd               BFD file
 457 * @v section           Section
 458 * @v rel               Relocation entry
 459 * @v pe_reltab         PE relocation table to fill in
 460 */
 461static void process_reloc ( bfd *bfd, asection *section, arelent *rel,
 462                            struct pe_relocs **pe_reltab ) {
 463        reloc_howto_type *howto = rel->howto;
 464        asymbol *sym = *(rel->sym_ptr_ptr);
 465        unsigned long offset = ( bfd_get_section_vma ( bfd, section ) +
 466                                 rel->address );
 467
 468        if ( bfd_is_abs_section ( sym->section ) ) {
 469                /* Skip absolute symbols; the symbol value won't
 470                 * change when the object is loaded.
 471                 */
 472        } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) {
 473                /* Generate an 8-byte PE relocation */
 474                generate_pe_reloc ( pe_reltab, offset, 8 );
 475        } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) ||
 476                    ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) {
 477                /* Generate a 4-byte PE relocation */
 478                generate_pe_reloc ( pe_reltab, offset, 4 );
 479        } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) {
 480                /* Generate a 2-byte PE relocation */
 481                generate_pe_reloc ( pe_reltab, offset, 2 );
 482        } else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) ||
 483                    ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) {
 484                /* Skip PC-relative relocations; all relative offsets
 485                 * remain unaltered when the object is loaded.
 486                 */
 487        } else {
 488                eprintf ( "Unrecognised relocation type %s\n", howto->name );
 489                exit ( 1 );
 490        }
 491}
 492
 493/**
 494 * Create relocations section
 495 *
 496 * @v pe_header         PE file header
 497 * @v pe_reltab         PE relocation table
 498 * @ret section         Relocation section
 499 */
 500static struct pe_section *
 501create_reloc_section ( struct pe_header *pe_header,
 502                       struct pe_relocs *pe_reltab ) {
 503        struct pe_section *reloc;
 504        size_t section_memsz;
 505        size_t section_filesz;
 506        EFI_IMAGE_DATA_DIRECTORY *relocdir;
 507
 508        /* Allocate PE section */
 509        section_memsz = output_pe_reltab ( pe_reltab, NULL );
 510        section_filesz = efi_file_align ( section_memsz );
 511        reloc = xmalloc ( sizeof ( *reloc ) + section_filesz );
 512        memset ( reloc, 0, sizeof ( *reloc ) + section_filesz );
 513
 514        /* Fill in section header details */
 515        strncpy ( ( char * ) reloc->hdr.Name, ".reloc",
 516                  sizeof ( reloc->hdr.Name ) );
 517        reloc->hdr.Misc.VirtualSize = section_memsz;
 518        reloc->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage;
 519        reloc->hdr.SizeOfRawData = section_filesz;
 520        reloc->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
 521                                       EFI_IMAGE_SCN_MEM_NOT_PAGED |
 522                                       EFI_IMAGE_SCN_MEM_READ );
 523
 524        /* Copy in section contents */
 525        output_pe_reltab ( pe_reltab, reloc->contents );
 526
 527        /* Update file header details */
 528        pe_header->nt.FileHeader.NumberOfSections++;
 529        pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr );
 530        pe_header->nt.OptionalHeader.SizeOfImage += section_filesz;
 531        relocdir = &(pe_header->nt.OptionalHeader.DataDirectory
 532                     [EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]);
 533        relocdir->VirtualAddress = reloc->hdr.VirtualAddress;
 534        relocdir->Size = reloc->hdr.Misc.VirtualSize;
 535
 536        return reloc;
 537}
 538
 539/**
 540 * Create debug section
 541 *
 542 * @v pe_header         PE file header
 543 * @ret section         Debug section
 544 */
 545static struct pe_section *
 546create_debug_section ( struct pe_header *pe_header, const char *filename ) {
 547        struct pe_section *debug;
 548        size_t section_memsz;
 549        size_t section_filesz;
 550        EFI_IMAGE_DATA_DIRECTORY *debugdir;
 551        struct {
 552                EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug;
 553                EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds;
 554                char name[ strlen ( filename ) + 1 ];
 555        } *contents;
 556
 557        /* Allocate PE section */
 558        section_memsz = sizeof ( *contents );
 559        section_filesz = efi_file_align ( section_memsz );
 560        debug = xmalloc ( sizeof ( *debug ) + section_filesz );
 561        memset ( debug, 0, sizeof ( *debug ) + section_filesz );
 562        contents = ( void * ) debug->contents;
 563
 564        /* Fill in section header details */
 565        strncpy ( ( char * ) debug->hdr.Name, ".debug",
 566                  sizeof ( debug->hdr.Name ) );
 567        debug->hdr.Misc.VirtualSize = section_memsz;
 568        debug->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage;
 569        debug->hdr.SizeOfRawData = section_filesz;
 570        debug->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
 571                                       EFI_IMAGE_SCN_MEM_NOT_PAGED |
 572                                       EFI_IMAGE_SCN_MEM_READ );
 573
 574        /* Create section contents */
 575        contents->debug.TimeDateStamp = 0x10d1a884;
 576        contents->debug.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW;
 577        contents->debug.SizeOfData =
 578                ( sizeof ( *contents ) - sizeof ( contents->debug ) );
 579        contents->debug.RVA = ( debug->hdr.VirtualAddress +
 580                                offsetof ( typeof ( *contents ), rsds ) );
 581        contents->rsds.Signature = CODEVIEW_SIGNATURE_RSDS;
 582        snprintf ( contents->name, sizeof ( contents->name ), "%s",
 583                   filename );
 584
 585        /* Update file header details */
 586        pe_header->nt.FileHeader.NumberOfSections++;
 587        pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( debug->hdr );
 588        pe_header->nt.OptionalHeader.SizeOfImage += section_filesz;
 589        debugdir = &(pe_header->nt.OptionalHeader.DataDirectory
 590                     [EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
 591        debugdir->VirtualAddress = debug->hdr.VirtualAddress;
 592        debugdir->Size = debug->hdr.Misc.VirtualSize;
 593
 594        return debug;
 595}
 596
 597/**
 598 * Write out PE file
 599 *
 600 * @v pe_header         PE file header
 601 * @v pe_sections       List of PE sections
 602 * @v pe                Output file
 603 */
 604static void write_pe_file ( struct pe_header *pe_header,
 605                            struct pe_section *pe_sections,
 606                            FILE *pe ) {
 607        struct pe_section *section;
 608        unsigned long fpos = 0;
 609
 610        /* Assign raw data pointers */
 611        fpos = efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders );
 612        for ( section = pe_sections ; section ; section = section->next ) {
 613                if ( section->hdr.SizeOfRawData ) {
 614                        section->hdr.PointerToRawData = fpos;
 615                        fpos += section->hdr.SizeOfRawData;
 616                        fpos = efi_file_align ( fpos );
 617                }
 618        }
 619
 620        /* Write file header */
 621        if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 1 ) {
 622                perror ( "Could not write PE header" );
 623                exit ( 1 );
 624        }
 625
 626        /* Write section headers */
 627        for ( section = pe_sections ; section ; section = section->next ) {
 628                if ( fwrite ( &section->hdr, sizeof ( section->hdr ),
 629                              1, pe ) != 1 ) {
 630                        perror ( "Could not write section header" );
 631                        exit ( 1 );
 632                }
 633        }
 634
 635        /* Write sections */
 636        for ( section = pe_sections ; section ; section = section->next ) {
 637                if ( fseek ( pe, section->hdr.PointerToRawData,
 638                             SEEK_SET ) != 0 ) {
 639                        eprintf ( "Could not seek to %lx: %s\n",
 640                                  section->hdr.PointerToRawData,
 641                                  strerror ( errno ) );
 642                        exit ( 1 );
 643                }
 644                if ( section->hdr.SizeOfRawData &&
 645                     ( fwrite ( section->contents, section->hdr.SizeOfRawData,
 646                                1, pe ) != 1 ) ) {
 647                        eprintf ( "Could not write section %.8s: %s\n",
 648                                  section->hdr.Name, strerror ( errno ) );
 649                        exit ( 1 );
 650                }
 651        }
 652}
 653
 654/**
 655 * Convert ELF to PE
 656 *
 657 * @v elf_name          ELF file name
 658 * @v pe_name           PE file name
 659 */
 660static void elf2pe ( const char *elf_name, const char *pe_name,
 661                     struct options *opts ) {
 662        char pe_name_tmp[ strlen ( pe_name ) + 1 ];
 663        bfd *bfd;
 664        asymbol **symtab;
 665        asection *section;
 666        arelent **reltab;
 667        arelent **rel;
 668        struct pe_relocs *pe_reltab = NULL;
 669        struct pe_section *pe_sections = NULL;
 670        struct pe_section **next_pe_section = &pe_sections;
 671        struct pe_header pe_header;
 672        FILE *pe;
 673
 674        /* Create a modifiable copy of the PE name */
 675        memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) );
 676
 677        /* Open the file */
 678        bfd = open_input_bfd ( elf_name );
 679        symtab = read_symtab ( bfd );
 680
 681        /* Initialise the PE header */
 682        memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) );
 683        pe_header.nt.OptionalHeader.AddressOfEntryPoint =
 684                bfd_get_start_address ( bfd );
 685        pe_header.nt.OptionalHeader.Subsystem = opts->subsystem;
 686
 687        /* For each input section, build an output section and create
 688         * the appropriate relocation records
 689         */
 690        for ( section = bfd->sections ; section ; section = section->next ) {
 691                /* Discard non-allocatable sections */
 692                if ( ! ( bfd_get_section_flags ( bfd, section ) & SEC_ALLOC ) )
 693                        continue;
 694                /* Create output section */
 695                *(next_pe_section) = process_section ( bfd, &pe_header,
 696                                                       section );
 697                next_pe_section = &(*next_pe_section)->next;
 698                /* Add relocations from this section */
 699                reltab = read_reltab ( bfd, symtab, section );
 700                for ( rel = reltab ; *rel ; rel++ )
 701                        process_reloc ( bfd, section, *rel, &pe_reltab );
 702                free ( reltab );
 703        }
 704
 705        /* Create the .reloc section */
 706        *(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab );
 707        next_pe_section = &(*next_pe_section)->next;
 708
 709        /* Create the .reloc section */
 710        *(next_pe_section) = create_debug_section ( &pe_header,
 711                                                    basename ( pe_name_tmp ) );
 712        next_pe_section = &(*next_pe_section)->next;
 713
 714        /* Write out PE file */
 715        pe = fopen ( pe_name, "w" );
 716        if ( ! pe ) {
 717                eprintf ( "Could not open %s for writing: %s\n",
 718                          pe_name, strerror ( errno ) );
 719                exit ( 1 );
 720        }
 721        write_pe_file ( &pe_header, pe_sections, pe );
 722        fclose ( pe );
 723
 724        /* Close BFD file */
 725        bfd_close ( bfd );
 726}
 727
 728/**
 729 * Print help
 730 *
 731 * @v program_name      Program name
 732 */
 733static void print_help ( const char *program_name ) {
 734        eprintf ( "Syntax: %s [--subsystem=<number>] infile outfile\n",
 735                  program_name );
 736}
 737
 738/**
 739 * Parse command-line options
 740 *
 741 * @v argc              Argument count
 742 * @v argv              Argument list
 743 * @v opts              Options structure to populate
 744 */
 745static int parse_options ( const int argc, char **argv,
 746                           struct options *opts ) {
 747        char *end;
 748        int c;
 749
 750        while (1) {
 751                int option_index = 0;
 752                static struct option long_options[] = {
 753                        { "subsystem", required_argument, NULL, 's' },
 754                        { "help", 0, NULL, 'h' },
 755                        { 0, 0, 0, 0 }
 756                };
 757
 758                if ( ( c = getopt_long ( argc, argv, "s:h",
 759                                         long_options,
 760                                         &option_index ) ) == -1 ) {
 761                        break;
 762                }
 763
 764                switch ( c ) {
 765                case 's':
 766                        opts->subsystem = strtoul ( optarg, &end, 0 );
 767                        if ( *end ) {
 768                                eprintf ( "Invalid subsytem \"%s\"\n",
 769                                          optarg );
 770                                exit ( 2 );
 771                        }
 772                        break;
 773                case 'h':
 774                        print_help ( argv[0] );
 775                        exit ( 0 );
 776                case '?':
 777                default:
 778                        exit ( 2 );
 779                }
 780        }
 781        return optind;
 782}
 783
 784int main ( int argc, char **argv ) {
 785        struct options opts = {
 786                .subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION,
 787        };
 788        unsigned int infile_index;
 789        const char *infile;
 790        const char *outfile;
 791
 792        /* Initialise libbfd */
 793        bfd_init();
 794
 795        /* Parse command-line arguments */
 796        infile_index = parse_options ( argc, argv, &opts );
 797        if ( argc != ( infile_index + 2 ) ) {
 798                print_help ( argv[0] );
 799                exit ( 2 );
 800        }
 801        infile = argv[infile_index];
 802        outfile = argv[infile_index + 1];
 803
 804        /* Convert file */
 805        elf2pe ( infile, outfile, &opts );
 806
 807        return 0;
 808}
 809
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.