]> git.proxmox.com Git - grub2.git/blame - util/grub-pe2elf.c
* grub-core/loader/i386/bsdXX.c (grub_openbsd_find_ramdisk): Use
[grub2.git] / util / grub-pe2elf.c
CommitLineData
2a8a80e4 1/* grub-pe2elf.c - tool to convert pe image to elf. */
2/*
3 * GRUB -- GRand Unified Bootloader
8a4c07fd 4 * Copyright (C) 2008,2009 Free Software Foundation, Inc.
2a8a80e4 5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
21#include <grub/types.h>
22#include <grub/util/misc.h>
23#include <grub/elf.h>
24#include <grub/efi/pe32.h>
1bab1ae3 25#include <grub/misc.h>
2a8a80e4 26
27#include <stdio.h>
28#include <unistd.h>
29#include <string.h>
30#include <stdlib.h>
31#include <getopt.h>
32
4760f979
CF
33#include "progname.h"
34
10f0117b
VS
35/* Please don't internationalise this file. It's pointless. */
36
2a8a80e4 37static struct option options[] = {
38 {"help", no_argument, 0, 'h'},
39 {"version", no_argument, 0, 'V'},
40 {"verbose", no_argument, 0, 'v'},
41 {0, 0, 0, 0}
42};
43
1bab1ae3 44static void __attribute__ ((noreturn))
2a8a80e4 45usage (int status)
46{
47 if (status)
70a14d3d 48 fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
2a8a80e4 49 else
50 printf ("\
8a4c07fd 51Usage: %s [OPTIONS] input [output]\n\
2a8a80e4 52\n\
53Tool to convert pe image to elf.\n\
54\nOptions:\n\
55 -h, --help display this message and exit\n\
56 -V, --version print version information and exit\n\
57 -v, --verbose print verbose messages\n\
58\n\
8a4c07fd 59Report bugs to <%s>.\n", program_name, PACKAGE_BUGREPORT);
2a8a80e4 60
61 exit (status);
62}
63
64/*
65 * Section layout
66 *
67 * null
68 * .text
69 * .rdata
70 * .data
71 * .bss
72 * .modname
73 * .moddeps
74 * .symtab
75 * .strtab
76 * relocation sections
77 */
78
79#define TEXT_SECTION 1
80#define RDATA_SECTION 2
81#define DATA_SECTION 3
82#define BSS_SECTION 4
83#define MODNAME_SECTION 5
84#define MODDEPS_SECTION 6
2217a143
VS
85#define MODLICENSE_SECTION 7
86#define SYMTAB_SECTION 8
87#define STRTAB_SECTION 9
2a8a80e4 88
2217a143 89#define REL_SECTION 10
c30582ba
VS
90
91/* 10 normal section + up to 4 relocation (.text, .rdata, .data, .symtab). */
2217a143 92#define MAX_SECTIONS 16
2a8a80e4 93
94#define STRTAB_BLOCK 256
95
96static char *strtab;
97static int strtab_max, strtab_len;
98
c30582ba
VS
99static Elf32_Ehdr ehdr;
100static Elf32_Shdr shdr[MAX_SECTIONS];
101static int num_sections;
102static grub_uint32_t offset;
2a8a80e4 103
104static int
2217a143 105insert_string (const char *name)
2a8a80e4 106{
107 int len, result;
108
109 if (*name == '_')
110 name++;
111
112 len = strlen (name);
113 if (strtab_len + len >= strtab_max)
114 {
115 strtab_max += STRTAB_BLOCK;
116 strtab = xrealloc (strtab, strtab_max);
117 }
118
119 strcpy (strtab + strtab_len, name);
120 result = strtab_len;
121 strtab_len += len + 1;
122
123 return result;
124}
125
126static int *
0ae70393 127write_section_data (FILE* fp, const char *name, char *image,
2a8a80e4 128 struct grub_pe32_coff_header *pe_chdr,
129 struct grub_pe32_section_table *pe_shdr)
130{
131 int *section_map;
132 int i;
2217a143
VS
133 char *pe_strtab = (image + pe_chdr->symtab_offset
134 + pe_chdr->num_symbols * sizeof (struct grub_pe32_symbol));
2a8a80e4 135
136 section_map = xmalloc ((pe_chdr->num_sections + 1) * sizeof (int));
137 section_map[0] = 0;
138
139 for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++)
140 {
141 grub_uint32_t idx;
1bab1ae3 142 const char *shname = pe_shdr->name;
2a8a80e4 143
1bab1ae3 144 if (shname[0] == '/' && grub_isdigit (shname[1]))
2217a143
VS
145 {
146 char t[sizeof (pe_shdr->name) + 1];
1bab1ae3 147 memcpy (t, shname, sizeof (pe_shdr->name));
2217a143 148 t[sizeof (pe_shdr->name)] = 0;
1bab1ae3 149 shname = pe_strtab + atoi (t + 1);
2217a143
VS
150 }
151
1bab1ae3 152 if (! strcmp (shname, ".text"))
2a8a80e4 153 {
154 idx = TEXT_SECTION;
155 shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
156 }
1bab1ae3 157 else if (! strcmp (shname, ".rdata"))
2a8a80e4 158 {
159 idx = RDATA_SECTION;
160 shdr[idx].sh_flags = SHF_ALLOC;
161 }
1bab1ae3 162 else if (! strcmp (shname, ".data"))
2a8a80e4 163 {
164 idx = DATA_SECTION;
165 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
166 }
1bab1ae3 167 else if (! strcmp (shname, ".bss"))
2a8a80e4 168 {
169 idx = BSS_SECTION;
170 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
171 }
1bab1ae3 172 else if (! strcmp (shname, ".modname"))
2a8a80e4 173 idx = MODNAME_SECTION;
1bab1ae3 174 else if (! strcmp (shname, ".moddeps"))
2a8a80e4 175 idx = MODDEPS_SECTION;
1bab1ae3 176 else if (strcmp (shname, ".module_license") == 0)
2217a143 177 idx = MODLICENSE_SECTION;
2a8a80e4 178 else
179 {
180 section_map[i + 1] = -1;
181 continue;
182 }
183
184 section_map[i + 1] = idx;
185
186 shdr[idx].sh_type = (idx == BSS_SECTION) ? SHT_NOBITS : SHT_PROGBITS;
187 shdr[idx].sh_size = pe_shdr->raw_data_size;
188 shdr[idx].sh_addralign = 1 << (((pe_shdr->characteristics >>
189 GRUB_PE32_SCN_ALIGN_SHIFT) &
190 GRUB_PE32_SCN_ALIGN_MASK) - 1);
191
192 if (idx != BSS_SECTION)
193 {
194 shdr[idx].sh_offset = offset;
195 grub_util_write_image_at (image + pe_shdr->raw_data_offset,
0ae70393 196 pe_shdr->raw_data_size, offset, fp,
1bab1ae3 197 shname);
2a8a80e4 198
199 offset += pe_shdr->raw_data_size;
200 }
201
202 if (pe_shdr->relocations_offset)
203 {
1bab1ae3 204 char relname[5 + strlen (shname)];
2a8a80e4 205
206 if (num_sections >= MAX_SECTIONS)
70a14d3d 207 grub_util_error ("too many sections");
2a8a80e4 208
1bab1ae3 209 sprintf (relname, ".rel%s", shname);
2a8a80e4 210
2217a143 211 shdr[num_sections].sh_name = insert_string (relname);
2a8a80e4 212 shdr[num_sections].sh_link = i;
213 shdr[num_sections].sh_info = idx;
214
215 shdr[idx].sh_name = shdr[num_sections].sh_name + 4;
216
217 num_sections++;
218 }
219 else
1bab1ae3 220 shdr[idx].sh_name = insert_string (shname);
2a8a80e4 221 }
222
223 return section_map;
224}
225
226static void
0ae70393 227write_reloc_section (FILE* fp, const char *name, char *image,
2a8a80e4 228 struct grub_pe32_coff_header *pe_chdr,
229 struct grub_pe32_section_table *pe_shdr,
230 Elf32_Sym *symtab,
231 int *symtab_map)
232{
233 int i;
234
235 for (i = REL_SECTION; i < num_sections; i++)
236 {
237 struct grub_pe32_section_table *pe_sec;
238 struct grub_pe32_reloc *pe_rel;
239 Elf32_Rel *rel;
240 int num_rels, j, modified;
241
242 pe_sec = pe_shdr + shdr[i].sh_link;
243 pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
244 rel = (Elf32_Rel *) xmalloc (pe_sec->num_relocations * sizeof (Elf32_Rel));
245 num_rels = 0;
246 modified = 0;
247
248 for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
249 {
250 int type;
251 grub_uint32_t ofs, *addr;
252
253 if ((pe_rel->symtab_index >= pe_chdr->num_symbols) ||
254 (symtab_map[pe_rel->symtab_index] == -1))
70a14d3d 255 grub_util_error ("invalid symbol");
2a8a80e4 256
257 if (pe_rel->type == GRUB_PE32_REL_I386_DIR32)
258 type = R_386_32;
259 else if (pe_rel->type == GRUB_PE32_REL_I386_REL32)
260 type = R_386_PC32;
261 else
70a14d3d 262 grub_util_error ("unknown pe relocation type %d\n", pe_rel->type);
2a8a80e4 263
264 ofs = pe_rel->offset - pe_sec->virtual_address;
265 addr = (grub_uint32_t *)(image + pe_sec->raw_data_offset + ofs);
266 if (type == R_386_PC32)
267 {
268 unsigned char code;
269
270 code = image[pe_sec->raw_data_offset + ofs - 1];
271
272 if (((code != 0xe8) && (code != 0xe9)) || (*addr))
70a14d3d 273 grub_util_error ("invalid relocation (%x %x)", code, *addr);
2a8a80e4 274
275 modified = 1;
276 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx)
277 {
278 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx
279 != shdr[i].sh_info)
70a14d3d 280 grub_util_error ("cross section call is not allowed");
2a8a80e4 281
282 *addr = (symtab[symtab_map[pe_rel->symtab_index]].st_value
283 - ofs - 4);
284
285 continue;
286 }
287 else
288 *addr = -4;
289 }
290
291 rel[num_rels].r_offset = ofs;
292 rel[num_rels].r_info = ELF32_R_INFO (symtab_map[pe_rel->symtab_index],
293 type);
294 num_rels++;
295 }
296
297 if (modified)
298 grub_util_write_image_at (image + pe_sec->raw_data_offset,
299 shdr[shdr[i].sh_info].sh_size,
300 shdr[shdr[i].sh_info].sh_offset,
0ae70393 301 fp, name);
2a8a80e4 302
303 shdr[i].sh_type = SHT_REL;
304 shdr[i].sh_offset = offset;
305 shdr[i].sh_link = SYMTAB_SECTION;
306 shdr[i].sh_addralign = 4;
307 shdr[i].sh_entsize = sizeof (Elf32_Rel);
308 shdr[i].sh_size = num_rels * sizeof (Elf32_Rel);
309
0ae70393 310 grub_util_write_image_at (rel, shdr[i].sh_size, offset, fp, name);
2a8a80e4 311 offset += shdr[i].sh_size;
312 free (rel);
313 }
314}
315
316static void
0ae70393 317write_symbol_table (FILE* fp, const char *name, char *image,
2a8a80e4 318 struct grub_pe32_coff_header *pe_chdr,
319 struct grub_pe32_section_table *pe_shdr,
320 int *section_map)
321{
322 struct grub_pe32_symbol *pe_symtab;
323 char *pe_strtab;
324 Elf32_Sym *symtab;
325 int *symtab_map, num_syms;
326 int i;
327
328 pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
329 pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
330
331 symtab = (Elf32_Sym *) xmalloc ((pe_chdr->num_symbols + 1) *
332 sizeof (Elf32_Sym));
333 memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf32_Sym));
334 num_syms = 1;
335
336 symtab_map = (int *) xmalloc (pe_chdr->num_symbols * sizeof (int));
337
338 for (i = 0; i < (int) pe_chdr->num_symbols;
339 i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
340 {
341 int bind, type;
342
343 symtab_map[i] = -1;
344 if ((pe_symtab->section > pe_chdr->num_sections) ||
345 (section_map[pe_symtab->section] == -1))
346 continue;
347
348 if (! pe_symtab->section)
349 type = STT_NOTYPE;
350 else if (pe_symtab->type == GRUB_PE32_DT_FUNCTION)
351 type = STT_FUNC;
352 else
353 type = STT_OBJECT;
354
355 if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
356 bind = STB_GLOBAL;
357 else
358 bind = STB_LOCAL;
359
f1632d4d 360 if ((pe_symtab->type != GRUB_PE32_DT_FUNCTION) && (pe_symtab->num_aux))
2a8a80e4 361 {
362 if (! pe_symtab->value)
363 type = STT_SECTION;
364
365 symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name;
366 }
367 else
368 {
33c846be 369 char short_name[9];
1bab1ae3 370 char *symname;
2a8a80e4 371
33c846be 372 if (pe_symtab->long_name[0])
373 {
374 strncpy (short_name, pe_symtab->short_name, 8);
375 short_name[8] = 0;
1bab1ae3 376 symname = short_name;
33c846be 377 }
378 else
1bab1ae3 379 symname = pe_strtab + pe_symtab->long_name[1];
2a8a80e4 380
1bab1ae3
VS
381 if ((strcmp (symname, "_grub_mod_init")) &&
382 (strcmp (symname, "_grub_mod_fini")) &&
2a8a80e4 383 (bind == STB_LOCAL))
384 continue;
385
1bab1ae3 386 symtab[num_syms].st_name = insert_string (symname);
2a8a80e4 387 }
388
389 symtab[num_syms].st_shndx = section_map[pe_symtab->section];
390 symtab[num_syms].st_value = pe_symtab->value;
391 symtab[num_syms].st_info = ELF32_ST_INFO (bind, type);
392
393 symtab_map[i] = num_syms;
394 num_syms++;
395 }
396
1bab1ae3
VS
397 write_reloc_section (fp, name, image, pe_chdr, pe_shdr,
398 symtab, symtab_map);
2a8a80e4 399
400 shdr[SYMTAB_SECTION].sh_name = insert_string (".symtab");
401 shdr[SYMTAB_SECTION].sh_type = SHT_SYMTAB;
402 shdr[SYMTAB_SECTION].sh_offset = offset;
403 shdr[SYMTAB_SECTION].sh_size = num_syms * sizeof (Elf32_Sym);
404 shdr[SYMTAB_SECTION].sh_entsize = sizeof (Elf32_Sym);
405 shdr[SYMTAB_SECTION].sh_link = STRTAB_SECTION;
406 shdr[SYMTAB_SECTION].sh_addralign = 4;
407
408 grub_util_write_image_at (symtab, shdr[SYMTAB_SECTION].sh_size,
0ae70393 409 offset, fp, name);
2a8a80e4 410 offset += shdr[SYMTAB_SECTION].sh_size;
411
412 free (symtab);
413 free (symtab_map);
414}
415
416static void
0ae70393 417write_string_table (FILE *fp, const char *name)
2a8a80e4 418{
419 shdr[STRTAB_SECTION].sh_name = insert_string (".strtab");
420 shdr[STRTAB_SECTION].sh_type = SHT_STRTAB;
421 shdr[STRTAB_SECTION].sh_offset = offset;
422 shdr[STRTAB_SECTION].sh_size = strtab_len;
423 shdr[STRTAB_SECTION].sh_addralign = 1;
0ae70393
VS
424 grub_util_write_image_at (strtab, strtab_len, offset, fp,
425 name);
2a8a80e4 426 offset += strtab_len;
427
428 free (strtab);
429}
430
431static void
0ae70393 432write_section_header (FILE *fp, const char *name)
2a8a80e4 433{
434 ehdr.e_ident[EI_MAG0] = ELFMAG0;
435 ehdr.e_ident[EI_MAG1] = ELFMAG1;
436 ehdr.e_ident[EI_MAG2] = ELFMAG2;
437 ehdr.e_ident[EI_MAG3] = ELFMAG3;
438 ehdr.e_ident[EI_VERSION] = EV_CURRENT;
439 ehdr.e_version = EV_CURRENT;
440 ehdr.e_type = ET_REL;
441
442 ehdr.e_ident[EI_CLASS] = ELFCLASS32;
443 ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
444 ehdr.e_machine = EM_386;
445
446 ehdr.e_ehsize = sizeof (ehdr);
447 ehdr.e_shentsize = sizeof (Elf32_Shdr);
448 ehdr.e_shstrndx = STRTAB_SECTION;
449
450 ehdr.e_shoff = offset;
451 ehdr.e_shnum = num_sections;
452 grub_util_write_image_at (&shdr, sizeof (Elf32_Shdr) * num_sections,
0ae70393 453 offset, fp, name);
2a8a80e4 454
0ae70393 455 grub_util_write_image_at (&ehdr, sizeof (Elf32_Ehdr), 0, fp, name);
2a8a80e4 456}
457
458static void
0ae70393 459convert_pe (FILE* fp, const char *name, char *image)
2a8a80e4 460{
461 struct grub_pe32_coff_header *pe_chdr;
462 struct grub_pe32_section_table *pe_shdr;
463 int *section_map;
464
465 pe_chdr = (struct grub_pe32_coff_header *) image;
466 if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE_I386)
70a14d3d 467 grub_util_error ("invalid coff image");
2a8a80e4 468
469 strtab = xmalloc (STRTAB_BLOCK);
470 strtab_max = STRTAB_BLOCK;
471 strtab[0] = 0;
472 strtab_len = 1;
473
474 offset = sizeof (ehdr);
475 pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1);
476 num_sections = REL_SECTION;
477
0ae70393 478 section_map = write_section_data (fp, name, image, pe_chdr, pe_shdr);
2a8a80e4 479
0ae70393 480 write_symbol_table (fp, name, image, pe_chdr, pe_shdr, section_map);
2a8a80e4 481 free (section_map);
482
0ae70393 483 write_string_table (fp, name);
2a8a80e4 484
0ae70393 485 write_section_header (fp, name);
2a8a80e4 486}
487
488int
489main (int argc, char *argv[])
490{
491 char *image;
492 FILE* fp;
493
8a4c07fd 494 set_program_name (argv[0]);
2a8a80e4 495
496 /* Check for options. */
497 while (1)
498 {
499 int c = getopt_long (argc, argv, "hVv", options, 0);
500
501 if (c == -1)
502 break;
503 else
504 switch (c)
505 {
506 case 'h':
507 usage (0);
508 break;
509
510 case 'V':
8a4c07fd 511 printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
2a8a80e4 512 return 0;
513
514 case 'v':
515 verbosity++;
516 break;
517
518 default:
519 usage (1);
520 break;
521 }
522 }
523
524 /* Obtain PATH. */
525 if (optind >= argc)
526 {
527 fprintf (stderr, "Filename not specified.\n");
528 usage (1);
529 }
530
531 image = grub_util_read_image (argv[optind]);
532
533 if (optind + 1 < argc)
534 optind++;
535
536 fp = fopen (argv[optind], "wb");
537 if (! fp)
538 grub_util_error ("cannot open %s", argv[optind]);
539
0ae70393 540 convert_pe (fp, argv[optind], image);
2a8a80e4 541
542 fclose (fp);
543
544 return 0;
545}