You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

efilink.c 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. #include <stdint.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <bfd.h>
  8. struct bfd_file {
  9. bfd *bfd;
  10. asymbol **symtab;
  11. long symcount;
  12. };
  13. struct pe_relocs {
  14. struct pe_relocs *next;
  15. unsigned long start_rva;
  16. unsigned int used_relocs;
  17. unsigned int total_relocs;
  18. uint16_t *relocs;
  19. };
  20. /**
  21. * Allocate memory
  22. *
  23. * @v len Length of memory to allocate
  24. * @ret ptr Pointer to allocated memory
  25. */
  26. static void * xmalloc ( size_t len ) {
  27. void *ptr;
  28. ptr = malloc ( len );
  29. if ( ! ptr ) {
  30. fprintf ( stderr, "Could not allocate %zd bytes\n", len );
  31. exit ( 1 );
  32. }
  33. return ptr;
  34. }
  35. /**
  36. * Generate entry in PE relocation table
  37. *
  38. * @v pe_reltab PE relocation table
  39. * @v rva RVA
  40. * @v size Size of relocation entry
  41. */
  42. static void generate_pe_reloc ( struct pe_relocs **pe_reltab,
  43. unsigned long rva, size_t size ) {
  44. unsigned long start_rva;
  45. uint16_t reloc;
  46. struct pe_relocs *pe_rel;
  47. uint16_t *relocs;
  48. /* Construct */
  49. start_rva = ( rva & ~0xfff );
  50. reloc = ( rva & 0xfff );
  51. switch ( size ) {
  52. case 8:
  53. reloc |= 0xa000;
  54. break;
  55. case 4:
  56. reloc |= 0x3000;
  57. break;
  58. case 2:
  59. reloc |= 0x2000;
  60. break;
  61. default:
  62. fprintf ( stderr, "Unsupported relocation size %zd\n", size );
  63. exit ( 1 );
  64. }
  65. /* Locate or create PE relocation table */
  66. for ( pe_rel = *pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
  67. if ( pe_rel->start_rva == start_rva )
  68. break;
  69. }
  70. if ( ! pe_rel ) {
  71. pe_rel = xmalloc ( sizeof ( *pe_rel ) );
  72. memset ( pe_rel, 0, sizeof ( *pe_rel ) );
  73. pe_rel->next = *pe_reltab;
  74. *pe_reltab = pe_rel;
  75. pe_rel->start_rva = start_rva;
  76. }
  77. /* Expand relocation list if necessary */
  78. if ( pe_rel->used_relocs < pe_rel->total_relocs ) {
  79. relocs = pe_rel->relocs;
  80. } else {
  81. pe_rel->total_relocs = ( pe_rel->total_relocs ?
  82. ( pe_rel->total_relocs * 2 ) : 256 );
  83. relocs = xmalloc ( pe_rel->total_relocs *
  84. sizeof ( pe_rel->relocs[0] ) );
  85. memset ( relocs, 0,
  86. pe_rel->total_relocs * sizeof ( pe_rel->relocs[0] ) );
  87. memcpy ( relocs, pe_rel->relocs,
  88. pe_rel->used_relocs * sizeof ( pe_rel->relocs[0] ) );
  89. free ( pe_rel->relocs );
  90. pe_rel->relocs = relocs;
  91. }
  92. /* Store relocation */
  93. pe_rel->relocs[ pe_rel->used_relocs++ ] = reloc;
  94. }
  95. /**
  96. * Calculate size of binary PE relocation table
  97. *
  98. * @v pe_reltab PE relocation table
  99. * @v buffer Buffer to contain binary table, or NULL
  100. * @ret size Size of binary table
  101. */
  102. static size_t output_pe_reltab ( struct pe_relocs *pe_reltab,
  103. void *buffer ) {
  104. struct pe_relocs *pe_rel;
  105. unsigned int num_relocs;
  106. size_t size;
  107. size_t total_size = 0;
  108. for ( pe_rel = pe_reltab ; pe_rel ; pe_rel = pe_rel->next ) {
  109. num_relocs = ( ( pe_rel->used_relocs + 1 ) & ~1 );
  110. size = ( sizeof ( uint32_t ) /* VirtualAddress */ +
  111. sizeof ( uint32_t ) /* SizeOfBlock */ +
  112. ( num_relocs * sizeof ( uint16_t ) ) );
  113. if ( buffer ) {
  114. *( (uint32_t *) ( buffer + total_size + 0 ) )
  115. = pe_rel->start_rva;
  116. *( (uint32_t *) ( buffer + total_size + 4 ) ) = size;
  117. memcpy ( ( buffer + total_size + 8 ), pe_rel->relocs,
  118. ( num_relocs * sizeof ( uint16_t ) ) );
  119. }
  120. total_size += size;
  121. }
  122. return total_size;
  123. }
  124. /**
  125. * Read symbol table
  126. *
  127. * @v bfd BFD file
  128. */
  129. static void read_symtab ( struct bfd_file *bfd ) {
  130. long symtab_size;
  131. /* Get symbol table size */
  132. symtab_size = bfd_get_symtab_upper_bound ( bfd->bfd );
  133. if ( symtab_size < 0 ) {
  134. bfd_perror ( "Could not get symbol table upper bound" );
  135. exit ( 1 );
  136. }
  137. /* Allocate and read symbol table */
  138. bfd->symtab = xmalloc ( symtab_size );
  139. bfd->symcount = bfd_canonicalize_symtab ( bfd->bfd, bfd->symtab );
  140. if ( bfd->symcount < 0 ) {
  141. bfd_perror ( "Cannot read symbol table" );
  142. exit ( 1 );
  143. }
  144. }
  145. /**
  146. * Read relocation table
  147. *
  148. * @v bfd BFD file
  149. * @v section Section
  150. * @v symtab Symbol table
  151. * @ret reltab Relocation table
  152. */
  153. static arelent ** read_reltab ( struct bfd_file *bfd, asection *section ) {
  154. long reltab_size;
  155. arelent **reltab;
  156. long numrels;
  157. /* Get relocation table size */
  158. reltab_size = bfd_get_reloc_upper_bound ( bfd->bfd, section );
  159. if ( reltab_size < 0 ) {
  160. bfd_perror ( "Could not get relocation table upper bound" );
  161. exit ( 1 );
  162. }
  163. /* Allocate and read relocation table */
  164. reltab = xmalloc ( reltab_size );
  165. numrels = bfd_canonicalize_reloc ( bfd->bfd, section, reltab,
  166. bfd->symtab );
  167. if ( numrels < 0 ) {
  168. bfd_perror ( "Cannot read relocation table" );
  169. exit ( 1 );
  170. }
  171. return reltab;
  172. }
  173. /**
  174. * Open input BFD file
  175. *
  176. * @v filename File name
  177. * @ret ibfd BFD file
  178. */
  179. static struct bfd_file * open_input_bfd ( const char *filename ) {
  180. struct bfd_file *ibfd;
  181. /* Create BFD file */
  182. ibfd = xmalloc ( sizeof ( *ibfd ) );
  183. memset ( ibfd, 0, sizeof ( *ibfd ) );
  184. /* Open the file */
  185. ibfd->bfd = bfd_openr ( filename, NULL );
  186. if ( ! ibfd->bfd ) {
  187. fprintf ( stderr, "Cannot open %s: ", filename );
  188. bfd_perror ( NULL );
  189. exit ( 1 );
  190. }
  191. /* The call to bfd_check_format() must be present, otherwise
  192. * we get a segfault from later BFD calls.
  193. */
  194. if ( bfd_check_format ( ibfd->bfd, bfd_object ) < 0 ) {
  195. fprintf ( stderr, "%s is not an object file\n", filename );
  196. exit ( 1 );
  197. }
  198. /* Read symbols and relocation entries */
  199. read_symtab ( ibfd );
  200. return ibfd;
  201. }
  202. /**
  203. * Open output BFD file
  204. *
  205. * @v filename File name
  206. * @v ibfd Input BFD file
  207. * @ret obfd BFD file
  208. */
  209. static struct bfd_file * open_output_bfd ( const char *filename,
  210. struct bfd_file *ibfd ) {
  211. struct bfd_file *obfd;
  212. asection *isection;
  213. asection *osection;
  214. /*
  215. * Most of this code is based on what objcopy.c does.
  216. *
  217. */
  218. /* Create BFD file */
  219. obfd = xmalloc ( sizeof ( *obfd ) );
  220. memset ( obfd, 0, sizeof ( *obfd ) );
  221. /* Open the file */
  222. obfd->bfd = bfd_openw ( filename, ibfd->bfd->xvec->name );
  223. if ( ! obfd->bfd ) {
  224. fprintf ( stderr, "Cannot open %s: ", filename );
  225. bfd_perror ( NULL );
  226. exit ( 1 );
  227. }
  228. /* Copy per-file data */
  229. if ( ! bfd_set_arch_mach ( obfd->bfd, bfd_get_arch ( ibfd->bfd ),
  230. bfd_get_mach ( ibfd->bfd ) ) ) {
  231. bfd_perror ( "Cannot copy architecture" );
  232. exit ( 1 );
  233. }
  234. if ( ! bfd_set_format ( obfd->bfd, bfd_get_format ( ibfd->bfd ) ) ) {
  235. bfd_perror ( "Cannot copy format" );
  236. exit ( 1 );
  237. }
  238. if ( ! bfd_copy_private_header_data ( ibfd->bfd, obfd->bfd ) ) {
  239. bfd_perror ( "Cannot copy private header data" );
  240. exit ( 1 );
  241. }
  242. /* Create sections */
  243. for ( isection = ibfd->bfd->sections ; isection ;
  244. isection = isection->next ) {
  245. osection = bfd_make_section_anyway ( obfd->bfd,
  246. isection->name );
  247. if ( ! osection ) {
  248. bfd_perror ( "Cannot create section" );
  249. exit ( 1 );
  250. }
  251. if ( ! bfd_set_section_flags ( obfd->bfd, osection,
  252. isection->flags ) ) {
  253. bfd_perror ( "Cannot copy section flags" );
  254. exit ( 1 );
  255. }
  256. if ( ! bfd_set_section_size ( obfd->bfd, osection,
  257. bfd_section_size ( ibfd->bfd, isection ) ) ) {
  258. bfd_perror ( "Cannot copy section size" );
  259. exit ( 1 );
  260. }
  261. if ( ! bfd_set_section_vma ( obfd->bfd, osection,
  262. bfd_section_vma ( ibfd->bfd, isection ) ) ) {
  263. bfd_perror ( "Cannot copy section VMA" );
  264. exit ( 1 );
  265. }
  266. osection->lma = bfd_section_lma ( ibfd->bfd, isection );
  267. if ( ! bfd_set_section_alignment ( obfd->bfd, osection,
  268. bfd_section_alignment ( ibfd->bfd, isection ) ) ) {
  269. bfd_perror ( "Cannot copy section alignment" );
  270. exit ( 1 );
  271. }
  272. osection->entsize = isection->entsize;
  273. isection->output_section = osection;
  274. isection->output_offset = 0;
  275. if ( ! bfd_copy_private_section_data ( ibfd->bfd, isection,
  276. obfd->bfd, osection ) ){
  277. bfd_perror ( "Cannot copy section private data" );
  278. exit ( 1 );
  279. }
  280. }
  281. /* Copy symbol table */
  282. bfd_set_symtab ( obfd->bfd, ibfd->symtab, ibfd->symcount );
  283. obfd->symtab = ibfd->symtab;
  284. return obfd;
  285. }
  286. /**
  287. * Copy section from input BFD file to output BFD file
  288. *
  289. * @v obfd Output BFD file
  290. * @v ibfd Input BFD file
  291. * @v section Section
  292. */
  293. static void copy_bfd_section ( struct bfd_file *obfd, struct bfd_file *ibfd,
  294. asection *isection ) {
  295. size_t size;
  296. void *buf;
  297. arelent **reltab;
  298. arelent **rel;
  299. char *errmsg;
  300. /* Read in original section */
  301. size = bfd_section_size ( ibfd->bfd, isection );
  302. if ( ! size )
  303. return;
  304. buf = xmalloc ( size );
  305. if ( ( ! bfd_get_section_contents ( ibfd->bfd, isection,
  306. buf, 0, size ) ) ) {
  307. fprintf ( stderr, "Cannot read section %s: ", isection->name );
  308. bfd_perror ( NULL );
  309. exit ( 1 );
  310. }
  311. /* Perform relocations. We do this here, rather than letting
  312. * ld do it for us when creating the input ELF file, so that
  313. * we can change symbol values as a result of having created
  314. * the .reloc section.
  315. */
  316. reltab = read_reltab ( ibfd, isection );
  317. for ( rel = reltab ; *rel ; rel++ ) {
  318. bfd_perform_relocation ( ibfd->bfd, *rel, buf, isection,
  319. NULL, &errmsg );
  320. }
  321. free ( reltab );
  322. /* Write out modified section */
  323. if ( ( ! bfd_set_section_contents ( obfd->bfd,
  324. isection->output_section,
  325. buf, 0, size ) ) ) {
  326. fprintf ( stderr, "Cannot write section %s: ",
  327. isection->output_section->name );
  328. bfd_perror ( NULL );
  329. exit ( 1 );
  330. }
  331. free ( buf );
  332. }
  333. /**
  334. * Process relocation record
  335. *
  336. * @v section Section
  337. * @v rel Relocation entry
  338. * @v pe_reltab PE relocation table to fill in
  339. */
  340. static void process_reloc ( asection *section, arelent *rel,
  341. struct pe_relocs **pe_reltab ) {
  342. reloc_howto_type *howto = rel->howto;
  343. asymbol *sym = *(rel->sym_ptr_ptr);
  344. unsigned long offset = ( section->lma + rel->address );
  345. if ( bfd_is_abs_section ( sym->section ) ) {
  346. /* Skip absolute symbols; the symbol value won't
  347. * change when the object is loaded.
  348. */
  349. } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) {
  350. /* Generate an 8-byte PE relocation */
  351. generate_pe_reloc ( pe_reltab, offset, 8 );
  352. } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) ||
  353. ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) {
  354. /* Generate a 4-byte PE relocation */
  355. generate_pe_reloc ( pe_reltab, offset, 4 );
  356. } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) {
  357. /* Generate a 2-byte PE relocation */
  358. generate_pe_reloc ( pe_reltab, offset, 2 );
  359. } else if ( ( strcmp ( howto->name, "R_386_PC32" ) == 0 ) ||
  360. ( strcmp ( howto->name, "R_X86_64_PC32" ) == 0 ) ) {
  361. /* Skip PC-relative relocations; all relative offsets
  362. * remain unaltered when the object is loaded.
  363. */
  364. } else {
  365. fprintf ( stderr, "Unrecognised relocation type %s\n",
  366. howto->name );
  367. exit ( 1 );
  368. }
  369. }
  370. /**
  371. * Create .reloc section
  372. *
  373. * obfd Output BFD file
  374. * section .reloc section in output file
  375. * pe_reltab PE relocation table
  376. */
  377. static void create_reloc_section ( struct bfd_file *obfd, asection *section,
  378. struct pe_relocs *pe_reltab ) {
  379. size_t raw_size;
  380. size_t size;
  381. size_t old_size;
  382. void *buf;
  383. asymbol **sym;
  384. /* Build binary PE relocation table */
  385. raw_size = output_pe_reltab ( pe_reltab, NULL );
  386. size = ( ( raw_size + 31 ) & ~31 );
  387. buf = xmalloc ( size );
  388. memset ( buf, 0, size );
  389. output_pe_reltab ( pe_reltab, buf );
  390. /* Write .reloc section */
  391. old_size = bfd_section_size ( obfd->bfd, section );
  392. if ( ! bfd_set_section_size ( obfd->bfd, section, size ) ) {
  393. bfd_perror ( "Cannot resize .reloc section" );
  394. exit ( 1 );
  395. }
  396. if ( ! bfd_set_section_contents ( obfd->bfd, section,
  397. buf, 0, size ) ) {
  398. bfd_perror ( "Cannot set .reloc section contents" );
  399. exit ( 1 );
  400. }
  401. /* Update symbols pertaining to the relocation directory */
  402. for ( sym = obfd->symtab ; *sym ; sym++ ) {
  403. if ( strcmp ( (*sym)->name, "_reloc_memsz" ) == 0 ) {
  404. (*sym)->value = size;
  405. } else if ( strcmp ( (*sym)->name, "_reloc_filesz" ) == 0 ){
  406. (*sym)->value = raw_size;
  407. } else if ( strcmp ( (*sym)->name, "_filesz" ) == 0 ) {
  408. (*sym)->value += ( size - old_size );
  409. }
  410. }
  411. }
  412. int main ( int argc, const char *argv[] ) {
  413. const char *iname;
  414. const char *oname;
  415. struct bfd_file *ibfd;
  416. struct bfd_file *obfd;
  417. asection *section;
  418. arelent **reltab;
  419. arelent **rel;
  420. struct pe_relocs *pe_reltab = NULL;
  421. asection *reloc_section;
  422. /* Initialise libbfd */
  423. bfd_init();
  424. /* Identify intput and output files */
  425. if ( argc != 3 ) {
  426. fprintf ( stderr, "Syntax: %s infile outfile\n", argv[0] );
  427. exit ( 1 );
  428. }
  429. iname = argv[1];
  430. oname = argv[2];
  431. /* Open BFD files */
  432. ibfd = open_input_bfd ( iname );
  433. obfd = open_output_bfd ( oname, ibfd );
  434. /* Process relocations in all sections */
  435. for ( section = ibfd->bfd->sections ; section ;
  436. section = section->next ) {
  437. reltab = read_reltab ( ibfd, section );
  438. for ( rel = reltab ; *rel ; rel++ ) {
  439. process_reloc ( section, *rel, &pe_reltab );
  440. }
  441. free ( reltab );
  442. }
  443. /* Create modified .reloc section */
  444. reloc_section = bfd_get_section_by_name ( obfd->bfd, ".reloc" );
  445. if ( ! reloc_section ) {
  446. fprintf ( stderr, "Cannot find .reloc section\n" );
  447. exit ( 1 );
  448. }
  449. create_reloc_section ( obfd, reloc_section, pe_reltab );
  450. /* Copy other section contents */
  451. for ( section = ibfd->bfd->sections ; section ;
  452. section = section->next ) {
  453. if ( section->output_section != reloc_section )
  454. copy_bfd_section ( obfd, ibfd, section );
  455. }
  456. /* Write out files and clean up */
  457. bfd_close ( obfd->bfd );
  458. bfd_close ( ibfd->bfd );
  459. return 0;
  460. }