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

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