1 | /* |
---|
2 | * Copyright (C) 2009 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
17 | */ |
---|
18 | |
---|
19 | #define _GNU_SOURCE |
---|
20 | #include <stdint.h> |
---|
21 | #include <stddef.h> |
---|
22 | #include <stdlib.h> |
---|
23 | #include <stdio.h> |
---|
24 | #include <string.h> |
---|
25 | #include <unistd.h> |
---|
26 | #include <errno.h> |
---|
27 | #include <assert.h> |
---|
28 | #include <getopt.h> |
---|
29 | #include <bfd.h> |
---|
30 | #include <gpxe/efi/efi.h> |
---|
31 | #include <gpxe/efi/IndustryStandard/PeImage.h> |
---|
32 | #include <libgen.h> |
---|
33 | |
---|
34 | #define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) |
---|
35 | |
---|
36 | #define EFI_FILE_ALIGN 0x20 |
---|
37 | |
---|
38 | struct pe_section { |
---|
39 | struct pe_section *next; |
---|
40 | EFI_IMAGE_SECTION_HEADER hdr; |
---|
41 | uint8_t contents[0]; |
---|
42 | }; |
---|
43 | |
---|
44 | struct pe_relocs { |
---|
45 | struct pe_relocs *next; |
---|
46 | unsigned long start_rva; |
---|
47 | unsigned int used_relocs; |
---|
48 | unsigned int total_relocs; |
---|
49 | uint16_t *relocs; |
---|
50 | }; |
---|
51 | |
---|
52 | struct pe_header { |
---|
53 | EFI_IMAGE_DOS_HEADER dos; |
---|
54 | uint8_t padding[128]; |
---|
55 | #if defined(MDE_CPU_IA32) |
---|
56 | EFI_IMAGE_NT_HEADERS32 nt; |
---|
57 | #elif defined(MDE_CPU_X64) |
---|
58 | EFI_IMAGE_NT_HEADERS64 nt; |
---|
59 | #endif |
---|
60 | }; |
---|
61 | |
---|
62 | static struct pe_header efi_pe_header = { |
---|
63 | .dos = { |
---|
64 | .e_magic = EFI_IMAGE_DOS_SIGNATURE, |
---|
65 | .e_lfanew = offsetof ( typeof ( efi_pe_header ), nt ), |
---|
66 | }, |
---|
67 | .nt = { |
---|
68 | .Signature = EFI_IMAGE_NT_SIGNATURE, |
---|
69 | .FileHeader = { |
---|
70 | #if defined(MDE_CPU_IA32) |
---|
71 | .Machine = EFI_IMAGE_MACHINE_IA32, |
---|
72 | #elif defined(MDE_CPU_X64) |
---|
73 | .Machine = EFI_IMAGE_MACHINE_X64, |
---|
74 | #endif |
---|
75 | .TimeDateStamp = 0x10d1a884, |
---|
76 | .SizeOfOptionalHeader = |
---|
77 | sizeof ( efi_pe_header.nt.OptionalHeader ), |
---|
78 | .Characteristics = ( EFI_IMAGE_FILE_DLL | |
---|
79 | #if defined(MDE_CPU_IA32) |
---|
80 | EFI_IMAGE_FILE_32BIT_MACHINE | |
---|
81 | #endif |
---|
82 | EFI_IMAGE_FILE_EXECUTABLE_IMAGE ), |
---|
83 | }, |
---|
84 | .OptionalHeader = { |
---|
85 | #if defined(MDE_CPU_IA32) |
---|
86 | .Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC, |
---|
87 | #elif defined(MDE_CPU_X64) |
---|
88 | .Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC, |
---|
89 | #endif |
---|
90 | .SectionAlignment = EFI_FILE_ALIGN, |
---|
91 | .FileAlignment = EFI_FILE_ALIGN, |
---|
92 | .SizeOfImage = sizeof ( efi_pe_header ), |
---|
93 | .SizeOfHeaders = sizeof ( efi_pe_header ), |
---|
94 | .NumberOfRvaAndSizes = |
---|
95 | EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, |
---|
96 | }, |
---|
97 | }, |
---|
98 | }; |
---|
99 | |
---|
100 | /** Command-line options */ |
---|
101 | struct options { |
---|
102 | unsigned int subsystem; |
---|
103 | }; |
---|
104 | |
---|
105 | /** |
---|
106 | * Allocate memory |
---|
107 | * |
---|
108 | * @v len Length of memory to allocate |
---|
109 | * @ret ptr Pointer to allocated memory |
---|
110 | */ |
---|
111 | static void * xmalloc ( size_t len ) { |
---|
112 | void *ptr; |
---|
113 | |
---|
114 | ptr = malloc ( len ); |
---|
115 | if ( ! ptr ) { |
---|
116 | eprintf ( "Could not allocate %zd bytes\n", len ); |
---|
117 | exit ( 1 ); |
---|
118 | } |
---|
119 | |
---|
120 | return ptr; |
---|
121 | } |
---|
122 | |
---|
123 | /** |
---|
124 | * Align section within PE file |
---|
125 | * |
---|
126 | * @v offset Unaligned offset |
---|
127 | * @ret aligned_offset Aligned offset |
---|
128 | */ |
---|
129 | static unsigned long efi_file_align ( unsigned long offset ) { |
---|
130 | return ( ( offset + EFI_FILE_ALIGN - 1 ) & ~( EFI_FILE_ALIGN - 1 ) ); |
---|
131 | } |
---|
132 | |
---|
133 | /** |
---|
134 | * Generate entry in PE relocation table |
---|
135 | * |
---|
136 | * @v pe_reltab PE relocation table |
---|
137 | * @v rva RVA |
---|
138 | * @v size Size of relocation entry |
---|
139 | */ |
---|
140 | static void generate_pe_reloc ( struct pe_relocs **pe_reltab, |
---|
141 | unsigned long rva, size_t size ) { |
---|
142 | unsigned long start_rva; |
---|
143 | uint16_t reloc; |
---|
144 | struct pe_relocs *pe_rel; |
---|
145 | uint16_t *relocs; |
---|
146 | |
---|
147 | /* Construct */ |
---|
148 | start_rva = ( rva & ~0xfff ); |
---|
149 | reloc = ( rva & 0xfff ); |
---|
150 | switch ( size ) { |
---|
151 | case 8: |
---|
152 | reloc |= 0xa000; |
---|
153 | break; |
---|
154 | case 4: |
---|
155 | reloc |= 0x3000; |
---|
156 | break; |
---|
157 | case 2: |
---|
158 | reloc |= 0x2000; |
---|
159 | break; |
---|
160 | default: |
---|
161 | eprintf ( "Unsupported relocation size %zd\n", size ); |
---|
162 | exit ( 1 ); |
---|
163 | } |
---|
164 | |
---|
165 | /* Locate or create PE relocation table */ |
---|
166 | for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { |
---|
167 | if ( pe_rel->start_rva == start_rva ) |
---|
168 | break; |
---|
169 | } |
---|
170 | if ( ! pe_rel ) { |
---|
171 | pe_rel = xmalloc ( sizeof ( *pe_rel ) ); |
---|
172 | memset ( pe_rel, 0, sizeof ( *pe_rel ) ); |
---|
173 | pe_rel->next = *pe_reltab; |
---|
174 | *pe_reltab = pe_rel; |
---|
175 | pe_rel->start_rva = start_rva; |
---|
176 | } |
---|
177 | |
---|
178 | /* Expand relocation list if necessary */ |
---|
179 | if ( pe_rel->used_relocs < pe_rel->total_relocs ) { |
---|
180 | relocs = pe_rel->relocs; |
---|
181 | } else { |
---|
182 | pe_rel->total_relocs = ( pe_rel->total_relocs ? |
---|
183 | ( pe_rel->total_relocs * 2 ) : 256 ); |
---|
184 | relocs = xmalloc ( pe_rel->total_relocs * |
---|
185 | sizeof ( pe_rel->relocs[0] ) ); |
---|
186 | memset ( relocs, 0, |
---|
187 | pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) ); |
---|
188 | memcpy ( relocs, pe_rel->relocs, |
---|
189 | pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) ); |
---|
190 | free ( pe_rel->relocs ); |
---|
191 | pe_rel->relocs = relocs; |
---|
192 | } |
---|
193 | |
---|
194 | /* Store relocation */ |
---|
195 | pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc; |
---|
196 | } |
---|
197 | |
---|
198 | /** |
---|
199 | * Calculate size of binary PE relocation table |
---|
200 | * |
---|
201 | * @v pe_reltab PE relocation table |
---|
202 | * @v buffer Buffer to contain binary table, or NULL |
---|
203 | * @ret size Size of binary table |
---|
204 | */ |
---|
205 | static size_t output_pe_reltab ( struct pe_relocs *pe_reltab, |
---|
206 | void *buffer ) { |
---|
207 | struct pe_relocs *pe_rel; |
---|
208 | unsigned int num_relocs; |
---|
209 | size_t size; |
---|
210 | size_t total_size = 0; |
---|
211 | |
---|
212 | for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) { |
---|
213 | num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 ); |
---|
214 | size = ( sizeof ( uint32_t ) /* VirtualAddress */ + |
---|
215 | sizeof ( uint32_t ) /* SizeOfBlock */ + |
---|
216 | ( num_relocs * sizeof ( uint16_t ) ) ); |
---|
217 | if ( buffer ) { |
---|
218 | *( (uint32_t *) ( buffer + total_size + 0 ) ) |
---|
219 | = pe_rel->start_rva; |
---|
220 | *( (uint32_t *) ( buffer + total_size + 4 ) ) = size; |
---|
221 | memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs, |
---|
222 | ( num_relocs * sizeof ( uint16_t ) ) ); |
---|
223 | } |
---|
224 | total_size += size; |
---|
225 | } |
---|
226 | |
---|
227 | return total_size; |
---|
228 | } |
---|
229 | |
---|
230 | /** |
---|
231 | * Open input BFD file |
---|
232 | * |
---|
233 | * @v filename File name |
---|
234 | * @ret ibfd BFD file |
---|
235 | */ |
---|
236 | static bfd * open_input_bfd ( const char *filename ) { |
---|
237 | bfd *bfd; |
---|
238 | |
---|
239 | /* Open the file */ |
---|
240 | bfd = bfd_openr ( filename, NULL ); |
---|
241 | if ( ! bfd ) { |
---|
242 | eprintf ( "Cannot open %s: ", filename ); |
---|
243 | bfd_perror ( NULL ); |
---|
244 | exit ( 1 ); |
---|
245 | } |
---|
246 | |
---|
247 | /* The call to bfd_check_format() must be present, otherwise |
---|
248 | * we get a segfault from later BFD calls. |
---|
249 | */ |
---|
250 | if ( bfd_check_format ( bfd, bfd_object ) < 0 ) { |
---|
251 | eprintf ( "%s is not an object file\n", filename ); |
---|
252 | exit ( 1 ); |
---|
253 | } |
---|
254 | |
---|
255 | return bfd; |
---|
256 | } |
---|
257 | |
---|
258 | /** |
---|
259 | * Read symbol table |
---|
260 | * |
---|
261 | * @v bfd BFD file |
---|
262 | */ |
---|
263 | static asymbol ** read_symtab ( bfd *bfd ) { |
---|
264 | long symtab_size; |
---|
265 | asymbol **symtab; |
---|
266 | long symcount; |
---|
267 | |
---|
268 | /* Get symbol table size */ |
---|
269 | symtab_size = bfd_get_symtab_upper_bound ( bfd ); |
---|
270 | if ( symtab_size < 0 ) { |
---|
271 | bfd_perror ( "Could not get symbol table upper bound" ); |
---|
272 | exit ( 1 ); |
---|
273 | } |
---|
274 | |
---|
275 | /* Allocate and read symbol table */ |
---|
276 | symtab = xmalloc ( symtab_size ); |
---|
277 | symcount = bfd_canonicalize_symtab ( bfd, symtab ); |
---|
278 | if ( symcount < 0 ) { |
---|
279 | bfd_perror ( "Cannot read symbol table" ); |
---|
280 | exit ( 1 ); |
---|
281 | } |
---|
282 | |
---|
283 | return symtab; |
---|
284 | } |
---|
285 | |
---|
286 | /** |
---|
287 | * Read relocation table |
---|
288 | * |
---|
289 | * @v bfd BFD file |
---|
290 | * @v symtab Symbol table |
---|
291 | * @v section Section |
---|
292 | * @v symtab Symbol table |
---|
293 | * @ret reltab Relocation table |
---|
294 | */ |
---|
295 | static arelent ** read_reltab ( bfd *bfd, asymbol **symtab, |
---|
296 | asection *section ) { |
---|
297 | long reltab_size; |
---|
298 | arelent **reltab; |
---|
299 | long numrels; |
---|
300 | |
---|
301 | /* Get relocation table size */ |
---|
302 | reltab_size = bfd_get_reloc_upper_bound ( bfd, section ); |
---|
303 | if ( reltab_size < 0 ) { |
---|
304 | bfd_perror ( "Could not get relocation table upper bound" ); |
---|
305 | exit ( 1 ); |
---|
306 | } |
---|
307 | |
---|
308 | /* Allocate and read relocation table */ |
---|
309 | reltab = xmalloc ( reltab_size ); |
---|
310 | numrels = bfd_canonicalize_reloc ( bfd, section, reltab, symtab ); |
---|
311 | if ( numrels < 0 ) { |
---|
312 | bfd_perror ( "Cannot read relocation table" ); |
---|
313 | exit ( 1 ); |
---|
314 | } |
---|
315 | |
---|
316 | return reltab; |
---|
317 | } |
---|
318 | |
---|
319 | /** |
---|
320 | * Process section |
---|
321 | * |
---|
322 | * @v bfd BFD file |
---|
323 | * @v pe_header PE file header |
---|
324 | * @v section Section |
---|
325 | * @ret new New PE section |
---|
326 | */ |
---|
327 | static struct pe_section * process_section ( bfd *bfd, |
---|
328 | struct pe_header *pe_header, |
---|
329 | asection *section ) { |
---|
330 | struct pe_section *new; |
---|
331 | size_t section_memsz; |
---|
332 | size_t section_filesz; |
---|
333 | unsigned long flags = bfd_get_section_flags ( bfd, section ); |
---|
334 | unsigned long code_start; |
---|
335 | unsigned long code_end; |
---|
336 | unsigned long data_start; |
---|
337 | unsigned long data_mid; |
---|
338 | unsigned long data_end; |
---|
339 | unsigned long start; |
---|
340 | unsigned long end; |
---|
341 | unsigned long *applicable_start; |
---|
342 | unsigned long *applicable_end; |
---|
343 | |
---|
344 | /* Extract current RVA limits from file header */ |
---|
345 | code_start = pe_header->nt.OptionalHeader.BaseOfCode; |
---|
346 | code_end = ( code_start + pe_header->nt.OptionalHeader.SizeOfCode ); |
---|
347 | #if defined(MDE_CPU_IA32) |
---|
348 | data_start = pe_header->nt.OptionalHeader.BaseOfData; |
---|
349 | #elif defined(MDE_CPU_X64) |
---|
350 | data_start = code_end; |
---|
351 | #endif |
---|
352 | data_mid = ( data_start + |
---|
353 | pe_header->nt.OptionalHeader.SizeOfInitializedData ); |
---|
354 | data_end = ( data_mid + |
---|
355 | pe_header->nt.OptionalHeader.SizeOfUninitializedData ); |
---|
356 | |
---|
357 | /* Allocate PE section */ |
---|
358 | section_memsz = bfd_section_size ( bfd, section ); |
---|
359 | section_filesz = ( ( flags & SEC_LOAD ) ? |
---|
360 | efi_file_align ( section_memsz ) : 0 ); |
---|
361 | new = xmalloc ( sizeof ( *new ) + section_filesz ); |
---|
362 | memset ( new, 0, sizeof ( *new ) + section_filesz ); |
---|
363 | |
---|
364 | /* Fill in section header details */ |
---|
365 | strncpy ( ( char * ) new->hdr.Name, section->name, |
---|
366 | sizeof ( new->hdr.Name ) ); |
---|
367 | new->hdr.Misc.VirtualSize = section_memsz; |
---|
368 | new->hdr.VirtualAddress = bfd_get_section_vma ( bfd, section ); |
---|
369 | new->hdr.SizeOfRawData = section_filesz; |
---|
370 | |
---|
371 | /* Fill in section characteristics and update RVA limits */ |
---|
372 | if ( flags & SEC_CODE ) { |
---|
373 | /* .text-type section */ |
---|
374 | new->hdr.Characteristics = |
---|
375 | ( EFI_IMAGE_SCN_CNT_CODE | |
---|
376 | EFI_IMAGE_SCN_MEM_NOT_PAGED | |
---|
377 | EFI_IMAGE_SCN_MEM_EXECUTE | |
---|
378 | EFI_IMAGE_SCN_MEM_READ ); |
---|
379 | applicable_start = &code_start; |
---|
380 | applicable_end = &code_end; |
---|
381 | } else if ( flags & SEC_DATA ) { |
---|
382 | /* .data-type section */ |
---|
383 | new->hdr.Characteristics = |
---|
384 | ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | |
---|
385 | EFI_IMAGE_SCN_MEM_NOT_PAGED | |
---|
386 | EFI_IMAGE_SCN_MEM_READ | |
---|
387 | EFI_IMAGE_SCN_MEM_WRITE ); |
---|
388 | applicable_start = &data_start; |
---|
389 | applicable_end = &data_mid; |
---|
390 | } else if ( flags & SEC_READONLY ) { |
---|
391 | /* .rodata-type section */ |
---|
392 | new->hdr.Characteristics = |
---|
393 | ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | |
---|
394 | EFI_IMAGE_SCN_MEM_NOT_PAGED | |
---|
395 | EFI_IMAGE_SCN_MEM_READ ); |
---|
396 | applicable_start = &data_start; |
---|
397 | applicable_end = &data_mid; |
---|
398 | } else if ( ! ( flags & SEC_LOAD ) ) { |
---|
399 | /* .bss-type section */ |
---|
400 | new->hdr.Characteristics = |
---|
401 | ( EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA | |
---|
402 | EFI_IMAGE_SCN_MEM_NOT_PAGED | |
---|
403 | EFI_IMAGE_SCN_MEM_READ | |
---|
404 | EFI_IMAGE_SCN_MEM_WRITE ); |
---|
405 | applicable_start = &data_mid; |
---|
406 | applicable_end = &data_end; |
---|
407 | } |
---|
408 | |
---|
409 | /* Copy in section contents */ |
---|
410 | if ( flags & SEC_LOAD ) { |
---|
411 | if ( ! bfd_get_section_contents ( bfd, section, new->contents, |
---|
412 | 0, section_memsz ) ) { |
---|
413 | eprintf ( "Cannot read section %s: ", section->name ); |
---|
414 | bfd_perror ( NULL ); |
---|
415 | exit ( 1 ); |
---|
416 | } |
---|
417 | } |
---|
418 | |
---|
419 | /* Update RVA limits */ |
---|
420 | start = new->hdr.VirtualAddress; |
---|
421 | end = ( start + new->hdr.Misc.VirtualSize ); |
---|
422 | if ( ( ! *applicable_start ) || ( *applicable_start >= start ) ) |
---|
423 | *applicable_start = start; |
---|
424 | if ( *applicable_end < end ) |
---|
425 | *applicable_end = end; |
---|
426 | if ( data_start < code_end ) |
---|
427 | data_start = code_end; |
---|
428 | if ( data_mid < data_start ) |
---|
429 | data_mid = data_start; |
---|
430 | if ( data_end < data_mid ) |
---|
431 | data_end = data_mid; |
---|
432 | |
---|
433 | /* Write RVA limits back to file header */ |
---|
434 | pe_header->nt.OptionalHeader.BaseOfCode = code_start; |
---|
435 | pe_header->nt.OptionalHeader.SizeOfCode = ( code_end - code_start ); |
---|
436 | #if defined(MDE_CPU_IA32) |
---|
437 | pe_header->nt.OptionalHeader.BaseOfData = data_start; |
---|
438 | #endif |
---|
439 | pe_header->nt.OptionalHeader.SizeOfInitializedData = |
---|
440 | ( data_mid - data_start ); |
---|
441 | pe_header->nt.OptionalHeader.SizeOfUninitializedData = |
---|
442 | ( data_end - data_mid ); |
---|
443 | |
---|
444 | /* Update remaining file header fields */ |
---|
445 | pe_header->nt.FileHeader.NumberOfSections++; |
---|
446 | pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr ); |
---|
447 | pe_header->nt.OptionalHeader.SizeOfImage = |
---|
448 | efi_file_align ( data_end ); |
---|
449 | |
---|
450 | return new; |
---|
451 | } |
---|
452 | |
---|
453 | /** |
---|
454 | * Process relocation record |
---|
455 | * |
---|
456 | * @v bfd BFD file |
---|
457 | * @v section Section |
---|
458 | * @v rel Relocation entry |
---|
459 | * @v pe_reltab PE relocation table to fill in |
---|
460 | */ |
---|
461 | static void process_reloc ( bfd *bfd, asection *section, arelent *rel, |
---|
462 | struct pe_relocs **pe_reltab ) { |
---|
463 | reloc_howto_type *howto = rel->howto; |
---|
464 | asymbol *sym = *(rel->sym_ptr_ptr); |
---|
465 | unsigned long offset = ( bfd_get_section_vma ( bfd, section ) + |
---|
466 | rel->address ); |
---|
467 | |
---|
468 | if ( bfd_is_abs_section ( sym->section ) ) { |
---|
469 | /* Skip absolute symbols; the symbol value won't |
---|
470 | * change when the object is loaded. |
---|
471 | */ |
---|
472 | } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { |
---|
473 | /* Generate an 8-byte PE relocation */ |
---|
474 | generate_pe_reloc ( pe_reltab, offset, 8 ); |
---|
475 | } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) || |
---|
476 | ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) { |
---|
477 | /* Generate a 4-byte PE relocation */ |
---|
478 | generate_pe_reloc ( pe_reltab, offset, 4 ); |
---|
479 | } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { |
---|
480 | /* Generate a 2-byte PE relocation */ |
---|
481 | generate_pe_reloc ( pe_reltab, offset, 2 ); |
---|
482 | } else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) || |
---|
483 | ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) { |
---|
484 | /* Skip PC-relative relocations; all relative offsets |
---|
485 | * remain unaltered when the object is loaded. |
---|
486 | */ |
---|
487 | } else { |
---|
488 | eprintf ( "Unrecognised relocation type %s\n", howto->name ); |
---|
489 | exit ( 1 ); |
---|
490 | } |
---|
491 | } |
---|
492 | |
---|
493 | /** |
---|
494 | * Create relocations section |
---|
495 | * |
---|
496 | * @v pe_header PE file header |
---|
497 | * @v pe_reltab PE relocation table |
---|
498 | * @ret section Relocation section |
---|
499 | */ |
---|
500 | static struct pe_section * |
---|
501 | create_reloc_section ( struct pe_header *pe_header, |
---|
502 | struct pe_relocs *pe_reltab ) { |
---|
503 | struct pe_section *reloc; |
---|
504 | size_t section_memsz; |
---|
505 | size_t section_filesz; |
---|
506 | EFI_IMAGE_DATA_DIRECTORY *relocdir; |
---|
507 | |
---|
508 | /* Allocate PE section */ |
---|
509 | section_memsz = output_pe_reltab ( pe_reltab, NULL ); |
---|
510 | section_filesz = efi_file_align ( section_memsz ); |
---|
511 | reloc = xmalloc ( sizeof ( *reloc ) + section_filesz ); |
---|
512 | memset ( reloc, 0, sizeof ( *reloc ) + section_filesz ); |
---|
513 | |
---|
514 | /* Fill in section header details */ |
---|
515 | strncpy ( ( char * ) reloc->hdr.Name, ".reloc", |
---|
516 | sizeof ( reloc->hdr.Name ) ); |
---|
517 | reloc->hdr.Misc.VirtualSize = section_memsz; |
---|
518 | reloc->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; |
---|
519 | reloc->hdr.SizeOfRawData = section_filesz; |
---|
520 | reloc->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | |
---|
521 | EFI_IMAGE_SCN_MEM_NOT_PAGED | |
---|
522 | EFI_IMAGE_SCN_MEM_READ ); |
---|
523 | |
---|
524 | /* Copy in section contents */ |
---|
525 | output_pe_reltab ( pe_reltab, reloc->contents ); |
---|
526 | |
---|
527 | /* Update file header details */ |
---|
528 | pe_header->nt.FileHeader.NumberOfSections++; |
---|
529 | pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr ); |
---|
530 | pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; |
---|
531 | relocdir = &(pe_header->nt.OptionalHeader.DataDirectory |
---|
532 | [EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]); |
---|
533 | relocdir->VirtualAddress = reloc->hdr.VirtualAddress; |
---|
534 | relocdir->Size = reloc->hdr.Misc.VirtualSize; |
---|
535 | |
---|
536 | return reloc; |
---|
537 | } |
---|
538 | |
---|
539 | /** |
---|
540 | * Create debug section |
---|
541 | * |
---|
542 | * @v pe_header PE file header |
---|
543 | * @ret section Debug section |
---|
544 | */ |
---|
545 | static struct pe_section * |
---|
546 | create_debug_section ( struct pe_header *pe_header, const char *filename ) { |
---|
547 | struct pe_section *debug; |
---|
548 | size_t section_memsz; |
---|
549 | size_t section_filesz; |
---|
550 | EFI_IMAGE_DATA_DIRECTORY *debugdir; |
---|
551 | struct { |
---|
552 | EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug; |
---|
553 | EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds; |
---|
554 | char name[ strlen ( filename ) + 1 ]; |
---|
555 | } *contents; |
---|
556 | |
---|
557 | /* Allocate PE section */ |
---|
558 | section_memsz = sizeof ( *contents ); |
---|
559 | section_filesz = efi_file_align ( section_memsz ); |
---|
560 | debug = xmalloc ( sizeof ( *debug ) + section_filesz ); |
---|
561 | memset ( debug, 0, sizeof ( *debug ) + section_filesz ); |
---|
562 | contents = ( void * ) debug->contents; |
---|
563 | |
---|
564 | /* Fill in section header details */ |
---|
565 | strncpy ( ( char * ) debug->hdr.Name, ".debug", |
---|
566 | sizeof ( debug->hdr.Name ) ); |
---|
567 | debug->hdr.Misc.VirtualSize = section_memsz; |
---|
568 | debug->hdr.VirtualAddress = pe_header->nt.OptionalHeader.SizeOfImage; |
---|
569 | debug->hdr.SizeOfRawData = section_filesz; |
---|
570 | debug->hdr.Characteristics = ( EFI_IMAGE_SCN_CNT_INITIALIZED_DATA | |
---|
571 | EFI_IMAGE_SCN_MEM_NOT_PAGED | |
---|
572 | EFI_IMAGE_SCN_MEM_READ ); |
---|
573 | |
---|
574 | /* Create section contents */ |
---|
575 | contents->debug.TimeDateStamp = 0x10d1a884; |
---|
576 | contents->debug.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW; |
---|
577 | contents->debug.SizeOfData = |
---|
578 | ( sizeof ( *contents ) - sizeof ( contents->debug ) ); |
---|
579 | contents->debug.RVA = ( debug->hdr.VirtualAddress + |
---|
580 | offsetof ( typeof ( *contents ), rsds ) ); |
---|
581 | contents->rsds.Signature = CODEVIEW_SIGNATURE_RSDS; |
---|
582 | snprintf ( contents->name, sizeof ( contents->name ), "%s", |
---|
583 | filename ); |
---|
584 | |
---|
585 | /* Update file header details */ |
---|
586 | pe_header->nt.FileHeader.NumberOfSections++; |
---|
587 | pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( debug->hdr ); |
---|
588 | pe_header->nt.OptionalHeader.SizeOfImage += section_filesz; |
---|
589 | debugdir = &(pe_header->nt.OptionalHeader.DataDirectory |
---|
590 | [EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); |
---|
591 | debugdir->VirtualAddress = debug->hdr.VirtualAddress; |
---|
592 | debugdir->Size = debug->hdr.Misc.VirtualSize; |
---|
593 | |
---|
594 | return debug; |
---|
595 | } |
---|
596 | |
---|
597 | /** |
---|
598 | * Write out PE file |
---|
599 | * |
---|
600 | * @v pe_header PE file header |
---|
601 | * @v pe_sections List of PE sections |
---|
602 | * @v pe Output file |
---|
603 | */ |
---|
604 | static void write_pe_file ( struct pe_header *pe_header, |
---|
605 | struct pe_section *pe_sections, |
---|
606 | FILE *pe ) { |
---|
607 | struct pe_section *section; |
---|
608 | unsigned long fpos = 0; |
---|
609 | |
---|
610 | /* Assign raw data pointers */ |
---|
611 | fpos = efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders ); |
---|
612 | for ( section = pe_sections ; section ; section = section->next ) { |
---|
613 | if ( section->hdr.SizeOfRawData ) { |
---|
614 | section->hdr.PointerToRawData = fpos; |
---|
615 | fpos += section->hdr.SizeOfRawData; |
---|
616 | fpos = efi_file_align ( fpos ); |
---|
617 | } |
---|
618 | } |
---|
619 | |
---|
620 | /* Write file header */ |
---|
621 | if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 1 ) { |
---|
622 | perror ( "Could not write PE header" ); |
---|
623 | exit ( 1 ); |
---|
624 | } |
---|
625 | |
---|
626 | /* Write section headers */ |
---|
627 | for ( section = pe_sections ; section ; section = section->next ) { |
---|
628 | if ( fwrite ( §ion->hdr, sizeof ( section->hdr ), |
---|
629 | 1, pe ) != 1 ) { |
---|
630 | perror ( "Could not write section header" ); |
---|
631 | exit ( 1 ); |
---|
632 | } |
---|
633 | } |
---|
634 | |
---|
635 | /* Write sections */ |
---|
636 | for ( section = pe_sections ; section ; section = section->next ) { |
---|
637 | if ( fseek ( pe, section->hdr.PointerToRawData, |
---|
638 | SEEK_SET ) != 0 ) { |
---|
639 | eprintf ( "Could not seek to %lx: %s\n", |
---|
640 | section->hdr.PointerToRawData, |
---|
641 | strerror ( errno ) ); |
---|
642 | exit ( 1 ); |
---|
643 | } |
---|
644 | if ( section->hdr.SizeOfRawData && |
---|
645 | ( fwrite ( section->contents, section->hdr.SizeOfRawData, |
---|
646 | 1, pe ) != 1 ) ) { |
---|
647 | eprintf ( "Could not write section %.8s: %s\n", |
---|
648 | section->hdr.Name, strerror ( errno ) ); |
---|
649 | exit ( 1 ); |
---|
650 | } |
---|
651 | } |
---|
652 | } |
---|
653 | |
---|
654 | /** |
---|
655 | * Convert ELF to PE |
---|
656 | * |
---|
657 | * @v elf_name ELF file name |
---|
658 | * @v pe_name PE file name |
---|
659 | */ |
---|
660 | static void elf2pe ( const char *elf_name, const char *pe_name, |
---|
661 | struct options *opts ) { |
---|
662 | char pe_name_tmp[ strlen ( pe_name ) + 1 ]; |
---|
663 | bfd *bfd; |
---|
664 | asymbol **symtab; |
---|
665 | asection *section; |
---|
666 | arelent **reltab; |
---|
667 | arelent **rel; |
---|
668 | struct pe_relocs *pe_reltab = NULL; |
---|
669 | struct pe_section *pe_sections = NULL; |
---|
670 | struct pe_section **next_pe_section = &pe_sections; |
---|
671 | struct pe_header pe_header; |
---|
672 | FILE *pe; |
---|
673 | |
---|
674 | /* Create a modifiable copy of the PE name */ |
---|
675 | memcpy ( pe_name_tmp, pe_name, sizeof ( pe_name_tmp ) ); |
---|
676 | |
---|
677 | /* Open the file */ |
---|
678 | bfd = open_input_bfd ( elf_name ); |
---|
679 | symtab = read_symtab ( bfd ); |
---|
680 | |
---|
681 | /* Initialise the PE header */ |
---|
682 | memcpy ( &pe_header, &efi_pe_header, sizeof ( pe_header ) ); |
---|
683 | pe_header.nt.OptionalHeader.AddressOfEntryPoint = |
---|
684 | bfd_get_start_address ( bfd ); |
---|
685 | pe_header.nt.OptionalHeader.Subsystem = opts->subsystem; |
---|
686 | |
---|
687 | /* For each input section, build an output section and create |
---|
688 | * the appropriate relocation records |
---|
689 | */ |
---|
690 | for ( section = bfd->sections ; section ; section = section->next ) { |
---|
691 | /* Discard non-allocatable sections */ |
---|
692 | if ( ! ( bfd_get_section_flags ( bfd, section ) & SEC_ALLOC ) ) |
---|
693 | continue; |
---|
694 | /* Create output section */ |
---|
695 | *(next_pe_section) = process_section ( bfd, &pe_header, |
---|
696 | section ); |
---|
697 | next_pe_section = &(*next_pe_section)->next; |
---|
698 | /* Add relocations from this section */ |
---|
699 | reltab = read_reltab ( bfd, symtab, section ); |
---|
700 | for ( rel = reltab ; *rel ; rel++ ) |
---|
701 | process_reloc ( bfd, section, *rel, &pe_reltab ); |
---|
702 | free ( reltab ); |
---|
703 | } |
---|
704 | |
---|
705 | /* Create the .reloc section */ |
---|
706 | *(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab ); |
---|
707 | next_pe_section = &(*next_pe_section)->next; |
---|
708 | |
---|
709 | /* Create the .reloc section */ |
---|
710 | *(next_pe_section) = create_debug_section ( &pe_header, |
---|
711 | basename ( pe_name_tmp ) ); |
---|
712 | next_pe_section = &(*next_pe_section)->next; |
---|
713 | |
---|
714 | /* Write out PE file */ |
---|
715 | pe = fopen ( pe_name, "w" ); |
---|
716 | if ( ! pe ) { |
---|
717 | eprintf ( "Could not open %s for writing: %s\n", |
---|
718 | pe_name, strerror ( errno ) ); |
---|
719 | exit ( 1 ); |
---|
720 | } |
---|
721 | write_pe_file ( &pe_header, pe_sections, pe ); |
---|
722 | fclose ( pe ); |
---|
723 | |
---|
724 | /* Close BFD file */ |
---|
725 | bfd_close ( bfd ); |
---|
726 | } |
---|
727 | |
---|
728 | /** |
---|
729 | * Print help |
---|
730 | * |
---|
731 | * @v program_name Program name |
---|
732 | */ |
---|
733 | static void print_help ( const char *program_name ) { |
---|
734 | eprintf ( "Syntax: %s [--subsystem=<number>] infile outfile\n", |
---|
735 | program_name ); |
---|
736 | } |
---|
737 | |
---|
738 | /** |
---|
739 | * Parse command-line options |
---|
740 | * |
---|
741 | * @v argc Argument count |
---|
742 | * @v argv Argument list |
---|
743 | * @v opts Options structure to populate |
---|
744 | */ |
---|
745 | static int parse_options ( const int argc, char **argv, |
---|
746 | struct options *opts ) { |
---|
747 | char *end; |
---|
748 | int c; |
---|
749 | |
---|
750 | while (1) { |
---|
751 | int option_index = 0; |
---|
752 | static struct option long_options[] = { |
---|
753 | { "subsystem", required_argument, NULL, 's' }, |
---|
754 | { "help", 0, NULL, 'h' }, |
---|
755 | { 0, 0, 0, 0 } |
---|
756 | }; |
---|
757 | |
---|
758 | if ( ( c = getopt_long ( argc, argv, "s:h", |
---|
759 | long_options, |
---|
760 | &option_index ) ) == -1 ) { |
---|
761 | break; |
---|
762 | } |
---|
763 | |
---|
764 | switch ( c ) { |
---|
765 | case 's': |
---|
766 | opts->subsystem = strtoul ( optarg, &end, 0 ); |
---|
767 | if ( *end ) { |
---|
768 | eprintf ( "Invalid subsytem \"%s\"\n", |
---|
769 | optarg ); |
---|
770 | exit ( 2 ); |
---|
771 | } |
---|
772 | break; |
---|
773 | case 'h': |
---|
774 | print_help ( argv[0] ); |
---|
775 | exit ( 0 ); |
---|
776 | case '?': |
---|
777 | default: |
---|
778 | exit ( 2 ); |
---|
779 | } |
---|
780 | } |
---|
781 | return optind; |
---|
782 | } |
---|
783 | |
---|
784 | int main ( int argc, char **argv ) { |
---|
785 | struct options opts = { |
---|
786 | .subsystem = EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION, |
---|
787 | }; |
---|
788 | unsigned int infile_index; |
---|
789 | const char *infile; |
---|
790 | const char *outfile; |
---|
791 | |
---|
792 | /* Initialise libbfd */ |
---|
793 | bfd_init(); |
---|
794 | |
---|
795 | /* Parse command-line arguments */ |
---|
796 | infile_index = parse_options ( argc, argv, &opts ); |
---|
797 | if ( argc != ( infile_index + 2 ) ) { |
---|
798 | print_help ( argv[0] ); |
---|
799 | exit ( 2 ); |
---|
800 | } |
---|
801 | infile = argv[infile_index]; |
---|
802 | outfile = argv[infile_index + 1]; |
---|
803 | |
---|
804 | /* Convert file */ |
---|
805 | elf2pe ( infile, outfile, &opts ); |
---|
806 | |
---|
807 | return 0; |
---|
808 | } |
---|