From 5de37e124fd21c8f918f3fe26fc33a764709ead4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 10 Sep 2014 02:58:50 +0100 Subject: [PATCH] [efi] Add efifatbin utility Add utility for constructing EFI fat binaries (dual 32/64-bit binaries, usable only on Apple EFI systems). This utility is not part of the standard build process. To use it: make util/efifatbin bin-i386-efi/ipxe.efi bin-x86_64-efi/ipxe.efi and then ./util/efifatbin bin-*-efi/ipxe.efi fat-ipxe.efi Requested-by: Brandon Penglase Tested-by: Brandon Penglase Signed-off-by: Michael Brown --- src/Makefile | 1 + src/Makefile.housekeeping | 5 + src/util/.gitignore | 1 + src/util/efifatbin.c | 260 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 src/util/efifatbin.c diff --git a/src/Makefile b/src/Makefile index c6f4d590..b742d128 100644 --- a/src/Makefile +++ b/src/Makefile @@ -46,6 +46,7 @@ ZBIN := ./util/zbin ELF2EFI32 := ./util/elf2efi32 ELF2EFI64 := ./util/elf2efi64 EFIROM := ./util/efirom +EFIFATBIN := ./util/efifatbin ICCFIX := ./util/iccfix EINFO := ./util/einfo GENKEYMAP := ./util/genkeymap.pl diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index c89b23b7..1a75d393 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1268,6 +1268,11 @@ $(EFIROM) : util/efirom.c $(MAKEDEPS) $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -o $@ $< CLEANUP += $(EFIROM) +$(EFIFATBIN) : util/efifatbin.c $(MAKEDEPS) + $(QM)$(ECHO) " [HOSTCC] $@" + $(Q)$(HOST_CC) $(HOST_CFLAGS) -idirafter include -o $@ $< +CLEANUP += $(EFIFATBIN) + ############################################################################### # # The ICC fixup utility diff --git a/src/util/.gitignore b/src/util/.gitignore index 633ca322..33bedefd 100644 --- a/src/util/.gitignore +++ b/src/util/.gitignore @@ -5,5 +5,6 @@ prototester elf2efi32 elf2efi64 efirom +efifatbin iccfix einfo diff --git a/src/util/efifatbin.c b/src/util/efifatbin.c new file mode 100644 index 00000000..c02f750b --- /dev/null +++ b/src/util/efifatbin.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2014 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +/** Command-line options */ +struct options { +}; + +/** EFI fat binary file header */ +struct efifatbin_file_header { + /** Signature */ + uint32_t signature; + /** Count */ + uint32_t count; +} __attribute__ (( packed )); + +/** EFI fat binary signature */ +#define EFIFATBIN_SIGNATURE 0x0ef1fab9 + +/** EFI fat binary image header */ +struct efifatbin_image_header { + /** Flags */ + uint64_t flags; + /** Offset */ + uint32_t offset; + /** Length */ + uint32_t len; + /** Padding */ + uint32_t pad; +} __attribute__ (( packed )); + +/** EFI fat binary default flags */ +#define EFIFATBIN_FLAGS 0x0000000300000007ULL + +/** EFI fat binary 64-bit flag */ +#define EFIFATBIN_64BIT 0x0000000001000000ULL + +/** + * Allocate memory + * + * @v len Length of memory to allocate + * @ret ptr Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { + void *ptr; + + ptr = malloc ( len ); + if ( ! ptr ) { + eprintf ( "Could not allocate %zd bytes\n", len ); + exit ( 1 ); + } + + return ptr; +} + +/** + * Generate EFI fat binary + * + * @v count Number of input files + * @v infile_names Input filenames + * @v outfile_name Output filename + */ +static void make_efifatbin ( unsigned int count, char **infile_names, + const char *outfile_name ) { + FILE *infile[count]; + FILE *outfile; + struct stat stat[count]; + void *buf[count]; + struct efifatbin_file_header file_header; + struct efifatbin_image_header header[count]; + size_t offset; + EFI_IMAGE_DOS_HEADER *dos; + union { + EFI_IMAGE_NT_HEADERS32 nt32; + EFI_IMAGE_NT_HEADERS64 nt64; + } *nt; + unsigned int i; + + /* Generate file header */ + file_header.signature = EFIFATBIN_SIGNATURE; + file_header.count = count; + offset = ( sizeof ( file_header ) + sizeof ( header ) ); + + /* Process input files */ + for ( i = 0 ; i < count ; i++ ) { + + /* Open input file */ + infile[i] = fopen ( infile_names[i], "r" ); + if ( ! infile[i] ) { + eprintf ( "Could not open %s for reading: %s\n", + infile_names[i], strerror ( errno ) ); + exit ( 1 ); + } + + /* Determine PE file size */ + if ( fstat ( fileno ( infile[i] ), &stat[i] ) != 0 ) { + eprintf ( "Could not stat %s: %s\n", + infile_names[i], strerror ( errno ) ); + exit ( 1 ); + } + + /* Allocate buffer and read in PE file */ + buf[i] = xmalloc ( stat[i].st_size ); + if ( fread ( buf[i], stat[i].st_size, 1, infile[i] ) != 1 ) { + eprintf ( "Could not read %s: %s\n", + infile_names[i], strerror ( errno ) ); + exit ( 1 ); + } + + /* Close input file */ + fclose ( infile[i] ); + + /* Generate image header */ + header[i].flags = EFIFATBIN_FLAGS; + header[i].offset = offset; + header[i].len = stat[i].st_size; + header[i].pad = 0; + + /* Determine architecture */ + dos = buf[i]; + nt = ( buf[i] + dos->e_lfanew ); + if ( nt->nt32.FileHeader.Machine == EFI_IMAGE_MACHINE_X64 ) + header[i].flags |= EFIFATBIN_64BIT; + + /* Allow space for this image */ + offset += stat[i].st_size; + } + + /* Open output file */ + outfile = fopen ( outfile_name, "w" ); + if ( ! outfile ) { + eprintf ( "Could not open %s for writing: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + + /* Write fat binary header */ + if ( fwrite ( &file_header, sizeof ( file_header ), 1, outfile ) != 1 ){ + eprintf ( "Could not write %s: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + for ( i = 0 ; i < count ; i++ ) { + if ( fwrite ( &header[i], sizeof ( header[i] ), 1, + outfile ) != 1 ) { + eprintf ( "Could not write %s: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + } + + /* Write images */ + for ( i = 0 ; i < count ; i++ ) { + if ( fwrite ( buf[i], stat[i].st_size, 1, outfile ) != 1 ) { + eprintf ( "Could not write %s: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + } + + /* Close output file */ + fclose ( outfile ); +} + +/** + * Print help + * + * @v program_name Program name + */ +static void print_help ( const char *program_name ) { + eprintf ( "Syntax: %s infile [infile...] outfile\n", program_name ); +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v opts Options structure to populate + */ +static int parse_options ( const int argc, char **argv, + struct options *opts __attribute__ (( unused )) ) { + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + if ( ( c = getopt_long ( argc, argv, "h", + long_options, + &option_index ) ) == -1 ) { + break; + } + + switch ( c ) { + case 'h': + print_help ( argv[0] ); + exit ( 0 ); + case '?': + default: + exit ( 2 ); + } + } + return optind; +} + +int main ( int argc, char **argv ) { + struct options opts; + int infile_index; + int outfile_index; + int count; + + /* Parse command-line arguments */ + memset ( &opts, 0, sizeof ( opts ) ); + infile_index = parse_options ( argc, argv, &opts ); + outfile_index = ( argc - 1 ); + count = ( outfile_index - infile_index ); + if ( count <= 0 ) { + print_help ( argv[0] ); + exit ( 2 ); + } + + /* Generate fat binary */ + make_efifatbin ( count, &argv[infile_index], argv[outfile_index] ); + + return 0; +}