david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[efi] Use elf2efi utility in place of efilink

elf2efi converts a suitable ELF executable (containing relocation
information, and with appropriate virtual addresses) into an EFI
executable.  It is less tightly coupled with the gPXE build process
and, in particular, does not require the use of a hand-crafted PE
image header in efiprefix.S.

elf2efi correctly handles .bss sections, which significantly reduces
the size of the gPXE EFI executable.
This commit is contained in:
Michael Brown 2009-01-07 02:05:51 +00:00
parent 85e5e25c52
commit 314779eb36
16 changed files with 939 additions and 1258 deletions

View File

@ -37,7 +37,8 @@ SYMCHECK := $(PERL) ./util/symcheck.pl
SORTOBJDUMP := $(PERL) ./util/sortobjdump.pl
NRV2B := ./util/nrv2b
ZBIN := ./util/zbin
EFILINK := ./util/efilink
ELF2EFI32 := ./util/elf2efi32
ELF2EFI64 := ./util/elf2efi64
DOXYGEN := doxygen
###############################################################################

View File

@ -703,12 +703,17 @@ CLEANUP += $(ZBIN)
###############################################################################
#
# The EFI custom linker
# The EFI image converter
#
$(EFILINK) : util/efilink.c $(MAKEDEPS)
$(ELF2EFI32) : util/elf2efi.c $(MAKEDEPS)
$(QM)$(ECHO) " [HOSTCC] $@"
$(Q)$(HOST_CC) -O2 -o $@ $< -lbfd -liberty
CLEANUP += $(EFILINK)
$(Q)$(HOST_CC) -DMDE_CPU_IA32 -O2 -o $@ $< -lbfd -liberty
CLEANUP += $(ELF2EFI32)
$(ELF2EFI64) : util/elf2efi.c $(MAKEDEPS)
$(QM)$(ECHO) " [HOSTCC] $@"
$(Q)$(HOST_CC) -DMDE_CPU_X64 -O2 -o $@ $< -lbfd -liberty
CLEANUP += $(ELF2EFI64)
###############################################################################
#

View File

@ -2,11 +2,11 @@
# The EFI linker script
#
LDSCRIPT = arch/i386/scripts/efi.lds
LDSCRIPT = arch/x86/scripts/efi.lds
# Use a relocatable link; we perform final relocations in the efilink utility.
# Retain relocation information for elf2efi
#
LDFLAGS += -r -d -S
LDFLAGS += -q -S
# Media types.
#
@ -14,12 +14,6 @@ NON_AUTO_MEDIA += efi
# Rule for building EFI files
#
$(BIN)/%.efi.tmp-reloc : $(BIN)/%.efi.tmp $(EFILINK)
$(QM)$(ECHO) " [EFILINK] $@"
$(Q)# Check for unresolved symbols
$(Q)$(LD) -e 0 --no-warn-mismatch -o /dev/null $<
$(Q)$(EFILINK) $< $@
$(BIN)/%.efi : $(BIN)/%.efi.tmp-reloc
$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI32)
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(OBJCOPY) -Obinary $< $@
$(Q)$(ELF2EFI32) $< $@

View File

