16 struct pe_relocs *next;
17 unsigned long start_rva;
18 unsigned int used_relocs;
19 unsigned int total_relocs;
26 * @v len Length of memory to allocate
27 * @ret ptr Pointer to allocated memory
29 static void * xmalloc ( size_t len ) {
34 fprintf ( stderr, "Could not allocate %zd bytes\n", len );
42 * Generate entry in PE relocation table
44 * @v pe_reltab PE relocation table
46 * @v size Size of relocation entry
48 static void generate_pe_reloc ( struct pe_relocs **pe_reltab,
49 unsigned long rva, size_t size ) {
50 unsigned long start_rva;
52 struct pe_relocs *pe_rel;
56 start_rva = ( rva & ~0xfff );
57 reloc = ( rva & 0xfff );
66 fprintf ( stderr, "Unsupported relocation size %zd\n", size );
70 /* Locate or create PE relocation table */
71 for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
72 if ( pe_rel->start_rva == start_rva )
76 pe_rel = xmalloc ( sizeof ( *pe_rel ) );
77 memset ( pe_rel, 0, sizeof ( *pe_rel ) );
78 pe_rel->next = *pe_reltab;
80 pe_rel->start_rva = start_rva;
83 /* Expand relocation list if necessary */
84 if ( pe_rel->used_relocs < pe_rel->total_relocs ) {
85 relocs = pe_rel->relocs;
87 pe_rel->total_relocs = ( pe_rel->total_relocs ?
88 ( pe_rel->total_relocs * 2 ) : 256 );
89 relocs = xmalloc ( pe_rel->total_relocs *
90 sizeof ( pe_rel->relocs[0] ) );
92 pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) );
93 memcpy ( relocs, pe_rel->relocs,
94 pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) );
95 free ( pe_rel->relocs );
96 pe_rel->relocs = relocs;
99 /* Store relocation */
100 pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc;
104 * Calculate size of binary PE relocation table
106 * @v pe_reltab PE relocation table
107 * @v buffer Buffer to contain binary table, or NULL
108 * @ret size Size of binary table
110 static size_t output_pe_reltab ( struct pe_relocs *pe_reltab,
112 struct pe_relocs *pe_rel;
113 unsigned int num_relocs;
115 size_t total_size = 0;
117 for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
118 num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 );
119 size = ( sizeof ( uint32_t ) /* VirtualAddress */ +
120 sizeof ( uint32_t ) /* SizeOfBlock */ +
121 ( num_relocs * sizeof ( uint16_t ) ) );
123 *( (uint32_t *) ( buffer + total_size + 0 ) )
125 *( (uint32_t *) ( buffer + total_size + 4 ) ) = size;
126 memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs,
127 ( num_relocs * sizeof ( uint16_t ) ) );
140 static void read_symtab ( struct bfd_file *bfd ) {
143 /* Get symbol table size */
144 symtab_size = bfd_get_symtab_upper_bound ( bfd->bfd );
145 if ( symtab_size < 0 ) {
146 bfd_perror ( "Could not get symbol table upper bound" );
150 /* Allocate and read symbol table */
151 bfd->symtab = xmalloc ( symtab_size );
152 bfd->symcount = bfd_canonicalize_symtab ( bfd->bfd, bfd->symtab );
153 if ( bfd->symcount < 0 ) {
154 bfd_perror ( "Cannot read symbol table" );
160 * Read relocation table
164 * @v symtab Symbol table
165 * @ret reltab Relocation table
167 static arelent ** read_reltab ( struct bfd_file *bfd, asection *section ) {
172 /* Get relocation table size */
173 reltab_size = bfd_get_reloc_upper_bound ( bfd->bfd, section );
174 if ( reltab_size < 0 ) {
175 bfd_perror ( "Could not get relocation table upper bound" );
179 /* Allocate and read relocation table */
180 reltab = xmalloc ( reltab_size );
181 numrels = bfd_canonicalize_reloc ( bfd->bfd, section, reltab,
184 bfd_perror ( "Cannot read relocation table" );
193 * Open input BFD file
195 * @v filename File name
198 static struct bfd_file * open_input_bfd ( const char *filename ) {
199 struct bfd_file *ibfd;
201 /* Create BFD file */
202 ibfd = xmalloc ( sizeof ( *ibfd ) );
203 memset ( ibfd, 0, sizeof ( *ibfd ) );
206 ibfd->bfd = bfd_openr ( filename, NULL );
208 fprintf ( stderr, "Cannot open %s: ", filename );
213 /* The call to bfd_check_format() must be present, otherwise
214 * we get a segfault from later BFD calls.
216 if ( bfd_check_format ( ibfd->bfd, bfd_object ) < 0 ) {
217 fprintf ( stderr, "%s is not an object file\n", filename );
221 /* Read symbols and relocation entries */
222 read_symtab ( ibfd );
228 * Open output BFD file
230 * @v filename File name
231 * @v ibfd Input BFD file
234 static struct bfd_file * open_output_bfd ( const char *filename,
235 struct bfd_file *ibfd ) {
236 struct bfd_file *obfd;
241 * Most of this code is based on what objcopy.c does.
245 /* Create BFD file */
246 obfd = xmalloc ( sizeof ( *obfd ) );
247 memset ( obfd, 0, sizeof ( *obfd ) );
250 obfd->bfd = bfd_openw ( filename, ibfd->bfd->xvec->name );
252 fprintf ( stderr, "Cannot open %s: ", filename );
257 /* Copy per-file data */
258 if ( ! bfd_set_arch_mach ( obfd->bfd, bfd_get_arch ( ibfd->bfd ),
259 bfd_get_mach ( ibfd->bfd ) ) ) {
260 bfd_perror ( "Cannot copy architecture" );
263 if ( ! bfd_set_format ( obfd->bfd, bfd_get_format ( ibfd->bfd ) ) ) {
264 bfd_perror ( "Cannot copy format" );
267 if ( ! bfd_copy_private_header_data ( ibfd->bfd, obfd->bfd ) ) {
268 bfd_perror ( "Cannot copy private header data" );
272 /* Create sections */
273 for ( isection = ibfd->bfd->sections ; isection ;
274 isection = isection->next ) {
275 osection = bfd_make_section_anyway ( obfd->bfd,
278 bfd_perror ( "Cannot create section" );
281 if ( ! bfd_set_section_flags ( obfd->bfd, osection,
282 isection->flags ) ) {
283 bfd_perror ( "Cannot copy section flags" );
286 if ( ! bfd_set_section_size ( obfd->bfd, osection,
287 bfd_section_size ( ibfd->bfd, isection ) ) ) {
288 bfd_perror ( "Cannot copy section size" );
291 if ( ! bfd_set_section_vma ( obfd->bfd, osection,
292 bfd_section_vma ( ibfd->bfd, isection ) ) ) {
293 bfd_perror ( "Cannot copy section VMA" );
296 osection->lma = bfd_section_lma ( ibfd->bfd, isection );
297 if ( ! bfd_set_section_alignment ( obfd->bfd, osection,
298 bfd_section_alignment ( ibfd->bfd, isection ) ) ) {
299 bfd_perror ( "Cannot copy section alignment" );
302 osection->entsize = isection->entsize;
303 isection->output_section = osection;
304 isection->output_offset = 0;
305 if ( ! bfd_copy_private_section_data ( ibfd->bfd, isection,
306 obfd->bfd, osection ) ){
307 bfd_perror ( "Cannot copy section private data" );
312 /* Copy symbol table */
313 bfd_set_symtab ( obfd->bfd, ibfd->symtab, ibfd->symcount );
314 obfd->symtab = ibfd->symtab;
320 * Copy section from input BFD file to output BFD file
322 * @v obfd Output BFD file
323 * @v ibfd Input BFD file
326 static void copy_bfd_section ( struct bfd_file *obfd, struct bfd_file *ibfd,
327 asection *isection ) {
334 /* Read in original section */
335 size = bfd_section_size ( ibfd->bfd, isection );
338 buf = xmalloc ( size );
339 if ( ( ! bfd_get_section_contents ( ibfd->bfd, isection,
341 fprintf ( stderr, "Cannot read section %s: ", isection->name );
346 /* Perform relocations. We do this here, rather than letting
347 * ld do it for us when creating the input ELF file, so that
348 * we can change symbol values as a result of having created
349 * the .reloc section.
351 reltab = read_reltab ( ibfd, isection );
352 for ( rel = reltab ; *rel ; rel++ ) {
353 bfd_perform_relocation ( ibfd->bfd, *rel, buf, isection,
358 /* Write out modified section */
359 if ( ( ! bfd_set_section_contents ( obfd->bfd,
360 isection->output_section,
362 fprintf ( stderr, "Cannot write section %s: ",
363 isection->output_section->name );
372 * Process relocation record
375 * @v rel Relocation entry
376 * @v pe_reltab PE relocation table to fill in
378 static void process_reloc ( asection *section, arelent *rel,
379 struct pe_relocs **pe_reltab ) {
380 reloc_howto_type *howto = rel->howto;
381 asymbol *sym = *(rel->sym_ptr_ptr);
382 unsigned long offset = ( section->lma + rel->address );
384 if ( bfd_is_abs_section ( sym->section ) ) {
385 /* Skip absolute symbols; the symbol value won't
386 * change when the object is loaded.
388 } else if ( strcmp ( howto->name, "R_386_32" ) == 0 ) {
389 /* Generate a 4-byte PE relocation */
390 generate_pe_reloc ( pe_reltab, offset, 4 );
391 } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) {
392 /* Generate a 2-byte PE relocation */
393 generate_pe_reloc ( pe_reltab, offset, 2 );
394 } else if ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) {
395 /* Skip PC-relative relocations; all relative offsets
396 * remain unaltered when the object is loaded.
399 fprintf ( stderr, "Unrecognised relocation type %s\n",
406 * Create .reloc section
408 * obfd Output BFD file
409 * section .reloc section in output file
410 * pe_reltab PE relocation table
412 static void create_reloc_section ( struct bfd_file *obfd, asection *section,
413 struct pe_relocs *pe_reltab ) {
420 /* Build binary PE relocation table */
421 raw_size = output_pe_reltab ( pe_reltab, NULL );
422 size = ( ( raw_size + 31 ) & ~31 );
423 buf = xmalloc ( size );
424 memset ( buf, 0, size );
425 output_pe_reltab ( pe_reltab, buf );
427 /* Write .reloc section */
428 old_size = bfd_section_size ( obfd->bfd, section );
429 if ( ! bfd_set_section_size ( obfd->bfd, section, size ) ) {
430 bfd_perror ( "Cannot resize .reloc section" );
433 if ( ! bfd_set_section_contents ( obfd->bfd, section,
435 bfd_perror ( "Cannot set .reloc section contents" );
439 /* Update symbols pertaining to the relocation directory */
440 for ( sym = obfd->symtab ; *sym ; sym++ ) {
441 if ( strcmp ( (*sym)->name, "_reloc_memsz" ) == 0 ) {
442 (*sym)->value = size;
443 } else if ( strcmp ( (*sym)->name, "_reloc_filesz" ) == 0 ){
444 (*sym)->value = raw_size;
445 } else if ( strcmp ( (*sym)->name, "_filesz" ) == 0 ) {
446 (*sym)->value += ( size - old_size );
451 int main ( int argc, const char *argv[] ) {
454 struct bfd_file *ibfd;
455 struct bfd_file *obfd;
459 struct pe_relocs *pe_reltab = NULL;
460 asection *reloc_section;
462 /* Initialise libbfd */
465 /* Identify intput and output files */
467 fprintf ( stderr, "Syntax: %s infile outfile\n", argv[0] );
474 ibfd = open_input_bfd ( iname );
475 obfd = open_output_bfd ( oname, ibfd );
477 /* Process relocations in all sections */
478 for ( section = ibfd->bfd->sections ; section ;
479 section = section->next ) {
480 reltab = read_reltab ( ibfd, section );
481 for ( rel = reltab ; *rel ; rel++ ) {
482 process_reloc ( section, *rel, &pe_reltab );
487 /* Create modified .reloc section */
488 reloc_section = bfd_get_section_by_name ( obfd->bfd, ".reloc" );
489 if ( ! reloc_section ) {
490 fprintf ( stderr, "Cannot find .reloc section\n" );
493 create_reloc_section ( obfd, reloc_section, pe_reltab );
495 /* Copy other section contents */
496 for ( section = ibfd->bfd->sections ; section ;
497 section = section->next ) {
498 if ( section->output_section != reloc_section )
499 copy_bfd_section ( obfd, ibfd, section );
502 /* Write out files and clean up */
503 bfd_close ( obfd->bfd );
504 bfd_close ( ibfd->bfd );