]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Eliminate use of libbfd
authorMichael Brown <mcb30@ipxe.org>
Mon, 2 May 2016 19:36:54 +0000 (20:36 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 2 May 2016 21:35:14 +0000 (22:35 +0100)
Parse the intermediate ELF file directly instead of using libbfd, in
order to allow for cross-compiled ELF objects.

As a side bonus, this eliminates libbfd as a build requirement.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/Makefile
src/Makefile.housekeeping
src/util/elf2efi.c

index 58eedb11b5c606d47d3229746d2fa07fa927818a..b7becdb437ba0630987537a2a648e8085785a9b0 100644 (file)
@@ -53,8 +53,6 @@ EINFO         := ./util/einfo
 GENKEYMAP      := ./util/genkeymap.pl
 DOXYGEN                := doxygen
 LCAB           := lcab
-BINUTILS_DIR   := /usr
-BFD_DIR                := $(BINUTILS_DIR)
 ZLIB_DIR       := /usr
 
 ###############################################################################
index 4280f398ed0b52e8d6f8e5e28b6dbbb630ebffc5..4ba1421bf6d990c41b4aee59fb3502e5f7486e3e 100644 (file)
@@ -1303,20 +1303,18 @@ CLEANUP += $(ZBIN)
 #
 # The EFI image converter
 #
-ELF2EFI_CFLAGS := -I$(BINUTILS_DIR)/include -I$(BFD_DIR)/include \
-                  -I$(ZLIB_DIR)/include -idirafter include
-ELF2EFI_LDFLAGS        := -L$(BINUTILS_DIR)/lib -L$(BFD_DIR)/lib -L$(ZLIB_DIR)/lib \
-                  -lbfd -ldl -lz -Wl,--no-warn-search-mismatch
+ELF2EFI_CFLAGS := -I$(ZLIB_DIR)/include -idirafter include
+ELF2EFI_LDFLAGS        := -L$(ZLIB_DIR)/lib -lz -Wl,--no-warn-search-mismatch
 
 $(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS)
        $(QM)$(ECHO) "  [HOSTCC] $@"
-       $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET_IA32 $< \
+       $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET32 $< \
                $(ELF2EFI_LDFLAGS) -o $@
 CLEANUP += $(ELF2EFI32)
 
 $(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS)
        $(QM)$(ECHO) "  [HOSTCC] $@"
-       $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET_X64 $< \
+       $(Q)$(HOST_CC) $(HOST_CFLAGS) $(ELF2EFI_CFLAGS) -DEFI_TARGET64 $< \
                $(ELF2EFI_LDFLAGS) -o $@
 CLEANUP += $(ELF2EFI64)
 
index dee70ee7a6c257df88a5237a469114400c6adaaf..5e1050e3d6cc86a9587c6ebe945047895e21049c 100644 (file)
@@ -17,9 +17,6 @@
  * 02110-1301, USA.
  */
 
-#define _GNU_SOURCE
-#define PACKAGE "elf2efi"
-#define PACKAGE_VERSION "1"
 #define FILE_LICENCE(...) extern void __file_licence ( void )
 #include <stdint.h>
 #include <stddef.h>
 #include <errno.h>
 #include <assert.h>
 #include <getopt.h>
-#include <bfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <elf.h>
 #include <ipxe/efi/Uefi.h>
 #include <ipxe/efi/IndustryStandard/PeImage.h>
 #include <libgen.h>
 
 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
 
+#ifdef EFI_TARGET32
+
+#define EFI_IMAGE_NT_HEADERS           EFI_IMAGE_NT_HEADERS32
+#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC        EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
+#define EFI_IMAGE_FILE_MACHINE         EFI_IMAGE_FILE_32BIT_MACHINE
+#define ELFCLASS   ELFCLASS32
+#define Elf_Ehdr   Elf32_Ehdr
+#define Elf_Shdr   Elf32_Shdr
+#define Elf_Sym    Elf32_Sym
+#define Elf_Addr   Elf32_Addr
+#define Elf_Rel    Elf32_Rel
+#define Elf_Rela   Elf32_Rela
+#define ELF_R_TYPE ELF32_R_TYPE
+#define ELF_R_SYM  ELF32_R_SYM
+
+#elif defined(EFI_TARGET64)
+
+#define EFI_IMAGE_NT_HEADERS           EFI_IMAGE_NT_HEADERS64
+#define EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC        EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
+#define EFI_IMAGE_FILE_MACHINE         0
+#define ELFCLASS   ELFCLASS64
+#define Elf_Ehdr   Elf64_Ehdr
+#define Elf_Shdr   Elf64_Shdr
+#define Elf_Sym    Elf64_Sym
+#define Elf_Addr   Elf64_Addr
+#define Elf_Rel    Elf64_Rel
+#define Elf_Rela   Elf64_Rela
+#define ELF_R_TYPE ELF64_R_TYPE
+#define ELF_R_SYM  ELF64_R_SYM
+
+#endif
+
 #define EFI_FILE_ALIGN 0x20
 
+struct elf_machine {
+       unsigned int pe_machine;
+       unsigned int r_none;
+       unsigned int r_abs;
+       unsigned int r_pcrel;
+};
+
+struct elf_file {
+       void *data;
+       size_t len;
+       const Elf_Ehdr *ehdr;
+       struct elf_machine *machine;
+};
+
 struct pe_section {
        struct pe_section *next;
        EFI_IMAGE_SECTION_HEADER hdr;
@@ -57,11 +104,7 @@ struct pe_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
+       EFI_IMAGE_NT_HEADERS nt;
 };
 
 static struct pe_header efi_pe_header = {
@@ -72,26 +115,15 @@ static struct pe_header efi_pe_header = {
        .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_MACHINE |
                                             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
+                       .Magic = EFI_IMAGE_NT_OPTIONAL_HDR_MAGIC,
                        .MajorLinkerVersion = 42,
                        .MinorLinkerVersion = 42,
                        .SectionAlignment = EFI_FILE_ALIGN,
@@ -104,6 +136,20 @@ static struct pe_header efi_pe_header = {
        },
 };
 
+static struct elf_machine machine_i386 = {
+       .pe_machine = EFI_IMAGE_MACHINE_IA32,
+       .r_none = R_386_NONE,
+       .r_abs = R_386_32,
+       .r_pcrel = R_386_PC32,
+};
+
+static struct elf_machine machine_x86_64 = {
+       .pe_machine = EFI_IMAGE_MACHINE_X64,
+       .r_none = R_X86_64_NONE,
+       .r_abs = R_X86_64_64,
+       .r_pcrel = R_X86_64_PC32,
+};
+
 /** Command-line options */
 struct options {
        unsigned int subsystem;
@@ -235,110 +281,155 @@ static size_t output_pe_reltab ( struct pe_relocs *pe_reltab,
 }
 
 /**
- * Open input BFD file
+ * Read input ELF file
  *
- * @v filename         File name
- * @ret ibfd           BFD file
+ * @v name             File name
+ * @v elf              ELF 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 );
+static void read_elf_file ( const char *name, struct elf_file *elf ) {
+       static const unsigned char ident[] = {
+               ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELFCLASS, ELFDATA2LSB
+       };
+       struct stat stat;
+       const Elf_Ehdr *ehdr;
+       const Elf_Shdr *shdr;
+       void *data;
+       size_t offset;
+       unsigned int i;
+       int fd;
+
+       /* Open file */
+       fd = open ( name, O_RDONLY );
+       if ( fd < 0 ) {
+               eprintf ( "Could not open %s: %s\n", name, strerror ( errno ) );
                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 );
+       /* Get file size */
+       if ( fstat ( fd, &stat ) < 0 ) {
+               eprintf ( "Could not get size of %s: %s\n",
+                         name, strerror ( errno ) );
                exit ( 1 );
        }
+       elf->len = stat.st_size;
 
-       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" );
+       /* Map file */
+       data = mmap ( NULL, elf->len, PROT_READ, MAP_SHARED, fd, 0 );
+       if ( data == MAP_FAILED ) {
+               eprintf ( "Could not map %s: %s\n", name, strerror ( errno ) );
                exit ( 1 );
        }
+       elf->data = data;
 
-       /* Allocate and read symbol table */
-       symtab = xmalloc ( symtab_size );
-       symcount = bfd_canonicalize_symtab ( bfd, symtab );
-       if ( symcount < 0 ) {
-               bfd_perror ( "Cannot read symbol table" );
+       /* Close file */
+       close ( fd );
+
+       /* Check header */
+       ehdr = elf->data;
+       if ( ( elf->len < sizeof ( *ehdr ) ) ||
+            ( memcmp ( ident, ehdr->e_ident, sizeof ( ident ) ) != 0 ) ) {
+               eprintf ( "Invalid ELF header in %s\n", name );
                exit ( 1 );
        }
+       elf->ehdr = ehdr;
+
+       /* Check section headers */
+       for ( i = 0 ; i < ehdr->e_shnum ; i++ ) {
+               offset = ( ehdr->e_shoff + ( i * ehdr->e_shentsize ) );
+               if ( elf->len < ( offset + sizeof ( *shdr ) ) ) {
+                       eprintf ( "ELF section header outside file in %s\n",
+                                 name );
+                       exit ( 1 );
+               }
+               shdr = ( data + offset );
+               if ( ( shdr->sh_type != SHT_NOBITS ) &&
+                    ( ( elf->len < shdr->sh_offset ) ||
+                      ( ( ( elf->len - shdr->sh_offset ) < shdr->sh_size ) ))){
+                       eprintf ( "ELF section %d outside file in %s\n",
+                                 i, name );
+                       exit ( 1 );
+               }
+               if ( shdr->sh_link >= ehdr->e_shnum ) {
+                       eprintf ( "ELF section %d link section %d out of "
+                                 "range\n", i, shdr->sh_link );
+                       exit ( 1 );
+               }
+       }
 
-       return symtab;
+       /* Identify architecture */
+       switch ( ehdr->e_machine ) {
+       case EM_386:
+               elf->machine = &machine_i386;
+               break;
+       case EM_X86_64:
+               elf->machine = &machine_x86_64;
+               break;
+       default:
+               eprintf ( "Unknown ELF architecture %d\n", ehdr->e_machine );
+               exit ( 1 );
+       }
 }
 
 /**
- * Read relocation table
+ * Get ELF string
  *
- * @v bfd              BFD file
- * @v symtab           Symbol table
- * @v section          Section
- * @v symtab           Symbol table
- * @ret reltab         Relocation table
+ * @v elf              ELF file
+ * @v section          String table section number
+ * @v offset           String table offset
+ * @ret string         ELF string
  */
-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" );
+static const char * elf_string ( struct elf_file *elf, unsigned int section,
+                                size_t offset ) {
+       const Elf_Ehdr *ehdr = elf->ehdr;
+       const Elf_Shdr *shdr;
+       char *string;
+       char *last;
+
+       /* Locate section header */
+       if ( section >= ehdr->e_shnum ) {
+               eprintf ( "Invalid ELF string section %d\n", section );
                exit ( 1 );
        }
+       shdr = ( elf->data + ehdr->e_shoff + ( section * ehdr->e_shentsize ) );
 
-       /* 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" );
+       /* Sanity check section */
+       if ( shdr->sh_type != SHT_STRTAB ) {
+               eprintf ( "ELF section %d (type %d) is not a string table\n",
+                         section, shdr->sh_type );
+               exit ( 1 );
+       }
+       last = ( elf->data + shdr->sh_offset + shdr->sh_size - 1 );
+       if ( *last != '\0' ) {
+               eprintf ( "ELF section %d is not NUL-terminated\n", section );
                exit ( 1 );
        }
 
-       return reltab;
+       /* Locate string */
+       if ( offset >= shdr->sh_size ) {
+               eprintf ( "Invalid ELF string offset %zd in section %d\n",
+                         offset, section );
+               exit ( 1 );
+       }
+       string = ( elf->data + shdr->sh_offset + offset );
+
+       return string;
 }
 
 /**
  * Process section
  *
- * @v bfd              BFD file
+ * @v elf              ELF file
+ * @v shdr             ELF section header
  * @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 ) {
+static struct pe_section * process_section ( struct elf_file *elf,
+                                            const Elf_Shdr *shdr,
+                                            struct pe_header *pe_header ) {
        struct pe_section *new;
+       const char *name;
        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;
@@ -349,12 +440,15 @@ static struct pe_section * process_section ( bfd *bfd,
        unsigned long *applicable_start;
        unsigned long *applicable_end;
 
+       /* Get section name */
+       name = elf_string ( elf, elf->ehdr->e_shstrndx, shdr->sh_name );
+
        /* 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)
+#if defined(EFI_TARGET32)
        data_start = pe_header->nt.OptionalHeader.BaseOfData;
-#elif defined(EFI_TARGET_X64)
+#elif defined(EFI_TARGET64)
        data_start = code_end;
 #endif
        data_mid = ( data_start +
@@ -363,21 +457,21 @@ static struct pe_section * process_section ( bfd *bfd,
                     pe_header->nt.OptionalHeader.SizeOfUninitializedData );
 
        /* Allocate PE section */
-       section_memsz = bfd_section_size ( bfd, section );
-       section_filesz = ( ( flags & SEC_LOAD ) ?
+       section_memsz = shdr->sh_size;
+       section_filesz = ( ( shdr->sh_type == SHT_PROGBITS ) ?
                           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 ) );
+       strncpy ( ( char * ) new->hdr.Name, name, sizeof ( new->hdr.Name ) );
        new->hdr.Misc.VirtualSize = section_memsz;
-       new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section );
+       new->hdr.VirtualAddress = shdr->sh_addr;
        new->hdr.SizeOfRawData = section_filesz;
 
        /* Fill in section characteristics and update RVA limits */
-       if ( flags & SEC_CODE ) {
+       if ( ( shdr->sh_type == SHT_PROGBITS ) &&
+            ( shdr->sh_flags & SHF_EXECINSTR ) ) {
                /* .text-type section */
                new->hdr.Characteristics =
                        ( EFI_IMAGE_SCN_CNT_CODE |
@@ -386,7 +480,8 @@ static struct pe_section * process_section ( bfd *bfd,
                          EFI_IMAGE_SCN_MEM_READ );
                applicable_start = &code_start;
                applicable_end = &code_end;
-       } else if ( flags & SEC_DATA ) {
+       } else if ( ( shdr->sh_type == SHT_PROGBITS ) &&
+                   ( shdr->sh_flags & SHF_WRITE ) ) {
                /* .data-type section */
                new->hdr.Characteristics =
                        ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
@@ -395,7 +490,7 @@ static struct pe_section * process_section ( bfd *bfd,
                          EFI_IMAGE_SCN_MEM_WRITE );
                applicable_start = &data_start;
                applicable_end = &data_mid;
-       } else if ( flags & SEC_READONLY ) {
+       } else if ( shdr->sh_type == SHT_PROGBITS ) {
                /* .rodata-type section */
                new->hdr.Characteristics =
                        ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA |
@@ -403,7 +498,7 @@ static struct pe_section * process_section ( bfd *bfd,
                          EFI_IMAGE_SCN_MEM_READ );
                applicable_start = &data_start;
                applicable_end = &data_mid;
-       } else if ( ! ( flags & SEC_LOAD ) ) {
+       } else if ( shdr->sh_type == SHT_NOBITS ) {
                /* .bss-type section */
                new->hdr.Characteristics =
                        ( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA |
@@ -413,19 +508,15 @@ static struct pe_section * process_section ( bfd *bfd,
                applicable_start = &data_mid;
                applicable_end = &data_end;
        } else {
-               eprintf ( "Unrecognised characteristics %#lx for section %s\n",
-                         flags, section->name );
+               eprintf ( "Unrecognised characteristics for section %s\n",
+                         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 );
-               }
+       if ( shdr->sh_type == SHT_PROGBITS ) {
+               memcpy ( new->contents, ( elf->data + shdr->sh_offset ),
+                        shdr->sh_size );
        }
 
        /* Update RVA limits */
@@ -445,7 +536,7 @@ static struct pe_section * process_section ( bfd *bfd,
        /* 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)
+#if defined(EFI_TARGET32)
        pe_header->nt.OptionalHeader.BaseOfData = data_start;
 #endif
        pe_header->nt.OptionalHeader.SizeOfInitializedData =
@@ -465,46 +556,76 @@ static struct pe_section * process_section ( bfd *bfd,
 /**
  * Process relocation record
  *
- * @v bfd              BFD file
- * @v section          Section
- * @v rel              Relocation entry
+ * @v elf              ELF file
+ * @v shdr             ELF section header
+ * @v syms             Symbol table
+ * @v nsyms            Number of symbol table entries
+ * @v rel              Relocation record
  * @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 ) ) {
+static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
+                           const Elf_Sym *syms, unsigned int nsyms,
+                           const Elf_Rel *rel, struct pe_relocs **pe_reltab ) {
+       unsigned int type = ELF_R_TYPE ( rel->r_info );
+       unsigned int sym = ELF_R_SYM ( rel->r_info );
+       size_t offset = ( shdr->sh_addr + rel->r_offset );
+
+       /* Look up symbol and process relocation */
+       if ( sym >= nsyms ) {
+               eprintf ( "Symbol out of range\n" );
+               exit ( 1 );
+       }
+       if ( syms[sym].st_shndx == SHN_ABS ) {
                /* Skip absolute symbols; the symbol value won't
                 * change when the object is loaded.
                 */
-       } else if ( ( strcmp ( howto->name, "R_386_NONE" ) == 0 ) ||
-                   ( strcmp ( howto->name, "R_X86_64_NONE" ) == 0 ) ) {
+       } else if ( type == elf->machine->r_none ) {
                /* Ignore dummy relocations used by REQUIRE_SYMBOL() */
-       } 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 ) {
-               /* 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 ) ) {
+       } else if ( type == elf->machine->r_abs ) {
+               /* Generate an 8-byte or 4-byte PE relocation */
+               generate_pe_reloc ( pe_reltab, offset, sizeof ( Elf_Addr ) );
+       } else if ( type == elf->machine->r_pcrel ) {
                /* Skip PC-relative relocations; all relative offsets
                 * remain unaltered when the object is loaded.
                 */
        } else {
-               eprintf ( "Unrecognised relocation type %s\n", howto->name );
+               eprintf ( "Unrecognised relocation type %d\n", type );
                exit ( 1 );
        }
 }
 
+/**
+ * Process relocation records
+ *
+ * @v elf              ELF file
+ * @v shdr             ELF section header
+ * @v stride           Relocation record size
+ * @v pe_reltab                PE relocation table to fill in
+ */
+static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr,
+                            size_t stride, struct pe_relocs **pe_reltab ) {
+       const Elf_Shdr *symtab;
+       const Elf_Sym *syms;
+       const Elf_Rel *rel;
+       unsigned int nsyms;
+       unsigned int nrels;
+       unsigned int i;
+
+       /* Identify symbol table */
+       symtab = ( elf->data + elf->ehdr->e_shoff +
+                  ( shdr->sh_link * elf->ehdr->e_shentsize ) );
+       syms = ( elf->data + symtab->sh_offset );
+       nsyms = ( symtab->sh_size / sizeof ( syms[0] ) );
+
+       /* Process each relocation */
+       rel = ( elf->data + shdr->sh_offset );
+       nrels = ( shdr->sh_size / stride );
+       for ( i = 0 ; i < nrels ; i++ ) {
+               process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab );
+               rel = ( ( ( const void * ) rel ) + stride );
+       }
+}
+
 /**
  * Create relocations section
  *
@@ -696,53 +817,60 @@ static void write_pe_file ( struct pe_header *pe_header,
 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;
+       struct elf_file elf;
+       const Elf_Shdr *shdr;
+       size_t offset;
+       unsigned int i;
        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 );
+       /* Read ELF file */
+       read_elf_file ( elf_name, &elf );
 
        /* 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.FileHeader.Machine = elf.machine->pe_machine;
+       pe_header.nt.OptionalHeader.AddressOfEntryPoint = elf.ehdr->e_entry;
        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 );
+       /* Process input sections */
+       for ( i = 0 ; i < elf.ehdr->e_shnum ; i++ ) {
+               offset = ( elf.ehdr->e_shoff + ( i * elf.ehdr->e_shentsize ) );
+               shdr = ( elf.data + offset );
+
+               /* Process section */
+               if ( shdr->sh_flags & SHF_ALLOC ) {
+
+                       /* Create output section */
+                       *(next_pe_section) = process_section ( &elf, shdr,
+                                                              &pe_header );
+                       next_pe_section = &(*next_pe_section)->next;
+
+               } else if ( shdr->sh_type == SHT_REL ) {
+
+                       /* Process .rel relocations */
+                       process_relocs ( &elf, shdr, sizeof ( Elf_Rel ),
+                                        &pe_reltab );
+
+               } else if ( shdr->sh_type == SHT_RELA ) {
+
+                       /* Process .rela relocations */
+                       process_relocs ( &elf, shdr, sizeof ( Elf_Rela ),
+                                        &pe_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 */
+       /* Create the .debug section */
        *(next_pe_section) = create_debug_section ( &pe_header,
                                                    basename ( pe_name_tmp ) );
        next_pe_section = &(*next_pe_section)->next;
@@ -757,8 +885,8 @@ static void elf2pe ( const char *elf_name, const char *pe_name,
        write_pe_file ( &pe_header, pe_sections, pe );
        fclose ( pe );
 
-       /* Close BFD file */
-       bfd_close ( bfd );
+       /* Unmap ELF file */
+       munmap ( elf.data, elf.len );
 }
 
 /**
@@ -825,9 +953,6 @@ int main ( int argc, char **argv ) {
        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 ) ) {