@ -1,175 +0,0 @@
.text
.code32
.arch i386
.section ".prefix", "a", @progbits
.org 0x00
/* DOS (.com) header
*
* EFI executables seem to leave most of this empty
*/
mzhdr:
.ascii "MZ" /* Magic number */
.word 0 /* Bytes on last page of file */
.word 0 /* Pages in file */
.word 0 /* Relocations */
.word 0 /* Size of header in paragraphs */
.word 0 /* Minimum extra paragraphs needed */
.word 0 /* Maximum extra paragraphs needed */
.word 0 /* Initial (relative) SS value */
.word 0 /* Initial SP value */
.word 0 /* "Checksum" */
.word 0 /* Initial IP value */
.word 0 /* Initial (relative) CS value */
.word 0 /* File address of relocation table */
.word 0 /* Ovesrlay number */
.word 0, 0, 0, 0 /* Reserved words */
.word 0 /* OEM identifier (for e_oeminfo) */
.word 0 /* OEM information; e_oemid specific */
.word 0, 0, 0, 0, 0 /* Reserved words */
.word 0, 0, 0, 0, 0 /* Reserved words */
.long pehdr_lma /* File address of new exe header */
.size mzhdr, . - mzhdr
/* PE header */
.org 0xc0 /* For compatibility with MS toolchain */
pehdr:
.ascii "PE\0\0" /* Magic number */
.word 0x014c /* CPU architecture: i386 */
.word num_pe_sections /* Number of sections */
.long 0x10d1a884 /* Timestamp */
.long 0 /* Symbol table */
.long 0 /* Number of symbols */
.word opthdr_size /* Size of optional header */
.word 0x2102 /* Characteristics */
.size pehdr, . - pehdr
.equ pehdr_lma, pehdr - mzhdr
/* "Optional" header */
opthdr:
.word 0x010b /* Magic number */
.byte 0 /* Linker major version number */
.byte 0 /* Linker minor version number */
.long _text_filesz /* Size of text section */
.long _data_filesz /* Size of data section */
.long _bss_filesz /* Size of bss section */
.long efi_entry_lma /* Entry point */
.long _text_lma /* Text section start RVA */
.long _data_lma /* Data section start RVA */
.long 0 /* Image base address */
.long _max_align /* Section alignment */
.long _max_align /* File alignment */
.word 0 /* Operating system major version number */
.word 0 /* Operating system minor version number */
.word 0 /* Image major version number */
.word 0 /* Image minor version number */
.word 0 /* Subsystem major version number */
.word 0 /* Subsystem minor version number */
.long 0 /* Reserved */
.long _filesz /* Total image size */
.long _prefix_filesz /* Total header size */
.long 0 /* "Checksum" */
.word 0x0a /* Subsystem: EFI */
.word 0 /* DLL characteristics */
.long 0 /* Size of stack reserve */
.long 0 /* Size of stack commit */
.long 0 /* Size of heap reserve */
.long 0 /* Size of heap commit */
.long 0 /* Loader flags */
.long 16 /* Number of data directory entries */
.long 0, 0 /* Export directory */
.long 0, 0 /* Import directory */
.long 0, 0 /* Resource directory */
.long 0, 0 /* Exception directory */
.long 0, 0 /* Security directory */
.long _reloc_lma, _reloc_filesz /* Base relocation directory */
.long debugdir_lma, debugdir_size /* Debug directory */
.long 0, 0 /* Description directory */
.long 0, 0 /* Special directory */
.long 0, 0 /* Thread storage directory */
.long 0, 0 /* Load configuration directory */
.long 0, 0 /* Bound import directory */
.long 0, 0 /* Import address table directory */
.long 0, 0 /* Delay import directory */
.long 0, 0 /* Reserved */
.long 0, 0 /* Reserved */
.size opthdr, . - opthdr
.equ opthdr_size, . - opthdr
/* PE sections */
pe_sections:
text_section:
.asciz ".text" /* Section name */
.align 8
.long _text_filesz /* Section size */
.long _text_lma /* Relative Virtual Address */
.long _text_filesz /* Section size (rounded up) */
.long _text_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0x68000020 /* Characteristics */
rodata_section:
.asciz ".rodata" /* Section name */
.align 8
.long _rodata_filesz /* Section size */
.long _rodata_lma /* Relative Virtual Address */
.long _rodata_filesz /* Section size (rounded up) */
.long _rodata_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0x48000040 /* Characteristics */
data_section:
.asciz ".data" /* Section name */
.align 8
.long _data_filesz /* Section size */
.long _data_lma /* Relative Virtual Address */
.long _data_filesz /* Section size (rounded up) */
.long _data_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0xc8000040 /* Characteristics */
reloc_section:
.asciz ".reloc" /* Section name */
.align 8
.long _reloc_filesz /* Section size */
.long _reloc_lma /* Relative Virtual Address */
.long _reloc_filesz /* Section size (rounded up) */
.long _reloc_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0x42000040 /* Characteristics */
pe_sections_end:
.size pe_sections, . - pe_sections
.equ num_pe_sections, ( ( . - pe_sections ) / 0x28 )
/* Debug directory */
.section ".rodata"
.globl debugdir
debugdir:
.long 0 /* Characteristics */
.long 0x10d1a884 /* Timestamp */
.word 0 /* Major version */
.word 0 /* Minor version */
.long 0x02 /* RSDS? */
.long codeview_rsds_size /* Size of data */
.long codeview_rsds_lma /* RVA */
.long codeview_rsds_lma /* File offset */
.size debugdir, . - debugdir
.equ debugdir_size, . - debugdir
/* Codeview structure */
.globl codeview_rsds
codeview_rsds:
.ascii "RSDS" /* Magic number */
.long 0, 0, 0, 0, 0 /* Unused by EFI */
.asciz "efiprefix.pdb"
.size codeview_rsds, . - codeview_rsds
.equ codeview_rsds_size, . - codeview_rsds

View File

