Merge branch 'mainline' into zfs
[grub2/phcoder.git] / util / grub-pe2elf.c
blob2b2a43ab75938ac468cd157e382820a53525f1da
1 /* grub-pe2elf.c - tool to convert pe image to elf. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
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.
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.
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/>.
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>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <getopt.h>
32 static struct option options[] = {
33 {"help", no_argument, 0, 'h'},
34 {"version", no_argument, 0, 'V'},
35 {"verbose", no_argument, 0, 'v'},
36 {0, 0, 0, 0}
39 static void
40 usage (int status)
42 if (status)
43 fprintf (stderr, "Try ``grub-pe2elf --help'' for more information.\n");
44 else
45 printf ("\
46 Usage: grub-pe2elf [OPTIONS] input [output]\n\
47 \n\
48 Tool to convert pe image to elf.\n\
49 \nOptions:\n\
50 -h, --help display this message and exit\n\
51 -V, --version print version information and exit\n\
52 -v, --verbose print verbose messages\n\
53 \n\
54 Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
56 exit (status);
60 * Section layout
62 * null
63 * .text
64 * .rdata
65 * .data
66 * .bss
67 * .modname
68 * .moddeps
69 * .symtab
70 * .strtab
71 * relocation sections
74 #define TEXT_SECTION 1
75 #define RDATA_SECTION 2
76 #define DATA_SECTION 3
77 #define BSS_SECTION 4
78 #define MODNAME_SECTION 5
79 #define MODDEPS_SECTION 6
80 #define SYMTAB_SECTION 7
81 #define STRTAB_SECTION 8
83 #define REL_SECTION 9
84 #define MAX_SECTIONS 12
86 #define STRTAB_BLOCK 256
88 static char *strtab;
89 static int strtab_max, strtab_len;
91 Elf32_Ehdr ehdr;
92 Elf32_Shdr shdr[MAX_SECTIONS];
93 int num_sections;
94 grub_uint32_t offset;
96 static int
97 insert_string (char *name)
99 int len, result;
101 if (*name == '_')
102 name++;
104 len = strlen (name);
105 if (strtab_len + len >= strtab_max)
107 strtab_max += STRTAB_BLOCK;
108 strtab = xrealloc (strtab, strtab_max);
111 strcpy (strtab + strtab_len, name);
112 result = strtab_len;
113 strtab_len += len + 1;
115 return result;
118 static int *
119 write_section_data (FILE* fp, char *image,
120 struct grub_pe32_coff_header *pe_chdr,
121 struct grub_pe32_section_table *pe_shdr)
123 int *section_map;
124 int i;
126 section_map = xmalloc ((pe_chdr->num_sections + 1) * sizeof (int));
127 section_map[0] = 0;
129 for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++)
131 grub_uint32_t idx;
133 if (! strcmp (pe_shdr->name, ".text"))
135 idx = TEXT_SECTION;
136 shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
138 else if (! strcmp (pe_shdr->name, ".rdata"))
140 idx = RDATA_SECTION;
141 shdr[idx].sh_flags = SHF_ALLOC;
143 else if (! strcmp (pe_shdr->name, ".data"))
145 idx = DATA_SECTION;
146 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
148 else if (! strcmp (pe_shdr->name, ".bss"))
150 idx = BSS_SECTION;
151 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
153 else if (! strcmp (pe_shdr->name, ".modname"))
154 idx = MODNAME_SECTION;
155 else if (! strcmp (pe_shdr->name, ".moddeps"))
156 idx = MODDEPS_SECTION;
157 else
159 section_map[i + 1] = -1;
160 continue;
163 section_map[i + 1] = idx;
165 shdr[idx].sh_type = (idx == BSS_SECTION) ? SHT_NOBITS : SHT_PROGBITS;
166 shdr[idx].sh_size = pe_shdr->raw_data_size;
167 shdr[idx].sh_addralign = 1 << (((pe_shdr->characteristics >>
168 GRUB_PE32_SCN_ALIGN_SHIFT) &
169 GRUB_PE32_SCN_ALIGN_MASK) - 1);
171 if (idx != BSS_SECTION)
173 shdr[idx].sh_offset = offset;
174 grub_util_write_image_at (image + pe_shdr->raw_data_offset,
175 pe_shdr->raw_data_size, offset, fp);
177 offset += pe_shdr->raw_data_size;
180 if (pe_shdr->relocations_offset)
182 char name[5 + strlen (pe_shdr->name)];
184 if (num_sections >= MAX_SECTIONS)
185 grub_util_error ("Too many sections");
187 sprintf (name, ".rel%s", pe_shdr->name);
189 shdr[num_sections].sh_name = insert_string (name);
190 shdr[num_sections].sh_link = i;
191 shdr[num_sections].sh_info = idx;
193 shdr[idx].sh_name = shdr[num_sections].sh_name + 4;
195 num_sections++;
197 else
198 shdr[idx].sh_name = insert_string (pe_shdr->name);
201 return section_map;
204 static void
205 write_reloc_section (FILE* fp, char *image,
206 struct grub_pe32_coff_header *pe_chdr,
207 struct grub_pe32_section_table *pe_shdr,
208 Elf32_Sym *symtab,
209 int *symtab_map)
211 int i;
213 for (i = REL_SECTION; i < num_sections; i++)
215 struct grub_pe32_section_table *pe_sec;
216 struct grub_pe32_reloc *pe_rel;
217 Elf32_Rel *rel;
218 int num_rels, j, modified;
220 pe_sec = pe_shdr + shdr[i].sh_link;
221 pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
222 rel = (Elf32_Rel *) xmalloc (pe_sec->num_relocations * sizeof (Elf32_Rel));
223 num_rels = 0;
224 modified = 0;
226 for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
228 int type;
229 grub_uint32_t ofs, *addr;
231 if ((pe_rel->symtab_index >= pe_chdr->num_symbols) ||
232 (symtab_map[pe_rel->symtab_index] == -1))
233 grub_util_error ("Invalid symbol");
235 if (pe_rel->type == GRUB_PE32_REL_I386_DIR32)
236 type = R_386_32;
237 else if (pe_rel->type == GRUB_PE32_REL_I386_REL32)
238 type = R_386_PC32;
239 else
240 grub_util_error ("Unknown pe relocation type %d\n", pe_rel->type);
242 ofs = pe_rel->offset - pe_sec->virtual_address;
243 addr = (grub_uint32_t *)(image + pe_sec->raw_data_offset + ofs);
244 if (type == R_386_PC32)
246 unsigned char code;
248 code = image[pe_sec->raw_data_offset + ofs - 1];
250 if (((code != 0xe8) && (code != 0xe9)) || (*addr))
251 grub_util_error ("Invalid relocation (%x %x)", code, *addr);
253 modified = 1;
254 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx)
256 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx
257 != shdr[i].sh_info)
258 grub_util_error ("Cross section call is not allowed");
260 *addr = (symtab[symtab_map[pe_rel->symtab_index]].st_value
261 - ofs - 4);
263 continue;
265 else
266 *addr = -4;
269 rel[num_rels].r_offset = ofs;
270 rel[num_rels].r_info = ELF32_R_INFO (symtab_map[pe_rel->symtab_index],
271 type);
272 num_rels++;
275 if (modified)
276 grub_util_write_image_at (image + pe_sec->raw_data_offset,
277 shdr[shdr[i].sh_info].sh_size,
278 shdr[shdr[i].sh_info].sh_offset,
279 fp);
281 shdr[i].sh_type = SHT_REL;
282 shdr[i].sh_offset = offset;
283 shdr[i].sh_link = SYMTAB_SECTION;
284 shdr[i].sh_addralign = 4;
285 shdr[i].sh_entsize = sizeof (Elf32_Rel);
286 shdr[i].sh_size = num_rels * sizeof (Elf32_Rel);
288 grub_util_write_image_at (rel, shdr[i].sh_size, offset, fp);
289 offset += shdr[i].sh_size;
290 free (rel);
294 static void
295 write_symbol_table (FILE* fp, char *image,
296 struct grub_pe32_coff_header *pe_chdr,
297 struct grub_pe32_section_table *pe_shdr,
298 int *section_map)
300 struct grub_pe32_symbol *pe_symtab;
301 char *pe_strtab;
302 Elf32_Sym *symtab;
303 int *symtab_map, num_syms;
304 int i;
306 pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
307 pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
309 symtab = (Elf32_Sym *) xmalloc ((pe_chdr->num_symbols + 1) *
310 sizeof (Elf32_Sym));
311 memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf32_Sym));
312 num_syms = 1;
314 symtab_map = (int *) xmalloc (pe_chdr->num_symbols * sizeof (int));
316 for (i = 0; i < (int) pe_chdr->num_symbols;
317 i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
319 int bind, type;
321 symtab_map[i] = -1;
322 if ((pe_symtab->section > pe_chdr->num_sections) ||
323 (section_map[pe_symtab->section] == -1))
324 continue;
326 if (! pe_symtab->section)
327 type = STT_NOTYPE;
328 else if (pe_symtab->type == GRUB_PE32_DT_FUNCTION)
329 type = STT_FUNC;
330 else
331 type = STT_OBJECT;
333 if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
334 bind = STB_GLOBAL;
335 else
336 bind = STB_LOCAL;
338 if ((type != STT_FUNC) && (pe_symtab->num_aux))
340 if (! pe_symtab->value)
341 type = STT_SECTION;
343 symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name;
345 else
347 char short_name[9];
348 char *name;
350 if (pe_symtab->long_name[0])
352 strncpy (short_name, pe_symtab->short_name, 8);
353 short_name[8] = 0;
354 name = short_name;
356 else
357 name = pe_strtab + pe_symtab->long_name[1];
359 if ((strcmp (name, "_grub_mod_init")) &&
360 (strcmp (name, "_grub_mod_fini")) &&
361 (bind == STB_LOCAL))
362 continue;
364 symtab[num_syms].st_name = insert_string (name);
367 symtab[num_syms].st_shndx = section_map[pe_symtab->section];
368 symtab[num_syms].st_value = pe_symtab->value;
369 symtab[num_syms].st_info = ELF32_ST_INFO (bind, type);
371 symtab_map[i] = num_syms;
372 num_syms++;
375 write_reloc_section (fp, image, pe_chdr, pe_shdr, symtab, symtab_map);
377 shdr[SYMTAB_SECTION].sh_name = insert_string (".symtab");
378 shdr[SYMTAB_SECTION].sh_type = SHT_SYMTAB;
379 shdr[SYMTAB_SECTION].sh_offset = offset;
380 shdr[SYMTAB_SECTION].sh_size = num_syms * sizeof (Elf32_Sym);
381 shdr[SYMTAB_SECTION].sh_entsize = sizeof (Elf32_Sym);
382 shdr[SYMTAB_SECTION].sh_link = STRTAB_SECTION;
383 shdr[SYMTAB_SECTION].sh_addralign = 4;
385 grub_util_write_image_at (symtab, shdr[SYMTAB_SECTION].sh_size,
386 offset, fp);
387 offset += shdr[SYMTAB_SECTION].sh_size;
389 free (symtab);
390 free (symtab_map);
393 static void
394 write_string_table (FILE* fp)
396 shdr[STRTAB_SECTION].sh_name = insert_string (".strtab");
397 shdr[STRTAB_SECTION].sh_type = SHT_STRTAB;
398 shdr[STRTAB_SECTION].sh_offset = offset;
399 shdr[STRTAB_SECTION].sh_size = strtab_len;
400 shdr[STRTAB_SECTION].sh_addralign = 1;
401 grub_util_write_image_at (strtab, strtab_len, offset, fp);
402 offset += strtab_len;
404 free (strtab);
407 static void
408 write_section_header (FILE* fp)
410 ehdr.e_ident[EI_MAG0] = ELFMAG0;
411 ehdr.e_ident[EI_MAG1] = ELFMAG1;
412 ehdr.e_ident[EI_MAG2] = ELFMAG2;
413 ehdr.e_ident[EI_MAG3] = ELFMAG3;
414 ehdr.e_ident[EI_VERSION] = EV_CURRENT;
415 ehdr.e_version = EV_CURRENT;
416 ehdr.e_type = ET_REL;
418 ehdr.e_ident[EI_CLASS] = ELFCLASS32;
419 ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
420 ehdr.e_machine = EM_386;
422 ehdr.e_ehsize = sizeof (ehdr);
423 ehdr.e_shentsize = sizeof (Elf32_Shdr);
424 ehdr.e_shstrndx = STRTAB_SECTION;
426 ehdr.e_shoff = offset;
427 ehdr.e_shnum = num_sections;
428 grub_util_write_image_at (&shdr, sizeof (Elf32_Shdr) * num_sections,
429 offset, fp);
431 grub_util_write_image_at (&ehdr, sizeof (Elf32_Ehdr), 0, fp);
434 static void
435 convert_pe (FILE* fp, char *image)
437 struct grub_pe32_coff_header *pe_chdr;
438 struct grub_pe32_section_table *pe_shdr;
439 int *section_map;
441 pe_chdr = (struct grub_pe32_coff_header *) image;
442 if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE_I386)
443 grub_util_error ("Invalid coff image");
445 strtab = xmalloc (STRTAB_BLOCK);
446 strtab_max = STRTAB_BLOCK;
447 strtab[0] = 0;
448 strtab_len = 1;
450 offset = sizeof (ehdr);
451 pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1);
452 num_sections = REL_SECTION;
454 section_map = write_section_data (fp, image, pe_chdr, pe_shdr);
456 write_symbol_table (fp, image, pe_chdr, pe_shdr, section_map);
457 free (section_map);
459 write_string_table (fp);
461 write_section_header (fp);
465 main (int argc, char *argv[])
467 char *image;
468 FILE* fp;
470 progname = "grub-pe2elf";
472 /* Check for options. */
473 while (1)
475 int c = getopt_long (argc, argv, "hVv", options, 0);
477 if (c == -1)
478 break;
479 else
480 switch (c)
482 case 'h':
483 usage (0);
484 break;
486 case 'V':
487 printf ("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
488 return 0;
490 case 'v':
491 verbosity++;
492 break;
494 default:
495 usage (1);
496 break;
500 /* Obtain PATH. */
501 if (optind >= argc)
503 fprintf (stderr, "Filename not specified.\n");
504 usage (1);
507 image = grub_util_read_image (argv[optind]);
509 if (optind + 1 < argc)
510 optind++;
512 fp = fopen (argv[optind], "wb");
513 if (! fp)
514 grub_util_error ("cannot open %s", argv[optind]);
516 convert_pe (fp, image);
518 fclose (fp);
520 return 0;