| /* |
| * Copyright (C) 2014 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., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, USA. |
| */ |
| |
| #define FILE_LICENCE(...) extern void __file_licence ( void ) |
| #include <stdint.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <getopt.h> |
| #include <ipxe/efi/Uefi.h> |
| #include <ipxe/efi/IndustryStandard/PeImage.h> |
| |
| #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; |
| } |