@ -1,180 +0,0 @@
/* -*- sh -*- */
/*
* Linker script for EFI images
*
*/
EXTERN ( efi_entry )
SECTIONS {
/* The file starts at a virtual address of zero, and sections are
* contiguous. Each section is aligned to at least _max_align,
* which defaults to 32. Load addresses are equal to virtual
* addresses.
*/
. = 0;
PROVIDE ( _max_align = 32 );
/*
* The prefix
*
*/
.prefix : {
_prefix = .;
*(.prefix)
*(.prefix.*)
_mprefix = .;
} .bss.prefix (NOLOAD) : {
_eprefix = .;
}
_prefix_filesz = ABSOLUTE ( _mprefix - _prefix );
_prefix_memsz = ABSOLUTE ( _eprefix - _prefix );
/*
* The text section
*
*/
. = ALIGN ( _max_align );
.text : {
_text = .;
*(.text)
*(.text.*)
_mtext = .;
} .bss.text (NOLOAD) : {
_etext = .;
}
_text_filesz = ABSOLUTE ( _mtext - _text );
_text_memsz = ABSOLUTE ( _etext - _text );
/*
* The rodata section
*
*/
. = ALIGN ( _max_align );
.rodata : {
_rodata = .;
*(.rodata)
*(.rodata.*)
_mrodata = .;
} .bss.rodata (NOLOAD) : {
_erodata = .;
}
_rodata_filesz = ABSOLUTE ( _mrodata - _rodata );
_rodata_memsz = ABSOLUTE ( _erodata - _rodata );
/*
* The data section
*
*/
. = ALIGN ( _max_align );
.data : {
_data = .;
*(.data)
*(.data.*)
*(SORT(.tbl.*)) /* Various tables. See include/tables.h */
/* EFI seems to not support proper bss sections */
*(.bss)
*(.bss.*)
*(COMMON)
*(.stack)
*(.stack.*)
_mdata = .;
} .bss.data (NOLOAD) : {
_edata = .;
}
_data_filesz = ABSOLUTE ( _mdata - _data );
_data_memsz = ABSOLUTE ( _edata - _data );
/*
* The bss section
*
*/
. = ALIGN ( _max_align );
.bss : {
_bss = .;
/* EFI seems to not support proper bss sections */
_mbss = .;
} .bss.bss (NOLOAD) : {
_ebss = .;
}
_bss_filesz = ABSOLUTE ( _mbss - _bss );
_bss_memsz = ABSOLUTE ( _ebss - _bss );
/*
* The reloc section
*
*/
. = ALIGN ( _max_align );
.reloc : {
_reloc = .;
/* Provide some dummy contents to force ld to include this
* section. It will be created by the efilink utility.
*/
. += 1;
_mreloc = .;
} .bss.reloc (NOLOAD) : {
_ereloc = .;
}
_reloc_filesz = ABSOLUTE ( _mreloc - _reloc );
_reloc_memsz = ABSOLUTE ( _ereloc - _reloc );
_filesz = ABSOLUTE ( . );
/*
* Weak symbols that need zero values if not otherwise defined
*
*/
.weak 0x0 : {
_weak = .;
*(.weak)
_eweak = .;
}
_assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" );
/*
* Dispose of the comment and note sections to make the link map
* easier to read
*
*/
/DISCARD/ : {
*(.comment)
*(.comment.*)
*(.note)
*(.note.*)
*(.eh_frame)
*(.eh_frame.*)
*(.rel)
*(.rel.*)
}
/*
* Load address calculations.
*
*/
_prefix_lma = ABSOLUTE ( _prefix );
_text_lma = ABSOLUTE ( _text );
_rodata_lma = ABSOLUTE ( _rodata );
_data_lma = ABSOLUTE ( _data );
_bss_lma = ABSOLUTE ( _bss );
_reloc_lma = ABSOLUTE ( _reloc );
/*
* Load addresses required by the prefix
*
*/
efi_entry_lma = ABSOLUTE ( efi_entry );
debugdir_lma = ABSOLUTE ( debugdir );
codeview_rsds_lma = ABSOLUTE ( codeview_rsds );
}

View File

@ -6,3 +6,4 @@ CFLAGS += -Iarch/x86/include
#
SRCDIRS += arch/x86/core
SRCDIRS += arch/x86/interface/efi
SRCDIRS += arch/x86/prefix

View File

@ -0,0 +1,39 @@
/*
* 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.
*/
#include <stdlib.h>
#include <gpxe/efi/efi.h>
/**
* EFI entry point
*
* @v image_handle Image handle
* @v systab System table
* @ret efirc EFI return status code
*/
EFI_STATUS EFIAPI _start ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab ) {
EFI_STATUS efirc;
/* Initialise EFI environment */
if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 )
return efirc;
/* Call to main() */
return RC_TO_EFIRC ( main () );
}

View File

@ -0,0 +1,106 @@
/* -*- sh -*- */
/*
* Linker script for EFI images
*
*/
EXTERN ( _start )
ENTRY ( _start )
SECTIONS {
/* The file starts at a virtual address of zero, and sections are
* contiguous. Each section is aligned to at least _max_align,
* which defaults to 32. Load addresses are equal to virtual
* addresses.
*/
_max_align = 32;
/* Allow plenty of space for file headers */
. = 0x1000;
/*
* The text section
*
*/
. = ALIGN ( _max_align );
.text : {
_text = .;
*(.text)
*(.text.*)
_etext = .;
}
/*
* The rodata section
*
*/
. = ALIGN ( _max_align );
.rodata : {
_rodata = .;
*(.rodata)
*(.rodata.*)
_erodata = .;
}
/*
* The data section
*
*/
. = ALIGN ( _max_align );
.data : {
_data = .;
*(.data)
*(.data.*)
*(SORT(.tbl.*)) /* Various tables. See include/tables.h */
_edata = .;
}
/*
* The bss section
*
*/
. = ALIGN ( _max_align );
.bss : {
_bss = .;
*(.bss)
*(.bss.*)
*(COMMON)
_ebss = .;
}
/*
* Weak symbols that need zero values if not otherwise defined
*
*/
.weak 0x0 : {
_weak = .;
*(.weak)
_eweak = .;
}
_assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" );
/*
* Dispose of the comment and note sections to make the link map
* easier to read
*
*/
/DISCARD/ : {
*(.comment)
*(.comment.*)
*(.note)
*(.note.*)
*(.eh_frame)
*(.eh_frame.*)
*(.rel)
*(.rel.*)
}
}

View File

