2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 * Derived from iPXE's elf2efi.c
29 #define PACKAGE "wimboot"
30 #define PACKAGE_VERSION VERSION
42 #include "efi/IndustryStandard/PeImage.h"
45 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
47 /* Maintain compatibility with binutils 2.34 */
48 #ifndef bfd_get_section_vma
49 #define bfd_get_section_vma(bfd, ptr) bfd_section_vma(ptr)
51 #ifndef bfd_get_section_flags
52 #define bfd_get_section_flags(bfd, ptr) bfd_section_flags(ptr)
55 /** PE header maximum length
57 * This maximum length is guaranteed by the fact that the PE headers
58 * have to fit entirely before the start of the bzImage header.
60 #define PE_HEADER_LEN 512
62 /** .reloc section index */
63 #define RELOC_SECTION_INDEX 3
67 struct pe_relocs
*next
;
68 unsigned long start_rva
;
69 unsigned int used_relocs
;
70 unsigned int total_relocs
;
74 /** Command-line options */
83 * @v len Length of memory to allocate
84 * @ret ptr Pointer to allocated memory
86 static void * xmalloc ( size_t len
) {
91 eprintf ( "Could not allocate %zd bytes\n", len
);
101 * @v fd File descriptor
103 * @v len Length of data
105 static void xwrite ( int fd
, const void *data
, size_t len
) {
108 written
= write ( fd
, data
, len
);
110 eprintf ( "Could not write %zd bytes: %s\n",
111 len
, strerror ( errno
) );
114 if ( ( size_t ) written
!= len
) {
115 eprintf ( "Wrote only %zd of %zd bytes\n", written
, len
);
121 * Seek to file position
123 * @v fd File descriptor
127 static void xlseek ( int fd
, off_t offset
, int whence
) {
130 pos
= lseek ( fd
, offset
, whence
);
132 eprintf ( "Could not seek: %s\n", strerror ( errno
) );
140 * @v fd File descriptor
142 static void xclose ( int fd
) {
144 if ( close ( fd
) < 0 ) {
145 eprintf ( "Could not close: %s\n", strerror ( errno
) );
151 * Open input BFD file
153 * @v filename File name
156 static bfd
* open_input_bfd ( const char *filename
) {
160 bfd
= bfd_openr ( filename
, NULL
);
162 eprintf ( "Cannot open %s: ", filename
);
167 /* The call to bfd_check_format() must be present, otherwise
168 * we get a segfault from later BFD calls.
170 if ( ! bfd_check_format ( bfd
, bfd_object
) ) {
171 eprintf ( "%s is not an object file: ", filename
);
184 static asymbol
** read_symtab ( bfd
*bfd
) {
189 /* Get symbol table size */
190 symtab_size
= bfd_get_symtab_upper_bound ( bfd
);
191 if ( symtab_size
< 0 ) {
192 bfd_perror ( "Could not get symbol table upper bound" );
196 /* Allocate and read symbol table */
197 symtab
= xmalloc ( symtab_size
);
198 symcount
= bfd_canonicalize_symtab ( bfd
, symtab
);
199 if ( symcount
< 0 ) {
200 bfd_perror ( "Cannot read symbol table" );
208 * Read relocation table
211 * @v symtab Symbol table
213 * @v symtab Symbol table
214 * @ret reltab Relocation table
216 static arelent
** read_reltab ( bfd
*bfd
, asymbol
**symtab
,
217 asection
*section
) {
222 /* Get relocation table size */
223 reltab_size
= bfd_get_reloc_upper_bound ( bfd
, section
);
224 if ( reltab_size
< 0 ) {
225 bfd_perror ( "Could not get relocation table upper bound" );
229 /* Allocate and read relocation table */
230 reltab
= xmalloc ( reltab_size
);
231 numrels
= bfd_canonicalize_reloc ( bfd
, section
, reltab
, symtab
);
233 bfd_perror ( "Cannot read relocation table" );
241 * Generate entry in PE relocation table
243 * @v pe_reltab PE relocation table
245 * @v size Size of relocation entry
247 static void generate_pe_reloc ( struct pe_relocs
**pe_reltab
,
248 unsigned long rva
, size_t size
) {
249 unsigned long start_rva
;
251 struct pe_relocs
*pe_rel
;
255 start_rva
= ( rva
& ~0xfff );
256 reloc
= ( rva
& 0xfff );
268 eprintf ( "Unsupported relocation size %zd\n", size
);
272 /* Locate or create PE relocation table */
273 for ( pe_rel
= *pe_reltab
; pe_rel
; pe_rel
= pe_rel
->next
) {
274 if ( pe_rel
->start_rva
== start_rva
)
278 pe_rel
= xmalloc ( sizeof ( *pe_rel
) );
279 memset ( pe_rel
, 0, sizeof ( *pe_rel
) );
280 pe_rel
->next
= *pe_reltab
;
282 pe_rel
->start_rva
= start_rva
;
285 /* Expand relocation list if necessary */
286 if ( pe_rel
->used_relocs
< pe_rel
->total_relocs
) {
287 relocs
= pe_rel
->relocs
;
289 pe_rel
->total_relocs
= ( pe_rel
->total_relocs
?
290 ( pe_rel
->total_relocs
* 2 ) : 256 );
291 relocs
= xmalloc ( pe_rel
->total_relocs
*
292 sizeof ( pe_rel
->relocs
[0] ) );
294 pe_rel
->total_relocs
* sizeof ( pe_rel
->relocs
[0] ) );
295 memcpy ( relocs
, pe_rel
->relocs
,
296 pe_rel
->used_relocs
* sizeof ( pe_rel
->relocs
[0] ) );
297 free ( pe_rel
->relocs
);
298 pe_rel
->relocs
= relocs
;
301 /* Store relocation */
302 pe_rel
->relocs
[ pe_rel
->used_relocs
++ ] = reloc
;
306 * Process relocation record
310 * @v rel Relocation entry
311 * @v pe_reltab PE relocation table to fill in
313 static void process_reloc ( bfd
*bfd __unused
, asection
*section
, arelent
*rel
,
314 struct pe_relocs
**pe_reltab
) {
315 reloc_howto_type
*howto
= rel
->howto
;
316 asymbol
*sym
= *(rel
->sym_ptr_ptr
);
317 unsigned long offset
= ( bfd_get_section_vma ( bfd
, section
) +
318 rel
->address
- BASE_ADDRESS
);
320 if ( bfd_is_abs_section ( sym
->section
) ) {
321 /* Skip absolute symbols; the symbol value won't
322 * change when the object is loaded.
324 } else if ( strcmp ( howto
->name
, "R_X86_64_64" ) == 0 ) {
325 /* Generate an 8-byte PE relocation */
326 generate_pe_reloc ( pe_reltab
, offset
, 8 );
327 } else if ( ( strcmp ( howto
->name
, "R_386_32" ) == 0 ) ||
328 ( strcmp ( howto
->name
, "R_X86_64_32" ) == 0 ) ||
329 ( strcmp ( howto
->name
, "R_X86_64_32S" ) == 0 ) ) {
330 /* Generate a 4-byte PE relocation */
331 generate_pe_reloc ( pe_reltab
, offset
, 4 );
332 } else if ( ( strcmp ( howto
->name
, "R_386_16" ) == 0 ) ||
333 ( strcmp ( howto
->name
, "R_X86_64_16" ) == 0 ) ) {
334 /* Generate a 2-byte PE relocation */
335 generate_pe_reloc ( pe_reltab
, offset
, 2 );
336 } else if ( ( strcmp ( howto
->name
, "R_386_PC32" ) == 0 ) ||
337 ( strcmp ( howto
->name
, "R_X86_64_PC32" ) == 0 ) ||
338 ( strcmp ( howto
->name
, "R_X86_64_PLT32" ) == 0 ) ) {
339 /* Skip PC-relative relocations; all relative offsets
340 * remain unaltered when the object is loaded.
343 eprintf ( "Unrecognised relocation type %s\n", howto
->name
);
349 * Calculate size of binary PE relocation table
352 * @v pe_reltab PE relocation table
353 * @ret size Size of binary table
355 static size_t output_pe_reltab ( int fd
, struct pe_relocs
*pe_reltab
) {
356 EFI_IMAGE_BASE_RELOCATION header
;
357 struct pe_relocs
*pe_rel
;
358 static uint8_t pad
[16];
359 unsigned int num_relocs
;
362 size_t total_size
= 0;
364 for ( pe_rel
= pe_reltab
; pe_rel
; pe_rel
= pe_rel
->next
) {
365 num_relocs
= ( ( pe_rel
->used_relocs
+ 1 ) & ~1 );
366 size
= ( sizeof ( header
) +
367 ( num_relocs
* sizeof ( uint16_t ) ) );
368 pad_size
= ( ( -size
) & ( sizeof ( pad
) - 1 ) );
370 header
.VirtualAddress
= pe_rel
->start_rva
;
371 header
.SizeOfBlock
= size
;
372 xwrite ( fd
, &header
, sizeof ( header
) );
373 xwrite ( fd
, pe_rel
->relocs
,
374 ( num_relocs
* sizeof ( uint16_t ) ) );
375 xwrite ( fd
, pad
, pad_size
);
383 * Add relocation information
385 * @v elf_name ELF file name
386 * @v pe_name PE file name
388 static void efireloc ( const char *elf_name
, const char *pe_name
) {
389 struct pe_relocs
*pe_reltab
= NULL
;
391 EFI_IMAGE_DOS_HEADER
*dos
;
392 EFI_IMAGE_OPTIONAL_HEADER_UNION
*nt
;
393 EFI_IMAGE_DATA_DIRECTORY
*data_dir
;
394 EFI_IMAGE_SECTION_HEADER
*pe_sections
;
403 /* Open the output file */
404 fd
= open ( pe_name
, O_RDWR
);
406 eprintf ( "Could not open %s: %s\n",
407 pe_name
, strerror ( errno
) );
411 /* Map the output file header */
412 dos
= mmap ( NULL
, PE_HEADER_LEN
, ( PROT_READ
| PROT_WRITE
),
415 eprintf ( "Could not mmap %s: %s\n",
416 pe_name
, strerror ( errno
) );
420 /* Locate the modifiable fields within the output file header */
421 nt
= ( ( ( void * ) dos
) + dos
->e_lfanew
);
422 if ( nt
->Pe32
.FileHeader
.Machine
== EFI_IMAGE_MACHINE_IA32
) {
423 image_size
= &nt
->Pe32
.OptionalHeader
.SizeOfImage
;
424 data_dir
= nt
->Pe32
.OptionalHeader
.DataDirectory
;
425 pe_sections
= ( ( ( void * ) nt
) + sizeof ( nt
->Pe32
) );
426 } else if ( nt
->Pe32Plus
.FileHeader
.Machine
== EFI_IMAGE_MACHINE_X64
) {
427 image_size
= &nt
->Pe32Plus
.OptionalHeader
.SizeOfImage
;
428 data_dir
= nt
->Pe32Plus
.OptionalHeader
.DataDirectory
;
429 pe_sections
= ( ( ( void * ) nt
) + sizeof ( nt
->Pe32Plus
) );
431 eprintf ( "Unrecognised machine type\n" );
435 /* Open the input file */
436 bfd
= open_input_bfd ( elf_name
);
437 symtab
= read_symtab ( bfd
);
439 /* For each input section, create the appropriate relocation records */
440 for ( section
= bfd
->sections
; section
; section
= section
->next
) {
441 /* Discard non-allocatable sections */
442 if ( ! ( bfd_get_section_flags ( bfd
, section
) & SEC_ALLOC
) )
444 /* Add relocations from this section */
445 reltab
= read_reltab ( bfd
, symtab
, section
);
446 for ( rel
= reltab
; *rel
; rel
++ )
447 process_reloc ( bfd
, section
, *rel
, &pe_reltab
);
451 /* Close input file */
454 /* Generate relocation section */
455 xlseek ( fd
, 0, SEEK_END
);
456 reloc_len
= output_pe_reltab ( fd
, pe_reltab
);
458 /* Modify image header */
459 *image_size
+= reloc_len
;
460 data_dir
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
].Size
= reloc_len
;
461 pe_sections
[RELOC_SECTION_INDEX
].Misc
.VirtualSize
= reloc_len
;
462 pe_sections
[RELOC_SECTION_INDEX
].SizeOfRawData
= reloc_len
;
464 /* Unmap output file header */
465 munmap ( dos
, PE_HEADER_LEN
);
467 /* Close output file */
474 * @v program_name Program name
476 static void print_help ( const char *program_name
) {
477 eprintf ( "Syntax: %s [-v|-q] infile outfile\n", program_name
);
481 * Parse command-line options
483 * @v argc Argument count
484 * @v argv Argument list
485 * @v opts Options structure to populate
487 static int parse_options ( const int argc
, char **argv
,
488 struct options
*opts
) {
492 int option_index
= 0;
493 static struct option long_options
[] = {
494 { "help", 0, NULL
, 'h' },
495 { "verbose", 0, NULL
, 'v' },
496 { "quiet", 0, NULL
, 'q' },
500 if ( ( c
= getopt_long ( argc
, argv
, "hvq",
502 &option_index
) ) == -1 ) {
511 if ( opts
->verbosity
)
515 print_help ( argv
[0] );
528 * @v argc Number of arguments
529 * @v argv Command-line arguments
530 * @ret rc Return status code
532 int main ( int argc
, char **argv
) {
533 struct options opts
= {
540 /* Initialise libbfd */
543 /* Parse command-line arguments */
544 infile_index
= parse_options ( argc
, argv
, &opts
);
545 if ( argc
!= ( infile_index
+ 2 ) ) {
546 print_help ( argv
[0] );
549 infile
= argv
[infile_index
];
550 outfile
= argv
[infile_index
+ 1];
552 /* Add relocation information */
553 efireloc ( infile
, outfile
);