]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - wimboot/wimboot-2.7.3/src/efireloc.c
1.1.07 release
[Ventoy.git] / wimboot / wimboot-2.7.3 / src / efireloc.c
1 /*
2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3 *
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.
8 *
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.
13 *
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
17 * 02110-1301, USA.
18 */
19
20 /**
21 * @file
22 *
23 * EFI relocations
24 *
25 * Derived from iPXE's elf2efi.c
26 *
27 */
28
29 #define PACKAGE "wimboot"
30 #define PACKAGE_VERSION VERSION
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <sys/mman.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <bfd.h>
41 #include "efi.h"
42 #include "efi/IndustryStandard/PeImage.h"
43 #include "wimboot.h"
44
45 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
46
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)
50 #endif
51 #ifndef bfd_get_section_flags
52 #define bfd_get_section_flags(bfd, ptr) bfd_section_flags(ptr)
53 #endif
54
55 /** PE header maximum length
56 *
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.
59 */
60 #define PE_HEADER_LEN 512
61
62 /** .reloc section index */
63 #define RELOC_SECTION_INDEX 3
64
65 /** PE relocations */
66 struct pe_relocs {
67 struct pe_relocs *next;
68 unsigned long start_rva;
69 unsigned int used_relocs;
70 unsigned int total_relocs;
71 uint16_t *relocs;
72 };
73
74 /** Command-line options */
75 struct options {
76 /** Verbosity */
77 int verbosity;
78 };
79
80 /**
81 * Allocate memory
82 *
83 * @v len Length of memory to allocate
84 * @ret ptr Pointer to allocated memory
85 */
86 static void * xmalloc ( size_t len ) {
87 void *ptr;
88
89 ptr = malloc ( len );
90 if ( ! ptr ) {
91 eprintf ( "Could not allocate %zd bytes\n", len );
92 exit ( 1 );
93 }
94
95 return ptr;
96 }
97
98 /**
99 * Write to file
100 *
101 * @v fd File descriptor
102 * @v data Data
103 * @v len Length of data
104 */
105 static void xwrite ( int fd, const void *data, size_t len ) {
106 ssize_t written;
107
108 written = write ( fd, data, len );
109 if ( written < 0 ) {
110 eprintf ( "Could not write %zd bytes: %s\n",
111 len, strerror ( errno ) );
112 exit ( 1 );
113 }
114 if ( ( size_t ) written != len ) {
115 eprintf ( "Wrote only %zd of %zd bytes\n", written, len );
116 exit ( 1 );
117 }
118 }
119
120 /**
121 * Seek to file position
122 *
123 * @v fd File descriptor
124 * @v offset Offset
125 * @v whence Origin
126 */
127 static void xlseek ( int fd, off_t offset, int whence ) {
128 off_t pos;
129
130 pos = lseek ( fd, offset, whence );
131 if ( pos < 0 ) {
132 eprintf ( "Could not seek: %s\n", strerror ( errno ) );
133 exit ( 1 );
134 }
135 }
136
137 /**
138 * Close file
139 *
140 * @v fd File descriptor
141 */
142 static void xclose ( int fd ) {
143
144 if ( close ( fd ) < 0 ) {
145 eprintf ( "Could not close: %s\n", strerror ( errno ) );
146 exit ( 1 );
147 }
148 }
149
150 /**
151 * Open input BFD file
152 *
153 * @v filename File name
154 * @ret ibfd BFD file
155 */
156 static bfd * open_input_bfd ( const char *filename ) {
157 bfd *bfd;
158
159 /* Open the file */
160 bfd = bfd_openr ( filename, NULL );
161 if ( ! bfd ) {
162 eprintf ( "Cannot open %s: ", filename );
163 bfd_perror ( NULL );
164 exit ( 1 );
165 }
166
167 /* The call to bfd_check_format() must be present, otherwise
168 * we get a segfault from later BFD calls.
169 */
170 if ( ! bfd_check_format ( bfd, bfd_object ) ) {
171 eprintf ( "%s is not an object file: ", filename );
172 bfd_perror ( NULL );
173 exit ( 1 );
174 }
175
176 return bfd;
177 }
178
179 /**
180 * Read symbol table
181 *
182 * @v bfd BFD file
183 */
184 static asymbol ** read_symtab ( bfd *bfd ) {
185 long symtab_size;
186 asymbol **symtab;
187 long symcount;
188
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" );
193 exit ( 1 );
194 }
195
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" );
201 exit ( 1 );
202 }
203
204 return symtab;
205 }
206
207 /**
208 * Read relocation table
209 *
210 * @v bfd BFD file
211 * @v symtab Symbol table
212 * @v section Section
213 * @v symtab Symbol table
214 * @ret reltab Relocation table
215 */
216 static arelent ** read_reltab ( bfd *bfd, asymbol **symtab,
217 asection *section ) {
218 long reltab_size;
219 arelent **reltab;
220 long numrels;
221
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" );
226 exit ( 1 );
227 }
228
229 /* Allocate and read relocation table */
230 reltab = xmalloc ( reltab_size );
231 numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab );
232 if ( numrels < 0 ) {
233 bfd_perror ( "Cannot read relocation table" );
234 exit ( 1 );
235 }
236
237 return reltab;
238 }
239
240 /**
241 * Generate entry in PE relocation table
242 *
243 * @v pe_reltab PE relocation table
244 * @v rva RVA
245 * @v size Size of relocation entry
246 */
247 static void generate_pe_reloc ( struct pe_relocs **pe_reltab,
248 unsigned long rva, size_t size ) {
249 unsigned long start_rva;
250 uint16_t reloc;
251 struct pe_relocs *pe_rel;
252 uint16_t *relocs;
253
254 /* Construct */
255 start_rva = ( rva & ~0xfff );
256 reloc = ( rva & 0xfff );
257 switch ( size ) {
258 case 8:
259 reloc |= 0xa000;
260 break;
261 case 4:
262 reloc |= 0x3000;
263 break;
264 case 2:
265 reloc |= 0x2000;
266 break;
267 default:
268 eprintf ( "Unsupported relocation size %zd\n", size );
269 exit ( 1 );
270 }
271
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 )
275 break;
276 }
277 if ( ! pe_rel ) {
278 pe_rel = xmalloc ( sizeof ( *pe_rel ) );
279 memset ( pe_rel, 0, sizeof ( *pe_rel ) );
280 pe_rel->next = *pe_reltab;
281 *pe_reltab = pe_rel;
282 pe_rel->start_rva = start_rva;
283 }
284
285 /* Expand relocation list if necessary */
286 if ( pe_rel->used_relocs < pe_rel->total_relocs ) {
287 relocs = pe_rel->relocs;
288 } else {
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] ) );
293 memset ( 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;
299 }
300
301 /* Store relocation */
302 pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc;
303 }
304
305 /**
306 * Process relocation record
307 *
308 * @v bfd BFD file
309 * @v section Section
310 * @v rel Relocation entry
311 * @v pe_reltab PE relocation table to fill in
312 */
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 );
319
320 if ( bfd_is_abs_section ( sym->section ) ) {
321 /* Skip absolute symbols; the symbol value won't
322 * change when the object is loaded.
323 */
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.
341 */
342 } else {
343 eprintf ( "Unrecognised relocation type %s\n", howto->name );
344 exit ( 1 );
345 }
346 }
347
348 /**
349 * Calculate size of binary PE relocation table
350 *
351 * @v fh File handle
352 * @v pe_reltab PE relocation table
353 * @ret size Size of binary table
354 */
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;
360 size_t size;
361 size_t pad_size;
362 size_t total_size = 0;
363
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 ) );
369 size += pad_size;
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 );
376 total_size += size;
377 }
378
379 return total_size;
380 }
381
382 /**
383 * Add relocation information
384 *
385 * @v elf_name ELF file name
386 * @v pe_name PE file name
387 */
388 static void efireloc ( const char *elf_name, const char *pe_name ) {
389 struct pe_relocs *pe_reltab = NULL;
390 int fd;
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;
395 UINT32 *image_size;
396 bfd *bfd;
397 asymbol **symtab;
398 asection *section;
399 arelent **reltab;
400 arelent **rel;
401 size_t reloc_len;
402
403 /* Open the output file */
404 fd = open ( pe_name, O_RDWR );
405 if ( fd < 0 ) {
406 eprintf ( "Could not open %s: %s\n",
407 pe_name, strerror ( errno ) );
408 exit ( 1 );
409 }
410
411 /* Map the output file header */
412 dos = mmap ( NULL, PE_HEADER_LEN, ( PROT_READ | PROT_WRITE ),
413 MAP_SHARED, fd, 0 );
414 if ( ! dos ) {
415 eprintf ( "Could not mmap %s: %s\n",
416 pe_name, strerror ( errno ) );
417 exit ( 1 );
418 }
419
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 ) );
430 } else {
431 eprintf ( "Unrecognised machine type\n" );
432 exit ( 1 );
433 }
434
435 /* Open the input file */
436 bfd = open_input_bfd ( elf_name );
437 symtab = read_symtab ( bfd );
438
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 ) )
443 continue;
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 );
448 free ( reltab );
449 }
450
451 /* Close input file */
452 bfd_close ( bfd );
453
454 /* Generate relocation section */
455 xlseek ( fd, 0, SEEK_END );
456 reloc_len = output_pe_reltab ( fd, pe_reltab );
457
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;
463
464 /* Unmap output file header */
465 munmap ( dos, PE_HEADER_LEN );
466
467 /* Close output file */
468 xclose ( fd );
469 }
470
471 /**
472 * Print help
473 *
474 * @v program_name Program name
475 */
476 static void print_help ( const char *program_name ) {
477 eprintf ( "Syntax: %s [-v|-q] infile outfile\n", program_name );
478 }
479
480 /**
481 * Parse command-line options
482 *
483 * @v argc Argument count
484 * @v argv Argument list
485 * @v opts Options structure to populate
486 */
487 static int parse_options ( const int argc, char **argv,
488 struct options *opts ) {
489 int c;
490
491 while (1) {
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' },
497 { 0, 0, 0, 0 }
498 };
499
500 if ( ( c = getopt_long ( argc, argv, "hvq",
501 long_options,
502 &option_index ) ) == -1 ) {
503 break;
504 }
505
506 switch ( c ) {
507 case 'v':
508 opts->verbosity++;
509 break;
510 case 'q':
511 if ( opts->verbosity )
512 opts->verbosity--;
513 break;
514 case 'h':
515 print_help ( argv[0] );
516 exit ( 0 );
517 case '?':
518 default:
519 exit ( 2 );
520 }
521 }
522 return optind;
523 }
524
525 /**
526 * Main program
527 *
528 * @v argc Number of arguments
529 * @v argv Command-line arguments
530 * @ret rc Return status code
531 */
532 int main ( int argc, char **argv ) {
533 struct options opts = {
534 .verbosity = 0,
535 };
536 int infile_index;
537 const char *infile;
538 const char *outfile;
539
540 /* Initialise libbfd */
541 bfd_init();
542
543 /* Parse command-line arguments */
544 infile_index = parse_options ( argc, argv, &opts );
545 if ( argc != ( infile_index + 2 ) ) {
546 print_help ( argv[0] );
547 exit ( 2 );
548 }
549 infile = argv[infile_index];
550 outfile = argv[infile_index + 1];
551
552 /* Add relocation information */
553 efireloc ( infile, outfile );
554
555 return 0;
556 }