@ -6,11 +6,11 @@ CFLAGS += -mno-red-zone
# The EFI linker script
#
LDSCRIPT = arch/x86_64/scripts/efi.lds
LDSCRIPT = arch/x86/scripts/efi.lds
# Use a relocatable link; we perform final relocations in the efilink utility.
# Retain relocation information for elf2efi
#
LDFLAGS += -r -d -S
LDFLAGS += -q -S
# Media types.
#
@ -18,12 +18,6 @@ NON_AUTO_MEDIA += efi
# Rule for building EFI files
#
$(BIN)/%.efi.tmp-reloc : $(BIN)/%.efi.tmp $(EFILINK)
$(QM)$(ECHO) " [EFILINK] $@"
$(Q)# Check for unresolved symbols
$(Q)$(LD) -e 0 --no-warn-mismatch -o /dev/null $<
$(Q)$(EFILINK) $< $@
$(BIN)/%.efi : $(BIN)/%.efi.tmp-reloc
$(BIN)/%.efi : $(BIN)/%.efi.tmp $(ELF2EFI64)
$(QM)$(ECHO) " [FINISH] $@"
$(Q)$(OBJCOPY) -Obinary $< $@
$(Q)$(ELF2EFI64) $< $@

View File

@ -1,174 +0,0 @@
.text
.code32
.arch i386
.section ".prefix", "a", @progbits
.org 0x00
/* DOS (.com) header
*
* EFI executables seem to leave most of this empty
*/
mzhdr:
.ascii "MZ" /* Magic number */
.word 0 /* Bytes on last page of file */
.word 0 /* Pages in file */
.word 0 /* Relocations */
.word 0 /* Size of header in paragraphs */
.word 0 /* Minimum extra paragraphs needed */
.word 0 /* Maximum extra paragraphs needed */
.word 0 /* Initial (relative) SS value */
.word 0 /* Initial SP value */
.word 0 /* "Checksum" */
.word 0 /* Initial IP value */
.word 0 /* Initial (relative) CS value */
.word 0 /* File address of relocation table */
.word 0 /* Ovesrlay number */
.word 0, 0, 0, 0 /* Reserved words */
.word 0 /* OEM identifier (for e_oeminfo) */
.word 0 /* OEM information; e_oemid specific */
.word 0, 0, 0, 0, 0 /* Reserved words */
.word 0, 0, 0, 0, 0 /* Reserved words */
.long pehdr_lma /* File address of new exe header */
.size mzhdr, . - mzhdr
/* PE header */
.org 0xc0 /* For compatibility with MS toolchain */
pehdr:
.ascii "PE\0\0" /* Magic number */
.word 0x8664 /* CPU architecture: x86_64 */
.word num_pe_sections /* Number of sections */
.long 0x10d1a884 /* Timestamp */
.long 0 /* Symbol table */
.long 0 /* Number of symbols */
.word opthdr_size /* Size of optional header */
.word 0x2002 /* Characteristics */
.size pehdr, . - pehdr
.equ pehdr_lma, pehdr - mzhdr
/* "Optional" header */
opthdr:
.word 0x020b /* Magic number */
.byte 0 /* Linker major version number */
.byte 0 /* Linker minor version number */
.long _text_filesz /* Size of text section */
.long _data_filesz /* Size of data section */
.long _bss_filesz /* Size of bss section */
.long efi_entry_lma /* Entry point */
.long _text_lma /* Text section start RVA */
.quad 0 /* Image base address */
.long _max_align /* Section alignment */
.long _max_align /* File alignment */
.word 0 /* Operating system major version number */
.word 0 /* Operating system minor version number */
.word 0 /* Image major version number */
.word 0 /* Image minor version number */
.word 0 /* Subsystem major version number */
.word 0 /* Subsystem minor version number */
.long 0 /* Reserved */
.long _filesz /* Total image size */
.long _prefix_filesz /* Total header size */
.long 0 /* "Checksum" */
.word 0x0a /* Subsystem: EFI */
.word 0 /* DLL characteristics */
.quad 0 /* Size of stack reserve */
.quad 0 /* Size of stack commit */
.quad 0 /* Size of heap reserve */
.quad 0 /* Size of heap commit */
.long 0 /* Loader flags */
.long 16 /* Number of data directory entries */
.long 0, 0 /* Export directory */
.long 0, 0 /* Import directory */
.long 0, 0 /* Resource directory */
.long 0, 0 /* Exception directory */
.long 0, 0 /* Security directory */
.long _reloc_lma, _reloc_filesz /* Base relocation directory */
.long debugdir_lma, debugdir_size /* Debug directory */
.long 0, 0 /* Description directory */
.long 0, 0 /* Special directory */
.long 0, 0 /* Thread storage directory */
.long 0, 0 /* Load configuration directory */
.long 0, 0 /* Bound import directory */
.long 0, 0 /* Import address table directory */
.long 0, 0 /* Delay import directory */
.long 0, 0 /* Reserved */
.long 0, 0 /* Reserved */
.size opthdr, . - opthdr
.equ opthdr_size, . - opthdr
/* PE sections */
pe_sections:
text_section:
.asciz ".text" /* Section name */
.align 8
.long _text_filesz /* Section size */
.long _text_lma /* Relative Virtual Address */
.long _text_filesz /* Section size (rounded up) */
.long _text_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0x68000020 /* Characteristics */
rodata_section:
.asciz ".rodata" /* Section name */
.align 8
.long _rodata_filesz /* Section size */
.long _rodata_lma /* Relative Virtual Address */
.long _rodata_filesz /* Section size (rounded up) */
.long _rodata_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0x48000040 /* Characteristics */
data_section:
.asciz ".data" /* Section name */
.align 8
.long _data_filesz /* Section size */
.long _data_lma /* Relative Virtual Address */
.long _data_filesz /* Section size (rounded up) */
.long _data_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0xc8000040 /* Characteristics */
reloc_section:
.asciz ".reloc" /* Section name */
.align 8
.long _reloc_filesz /* Section size */
.long _reloc_lma /* Relative Virtual Address */
.long _reloc_filesz /* Section size (rounded up) */
.long _reloc_lma /* Pointer to raw data */
.long 0 /* Link-time relocations */
.long 0 /* Line numbers */
.word 0 /* Number of link-time relocations */
.word 0 /* Number of line numbers */
.long 0x42000040 /* Characteristics */
pe_sections_end:
.size pe_sections, . - pe_sections
.equ num_pe_sections, ( ( . - pe_sections ) / 0x28 )
/* Debug directory */
.section ".rodata"
.globl debugdir
debugdir:
.long 0 /* Characteristics */
.long 0x10d1a884 /* Timestamp */
.word 0 /* Major version */
.word 0 /* Minor version */
.long 0x02 /* RSDS? */
.long codeview_rsds_size /* Size of data */
.long codeview_rsds_lma /* RVA */
.long codeview_rsds_lma /* File offset */
.size debugdir, . - debugdir
.equ debugdir_size, . - debugdir
/* Codeview structure */
.globl codeview_rsds
codeview_rsds:
.ascii "RSDS" /* Magic number */
.long 0, 0, 0, 0, 0 /* Unused by EFI */
.asciz "efiprefix.pdb"
.size codeview_rsds, . - codeview_rsds
.equ codeview_rsds_size, . - codeview_rsds

View File

@ -1,180 +0,0 @@
/* -*- sh -*- */
/*
* Linker script for EFI images
*
*/
EXTERN ( efi_entry )
SECTIONS {
/* The file starts at a virtual address of zero, and sections are
* contiguous. Each section is aligned to at least _max_align,
* which defaults to 32. Load addresses are equal to virtual
* addresses.
*/
. = 0;
_max_align = 32;
/*
* The prefix
*
*/
.prefix : {
_prefix = .;
*(.prefix)
*(.prefix.*)
_mprefix = .;
} .bss.prefix (NOLOAD) : {
_eprefix = .;
}
_prefix_filesz = ABSOLUTE ( _mprefix - _prefix );
_prefix_memsz = ABSOLUTE ( _eprefix - _prefix );
/*
* The text section
*
*/
. = ALIGN ( _max_align );
.text : {
_text = .;
*(.text)
*(.text.*)
_mtext = .;
} .bss.text (NOLOAD) : {
_etext = .;
}
_text_filesz = ABSOLUTE ( _mtext - _text );
_text_memsz = ABSOLUTE ( _etext - _text );
/*
* The rodata section
*
*/
. = ALIGN ( _max_align );
.rodata : {
_rodata = .;
*(.rodata)
*(.rodata.*)
_mrodata = .;
} .bss.rodata (NOLOAD) : {
_erodata = .;
}
_rodata_filesz = ABSOLUTE ( _mrodata - _rodata );
_rodata_memsz = ABSOLUTE ( _erodata - _rodata );
/*
* The data section
*
*/
. = ALIGN ( _max_align );
.data : {
_data = .;
*(.data)
*(.data.*)
*(SORT(.tbl.*)) /* Various tables. See include/tables.h */
/* EFI seems to not support proper bss sections */
*(.bss)
*(.bss.*)
*(COMMON)
*(.stack)
*(.stack.*)
_mdata = .;
} .bss.data (NOLOAD) : {
_edata = .;
}
_data_filesz = ABSOLUTE ( _mdata - _data );
_data_memsz = ABSOLUTE ( _edata - _data );
/*
* The bss section
*
*/
. = ALIGN ( _max_align );
.bss : {
_bss = .;
/* EFI seems to not support proper bss sections */
_mbss = .;
} .bss.bss (NOLOAD) : {
_ebss = .;
}
_bss_filesz = ABSOLUTE ( _mbss - _bss );
_bss_memsz = ABSOLUTE ( _ebss - _bss );
/*
* The reloc section
*
*/
. = ALIGN ( _max_align );
.reloc : {
_reloc = .;
/* Provide some dummy contents to force ld to include this
* section. It will be created by the efilink utility.
*/
BYTE(0);
_mreloc = .;
} .bss.reloc (NOLOAD) : {
_ereloc = .;
}
_reloc_filesz = ABSOLUTE ( _mreloc - _reloc );
_reloc_memsz = ABSOLUTE ( _ereloc - _reloc );
_filesz = ABSOLUTE ( . );
/*
* Weak symbols that need zero values if not otherwise defined
*
*/
.weak 0x0 : {
_weak = .;
*(.weak)
_eweak = .;
}
_assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" );
/*
* Dispose of the comment and note sections to make the link map
* easier to read
*
*/
/DISCARD/ : {
*(.comment)
*(.comment.*)
*(.note)
*(.note.*)
*(.eh_frame)
*(.eh_frame.*)
*(.rel)
*(.rel.*)
}
/*
* Load address calculations.
*
*/
_prefix_lma = ABSOLUTE ( _prefix );
_text_lma = ABSOLUTE ( _text );
_rodata_lma = ABSOLUTE ( _rodata );
_data_lma = ABSOLUTE ( _data );
_bss_lma = ABSOLUTE ( _bss );
_reloc_lma = ABSOLUTE ( _reloc );
/*
* Load addresses required by the prefix
*
*/
efi_entry_lma = ABSOLUTE ( efi_entry );
debugdir_lma = ABSOLUTE ( debugdir );
codeview_rsds_lma = ABSOLUTE ( codeview_rsds );
}

View File

@ -124,5 +124,7 @@ extern EFI_HANDLE efi_image_handle;
extern EFI_SYSTEM_TABLE *efi_systab;
extern const char * efi_strerror ( EFI_STATUS efirc );
extern EFI_STATUS efi_init ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab );
#endif /* _EFI_H */

View File

@ -16,7 +16,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <gpxe/efi/efi.h>
#include <gpxe/uuid.h>
@ -58,14 +57,14 @@ static void * efi_find_table ( EFI_GUID *guid ) {
}
/**
* EFI entry point
* Initialise EFI environment
*
* @v image_handle Image handle
* @v systab System table
* @ret efirc EFI return status code
*/
EFI_STATUS EFIAPI efi_entry ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab ) {
EFI_STATUS efi_init ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab ) {
EFI_BOOT_SERVICES *bs;
struct efi_protocol *prot;
struct efi_config_table *tab;
@ -119,6 +118,5 @@ EFI_STATUS EFIAPI efi_entry ( EFI_HANDLE image_handle,
}
}
/* Call to main() */
return RC_TO_EFIRC ( main () );
return 0;
}

3
src/util/.gitignore vendored
View File

@ -2,4 +2,5 @@ nrv2b
zbin
hijack
prototester
efilink
elf2efi32
elf2efi64

View File

@ -1,515 +0,0 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <bfd.h>
struct bfd_file {
bfd *bfd;
asymbol **symtab;
long symcount;
};
struct pe_relocs {
struct pe_relocs *next;
unsigned long start_rva;
unsigned int used_relocs;
unsigned int total_relocs;
uint16_t *relocs;
};
/**
* 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 ) {
fprintf ( stderr, "Could not allocate %zd bytes\n", len );
exit ( 1 );
}
return ptr;
}
/**
* 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:
fprintf ( stderr, "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;
}
/**
* Read symbol table
*
* @v bfd BFD file
*/
static void read_symtab ( struct bfd_file *bfd ) {
long symtab_size;
/* Get symbol table size */
symtab_size = bfd_get_symtab_upper_bound ( bfd->bfd );
if ( symtab_size < 0 ) {
bfd_perror ( "Could not get symbol table upper bound" );
exit ( 1 );
}
/* Allocate and read symbol table */
bfd->symtab = xmalloc ( symtab_size );
bfd->symcount = bfd_canonicalize_symtab ( bfd->bfd, bfd->symtab );
if ( bfd->symcount < 0 ) {
bfd_perror ( "Cannot read symbol table" );
exit ( 1 );
}
}
/**
* Read relocation table
*
* @v bfd BFD file
* @v section Section
* @v symtab Symbol table
* @ret reltab Relocation table
*/
static arelent ** read_reltab ( struct bfd_file *bfd, asection *section ) {
long reltab_size;
arelent **reltab;
long numrels;
/* Get relocation table size */
reltab_size = bfd_get_reloc_upper_bound ( bfd->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->bfd, section, reltab,
bfd->symtab );
if ( numrels < 0 ) {
bfd_perror ( "Cannot read relocation table" );
exit ( 1 );
}
return reltab;
}
/**
* Open input BFD file
*
* @v filename File name
* @ret ibfd BFD file
*/
static struct bfd_file * open_input_bfd ( const char *filename ) {
struct bfd_file *ibfd;
/* Create BFD file */
ibfd = xmalloc ( sizeof ( *ibfd ) );
memset ( ibfd, 0, sizeof ( *ibfd ) );
/* Open the file */
ibfd->bfd = bfd_openr ( filename, NULL );
if ( ! ibfd->bfd ) {
fprintf ( stderr, "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 ( ibfd->bfd, bfd_object ) < 0 ) {
fprintf ( stderr, "%s is not an object file\n", filename );
exit ( 1 );
}
/* Read symbols and relocation entries */
read_symtab ( ibfd );
return ibfd;
}
/**
* Open output BFD file
*
* @v filename File name
* @v ibfd Input BFD file
* @ret obfd BFD file
*/
static struct bfd_file * open_output_bfd ( const char *filename,
struct bfd_file *ibfd ) {
struct bfd_file *obfd;
asection *isection;
asection *osection;
/*
* Most of this code is based on what objcopy.c does.
*
*/
/* Create BFD file */
obfd = xmalloc ( sizeof ( *obfd ) );
memset ( obfd, 0, sizeof ( *obfd ) );
/* Open the file */
obfd->bfd = bfd_openw ( filename, ibfd->bfd->xvec->name );
if ( ! obfd->bfd ) {
fprintf ( stderr, "Cannot open %s: ", filename );
bfd_perror ( NULL );
exit ( 1 );
}
/* Copy per-file data */
if ( ! bfd_set_arch_mach ( obfd->bfd, bfd_get_arch ( ibfd->bfd ),
bfd_get_mach ( ibfd->bfd ) ) ) {
bfd_perror ( "Cannot copy architecture" );
exit ( 1 );
}
if ( ! bfd_set_format ( obfd->bfd, bfd_get_format ( ibfd->bfd ) ) ) {
bfd_perror ( "Cannot copy format" );
exit ( 1 );
}
if ( ! bfd_copy_private_header_data ( ibfd->bfd, obfd->bfd ) ) {
bfd_perror ( "Cannot copy private header data" );
exit ( 1 );
}
/* Create sections */
for ( isection = ibfd->bfd->sections ; isection ;
isection = isection->next ) {
osection = bfd_make_section_anyway ( obfd->bfd,
isection->name );
if ( ! osection ) {
bfd_perror ( "Cannot create section" );
exit ( 1 );
}
if ( ! bfd_set_section_flags ( obfd->bfd, osection,
isection->flags ) ) {
bfd_perror ( "Cannot copy section flags" );
exit ( 1 );
}
if ( ! bfd_set_section_size ( obfd->bfd, osection,
bfd_section_size ( ibfd->bfd, isection ) ) ) {
bfd_perror ( "Cannot copy section size" );
exit ( 1 );
}
if ( ! bfd_set_section_vma ( obfd->bfd, osection,
bfd_section_vma ( ibfd->bfd, isection ) ) ) {
bfd_perror ( "Cannot copy section VMA" );
exit ( 1 );
}
osection->lma = bfd_section_lma ( ibfd->bfd, isection );
if ( ! bfd_set_section_alignment ( obfd->bfd, osection,
bfd_section_alignment ( ibfd->bfd, isection ) ) ) {
bfd_perror ( "Cannot copy section alignment" );
exit ( 1 );
}
osection->entsize = isection->entsize;
isection->output_section = osection;
isection->output_offset = 0;
if ( ! bfd_copy_private_section_data ( ibfd->bfd, isection,
obfd->bfd, osection ) ){
bfd_perror ( "Cannot copy section private data" );
exit ( 1 );
}
}
/* Copy symbol table */
bfd_set_symtab ( obfd->bfd, ibfd->symtab, ibfd->symcount );
obfd->symtab = ibfd->symtab;
return obfd;
}
/**
* Copy section from input BFD file to output BFD file
*
* @v obfd Output BFD file
* @v ibfd Input BFD file
* @v section Section
*/
static void copy_bfd_section ( struct bfd_file *obfd, struct bfd_file *ibfd,
asection *isection ) {
size_t size;
void *buf;
arelent **reltab;
arelent **rel;
char *errmsg;
/* Read in original section */
size = bfd_section_size ( ibfd->bfd, isection );
if ( ! size )
return;
buf = xmalloc ( size );
if ( ( ! bfd_get_section_contents ( ibfd->bfd, isection,
buf, 0, size ) ) ) {
fprintf ( stderr, "Cannot read section %s: ", isection->name );
bfd_perror ( NULL );
exit ( 1 );
}
/* Perform relocations. We do this here, rather than letting
* ld do it for us when creating the input ELF file, so that
* we can change symbol values as a result of having created
* the .reloc section.
*/
reltab = read_reltab ( ibfd, isection );
for ( rel = reltab ; *rel ; rel++ ) {
bfd_perform_relocation ( ibfd->bfd, *rel, buf, isection,
NULL, &errmsg );
}
free ( reltab );
/* Write out modified section */
if ( ( ! bfd_set_section_contents ( obfd->bfd,
isection->output_section,
buf, 0, size ) ) ) {
fprintf ( stderr, "Cannot write section %s: ",
isection->output_section->name );
bfd_perror ( NULL );
exit ( 1 );
}
free ( buf );
}
/**
* Process relocation record
*
* @v section Section
* @v rel Relocation entry
* @v pe_reltab PE relocation table to fill in
*/
static void process_reloc ( asection *section, arelent *rel,
struct pe_relocs **pe_reltab ) {
reloc_howto_type *howto = rel->howto;
asymbol *sym = *(rel->sym_ptr_ptr);
unsigned long offset = ( section->lma + 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 {
fprintf ( stderr, "Unrecognised relocation type %s\n",
howto->name );
exit ( 1 );
}
}
/**
* Create .reloc section
*
* obfd Output BFD file
* section .reloc section in output file
* pe_reltab PE relocation table
*/
static void create_reloc_section ( struct bfd_file *obfd, asection *section,
struct pe_relocs *pe_reltab ) {
size_t raw_size;
size_t size;
size_t old_size;
void *buf;
asymbol **sym;
/* Build binary PE relocation table */
raw_size = output_pe_reltab ( pe_reltab, NULL );
size = ( ( raw_size + 31 ) & ~31 );
buf = xmalloc ( size );
memset ( buf, 0, size );
output_pe_reltab ( pe_reltab, buf );
/* Write .reloc section */
old_size = bfd_section_size ( obfd->bfd, section );
if ( ! bfd_set_section_size ( obfd->bfd, section, size ) ) {
bfd_perror ( "Cannot resize .reloc section" );
exit ( 1 );
}
if ( ! bfd_set_section_contents ( obfd->bfd, section,
buf, 0, size ) ) {
bfd_perror ( "Cannot set .reloc section contents" );
exit ( 1 );
}
/* Update symbols pertaining to the relocation directory */
for ( sym = obfd->symtab ; *sym ; sym++ ) {
if ( strcmp ( (*sym)->name, "_reloc_memsz" ) == 0 ) {
(*sym)->value = size;
} else if ( strcmp ( (*sym)->name, "_reloc_filesz" ) == 0 ){
(*sym)->value = raw_size;
} else if ( strcmp ( (*sym)->name, "_filesz" ) == 0 ) {
(*sym)->value += ( size - old_size );
}
}
}
int main ( int argc, const char *argv[] ) {
const char *iname;
const char *oname;
struct bfd_file *ibfd;
struct bfd_file *obfd;
asection *section;
arelent **reltab;
arelent **rel;
struct pe_relocs *pe_reltab = NULL;
asection *reloc_section;
/* Initialise libbfd */
bfd_init();
/* Identify intput and output files */
if ( argc != 3 ) {
fprintf ( stderr, "Syntax: %s infile outfile\n", argv[0] );
exit ( 1 );
}
iname = argv[1];
oname = argv[2];
/* Open BFD files */
ibfd = open_input_bfd ( iname );
obfd = open_output_bfd ( oname, ibfd );
/* Process relocations in all sections */
for ( section = ibfd->bfd->sections ; section ;
section = section->next ) {
reltab = read_reltab ( ibfd, section );
for ( rel = reltab ; *rel ; rel++ ) {
process_reloc ( section, *rel, &pe_reltab );
}
free ( reltab );
}
/* Create modified .reloc section */
reloc_section = bfd_get_section_by_name ( obfd->bfd, ".reloc" );
if ( ! reloc_section ) {
fprintf ( stderr, "Cannot find .reloc section\n" );
exit ( 1 );
}
create_reloc_section ( obfd, reloc_section, pe_reltab );
/* Copy other section contents */
for ( section = ibfd->bfd->sections ; section ;
section = section->next ) {
if ( section->output_section != reloc_section )
copy_bfd_section ( obfd, ibfd, section );
}
/* Write out files and clean up */
bfd_close ( obfd->bfd );
bfd_close ( ibfd->bfd );
return 0;
}

764
src/util/elf2efi.c Normal file
View File

@ -0,0 +1,764 @@
/*
* 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 <bfd.h>
/* Include the EFI PE image header file */
typedef uint8_t UINT8;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
typedef uint64_t UINT64;
#define SIGNATURE_16( a, b ) ( (a) | ( (b) << 8 ) )
#define SIGNATURE_32( a, b, c, d ) \
( (a) | ( (b) << 8 ) | ( (c) << 16 ) | ( (d) << 24 ) )
#define BIT0 0x00000001
#define BIT1 0x00000002
#define BIT2 0x00000004
#define BIT3 0x00000008
#define BIT4 0x00000010
#define BIT5 0x00000020
#define BIT6 0x00000040
#define BIT7 0x00000080
#define BIT8 0x00000100
#define BIT9 0x00000200
#define BIT10 0x00000400
#define BIT11 0x00000800
#define BIT12 0x00001000
#define BIT13 0x00002000
#define BIT14 0x00004000
#define BIT15 0x00008000
#define BIT16 0x00010000
#define BIT17 0x00020000
#define BIT18 0x00040000
#define BIT19 0x00080000
#define BIT20 0x00100000
#define BIT21 0x00200000
#define BIT22 0x00400000
#define BIT23 0x00800000
#define BIT24 0x01000000
#define BIT25 0x02000000
#define BIT26 0x04000000
#define BIT27 0x08000000
#define BIT28 0x10000000
#define BIT29 0x20000000
#define BIT30 0x40000000
#define BIT31 0x80000000
#include "../include/gpxe/efi/IndustryStandard/PeImage.h"
#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 ),
.Subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION,
.NumberOfRvaAndSizes =
EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES,
},
},
};
/**
* 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 ) {
fprintf ( stderr, "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:
fprintf ( stderr, "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 ) {
fprintf ( stderr, "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 ) {
fprintf ( stderr, "%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 ) ) {
fprintf ( stderr, "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 {
fprintf ( stderr, "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 ) {
fprintf ( stderr, "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 ) ) {
fprintf ( stderr, "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 ) {
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;
/* 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 );
/* 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 ) );
next_pe_section = &(*next_pe_section)->next;
/* Write out PE file */
pe = fopen ( pe_name, "w" );
if ( ! pe ) {
fprintf ( stderr, "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 );
}
int main ( int argc, char **argv ) {
/* Initialise libbfd */
bfd_init();
elf2pe ( argv[1], argv[2] );
return 0;
}