tpm2_key_protector: Enable build for powerpc_ieee1275
[grub.git] / util / grub-pe2elf.c
blob11331294f1b2deb6f94414a65a1c6dd93818f1e0
1 /* grub-pe2elf.c - tool to convert pe image to elf. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008,2009 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>
25 #include <grub/misc.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <getopt.h>
34 /* Please don't internationalise this file. It's pointless. */
37 * Section layout
39 * null
40 * .text
41 * .rdata
42 * .data
43 * .bss
44 * .modname
45 * .moddeps
46 * .symtab
47 * .strtab
48 * relocation sections
51 #if GRUB_TARGET_WORDSIZE == 64
52 typedef Elf64_Rela elf_reloc_t;
53 #define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_X86_64
54 #else
55 typedef Elf32_Rel elf_reloc_t;
56 #define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_I386
57 #endif
59 #define STRTAB_BLOCK 256
61 static char *strtab;
62 static int strtab_max, strtab_len;
64 static Elf_Ehdr ehdr;
65 static Elf_Shdr *shdr;
66 static int num_sections, first_reloc_section, reloc_sections_end, symtab_section, strtab_section;
67 static grub_uint32_t offset, image_base;
69 static int
70 insert_string (const char *name)
72 int len, result;
74 if (*name == '_')
75 name++;
77 len = strlen (name);
78 if (strtab_len + len >= strtab_max)
80 strtab_max += STRTAB_BLOCK;
81 strtab = xrealloc (strtab, strtab_max);
84 strcpy (strtab + strtab_len, name);
85 result = strtab_len;
86 strtab_len += len + 1;
88 return result;
91 static int *
92 write_section_data (FILE* fp, const char *name, char *image,
93 struct grub_pe32_coff_header *pe_chdr,
94 struct grub_pe32_section_table *pe_shdr)
96 int *section_map;
97 int i;
98 grub_uint32_t last_category = 0;
99 grub_uint32_t idx, idx_reloc;
100 char *pe_strtab = (image + pe_chdr->symtab_offset
101 + pe_chdr->num_symbols * sizeof (struct grub_pe32_symbol));
103 section_map = xcalloc (2 * pe_chdr->num_sections + 5, sizeof (int));
104 section_map[0] = 0;
105 shdr = xcalloc (2 * pe_chdr->num_sections + 5, sizeof (shdr[0]));
106 idx = 1;
107 idx_reloc = pe_chdr->num_sections + 1;
109 for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++)
111 grub_uint32_t category;
112 const char *shname = pe_shdr->name;
113 grub_size_t secsize;
115 if (shname[0] == '/' && grub_isdigit (shname[1]))
117 char t[sizeof (pe_shdr->name) + 1];
118 memcpy (t, shname, sizeof (pe_shdr->name));
119 t[sizeof (pe_shdr->name)] = 0;
120 shname = pe_strtab + atoi (t + 1);
123 secsize = pe_shdr->raw_data_size;
125 shdr[idx].sh_type = SHT_PROGBITS;
127 if (! strcmp (shname, ".text"))
129 category = 0;
130 shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
132 else if (! strncmp (shname, ".rdata", 6))
134 category = 1;
135 shdr[idx].sh_flags = SHF_ALLOC;
137 else if (! strcmp (shname, ".data"))
139 category = 2;
140 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
142 else if (! strcmp (shname, ".bss"))
144 category = 3;
145 shdr[idx].sh_type = SHT_NOBITS;
146 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
147 if (secsize < pe_shdr->virtual_size)
148 secsize = pe_shdr->virtual_size;
150 else if (strcmp (shname, ".modname") == 0 || strcmp (shname, ".moddeps") == 0
151 || strcmp (shname, ".module_license") == 0)
152 category = 4;
153 else
155 section_map[i + 1] = -1;
156 continue;
159 if (category < last_category)
160 grub_util_error ("out of order sections");
162 section_map[i + 1] = idx;
164 if (pe_shdr->virtual_size
165 && pe_shdr->virtual_size < secsize)
166 secsize = pe_shdr->virtual_size;
168 shdr[idx].sh_size = secsize;
169 shdr[idx].sh_addralign = 1 << (((pe_shdr->characteristics >>
170 GRUB_PE32_SCN_ALIGN_SHIFT) &
171 GRUB_PE32_SCN_ALIGN_MASK) - 1);
172 shdr[idx].sh_addr = pe_shdr->virtual_address + image_base;
174 if (shdr[idx].sh_type != SHT_NOBITS)
176 shdr[idx].sh_offset = offset;
177 grub_util_write_image_at (image + pe_shdr->raw_data_offset,
178 pe_shdr->raw_data_size, offset, fp,
179 shname);
181 offset += secsize;
184 if (pe_shdr->relocations_offset)
186 char relname[5 + strlen (shname)];
188 sprintf (relname, ".rel%s", shname);
190 shdr[idx_reloc].sh_name = insert_string (relname);
191 shdr[idx_reloc].sh_link = i;
192 shdr[idx_reloc].sh_info = idx;
194 shdr[idx].sh_name = shdr[idx_reloc].sh_name + 4;
196 idx_reloc++;
198 else
199 shdr[idx].sh_name = insert_string (shname);
200 idx++;
203 idx_reloc -= pe_chdr->num_sections + 1;
204 num_sections = idx + idx_reloc + 2;
205 first_reloc_section = idx;
206 reloc_sections_end = idx + idx_reloc;
207 memmove (shdr + idx, shdr + pe_chdr->num_sections + 1,
208 idx_reloc * sizeof (shdr[0]));
209 memset (shdr + idx + idx_reloc, 0, 3 * sizeof (shdr[0]));
210 memset (shdr, 0, sizeof (shdr[0]));
212 symtab_section = idx + idx_reloc;
213 strtab_section = idx + idx_reloc + 1;
215 return section_map;
218 static void
219 write_reloc_section (FILE* fp, const char *name, char *image,
220 struct grub_pe32_coff_header *pe_chdr,
221 struct grub_pe32_section_table *pe_shdr,
222 Elf_Sym *symtab,
223 int *symtab_map)
225 int i;
227 for (i = first_reloc_section; i < reloc_sections_end; i++)
229 struct grub_pe32_section_table *pe_sec;
230 struct grub_pe32_reloc *pe_rel;
231 elf_reloc_t *rel;
232 int num_rels, j, modified;
234 pe_sec = pe_shdr + shdr[i].sh_link;
235 pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
236 rel = (elf_reloc_t *) xcalloc (pe_sec->num_relocations, sizeof (elf_reloc_t));
237 num_rels = 0;
238 modified = 0;
240 for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
242 int type;
243 grub_uint32_t ofs, *addr;
245 if ((pe_rel->symtab_index >= pe_chdr->num_symbols) ||
246 (symtab_map[pe_rel->symtab_index] == -1))
247 grub_util_error ("invalid symbol");
249 ofs = pe_rel->offset - pe_sec->virtual_address;
250 addr = (grub_uint32_t *)(image + pe_sec->raw_data_offset + ofs);
252 switch (pe_rel->type)
254 #if GRUB_TARGET_WORDSIZE == 64
255 case 1:
256 type = R_X86_64_64;
257 rel[num_rels].r_addend = *(grub_int64_t *)addr;
258 *(grub_int64_t *)addr = 0;
259 modified = 1;
260 break;
261 case 4:
262 type = R_X86_64_PC32;
263 rel[num_rels].r_addend = *(grub_int32_t *)addr;
264 *addr = 0;
265 modified = 1;
266 break;
267 case 14:
268 type = R_X86_64_PC64;
269 rel[num_rels].r_addend = *(grub_uint64_t *)addr - 8;
270 *(grub_uint64_t *)addr = 0;
271 modified = 1;
272 break;
273 #else
274 case GRUB_PE32_REL_I386_DIR32:
275 type = R_386_32;
276 break;
277 case GRUB_PE32_REL_I386_REL32:
278 type = R_386_PC32;
279 break;
280 #endif
281 default:
282 grub_util_error ("unknown pe relocation type %d", pe_rel->type);
285 if (type ==
286 #if GRUB_TARGET_WORDSIZE == 64
287 R_386_PC32
288 #else
289 R_X86_64_PC32
290 #endif
294 unsigned char code;
296 code = image[pe_sec->raw_data_offset + ofs - 1];
298 #if GRUB_TARGET_WORDSIZE == 32
299 if (((code != 0xe8) && (code != 0xe9)) || (*addr))
300 grub_util_error ("invalid relocation (%x %x)", code, *addr);
301 #endif
303 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx
304 && symtab[symtab_map[pe_rel->symtab_index]].st_shndx
305 == shdr[i].sh_info)
307 modified = 1;
308 *addr += (symtab[symtab_map[pe_rel->symtab_index]].st_value
309 - ofs - 4);
311 continue;
313 else
315 #if GRUB_TARGET_WORDSIZE == 64
316 rel[num_rels].r_addend -= 4;
317 #else
318 modified = 1;
319 *addr = -4;
320 #endif
324 rel[num_rels].r_offset = ofs;
325 rel[num_rels].r_info = ELF_R_INFO (symtab_map[pe_rel->symtab_index],
326 type);
327 num_rels++;
330 if (modified)
331 grub_util_write_image_at (image + pe_sec->raw_data_offset,
332 shdr[shdr[i].sh_info].sh_size,
333 shdr[shdr[i].sh_info].sh_offset,
334 fp, name);
336 #if GRUB_TARGET_WORDSIZE == 64
337 shdr[i].sh_type = SHT_RELA;
338 #else
339 shdr[i].sh_type = SHT_REL;
340 #endif
341 shdr[i].sh_offset = offset;
342 shdr[i].sh_link = symtab_section;
343 shdr[i].sh_addralign = 4;
344 shdr[i].sh_entsize = sizeof (elf_reloc_t);
345 shdr[i].sh_size = num_rels * sizeof (elf_reloc_t);
347 grub_util_write_image_at (rel, shdr[i].sh_size, offset, fp, name);
348 offset += shdr[i].sh_size;
349 free (rel);
353 static void
354 write_symbol_table (FILE* fp, const char *name, char *image,
355 struct grub_pe32_coff_header *pe_chdr,
356 struct grub_pe32_section_table *pe_shdr,
357 int *section_map)
359 struct grub_pe32_symbol *pe_symtab;
360 char *pe_strtab;
361 Elf_Sym *symtab;
362 int *symtab_map, num_syms;
363 int i;
365 pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
366 pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
368 symtab = (Elf_Sym *) xcalloc (pe_chdr->num_symbols + 1, sizeof (Elf_Sym));
369 num_syms = 1;
371 symtab_map = (int *) xcalloc (pe_chdr->num_symbols, sizeof (int));
373 for (i = 0; i < (int) pe_chdr->num_symbols;
374 i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
376 int bind, type;
378 symtab_map[i] = -1;
379 if ((pe_symtab->section > pe_chdr->num_sections) ||
380 (section_map[pe_symtab->section] == -1))
381 continue;
383 if (! pe_symtab->section)
384 type = STT_NOTYPE;
385 else if (pe_symtab->type == GRUB_PE32_DT_FUNCTION)
386 type = STT_FUNC;
387 else
388 type = STT_OBJECT;
390 if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
391 bind = STB_GLOBAL;
392 else
393 bind = STB_LOCAL;
395 if ((pe_symtab->type != GRUB_PE32_DT_FUNCTION) && (pe_symtab->num_aux))
397 if (! pe_symtab->value)
398 type = STT_SECTION;
400 symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name;
402 else
404 char short_name[9];
405 char *symname;
407 if (pe_symtab->long_name[0])
409 strncpy (short_name, pe_symtab->short_name, 8);
410 short_name[8] = 0;
411 symname = short_name;
413 else
414 symname = pe_strtab + pe_symtab->long_name[1];
416 if ((strcmp (symname, "_grub_mod_init")) &&
417 (strcmp (symname, "_grub_mod_fini")) &&
418 (strcmp (symname, "grub_mod_init")) &&
419 (strcmp (symname, "grub_mod_fini")) &&
420 (bind == STB_LOCAL))
421 continue;
423 symtab[num_syms].st_name = insert_string (symname);
426 symtab[num_syms].st_shndx = section_map[pe_symtab->section];
427 symtab[num_syms].st_value = pe_symtab->value;
428 symtab[num_syms].st_info = ELF_ST_INFO (bind, type);
430 symtab_map[i] = num_syms;
431 num_syms++;
434 write_reloc_section (fp, name, image, pe_chdr, pe_shdr,
435 symtab, symtab_map);
437 shdr[symtab_section].sh_name = insert_string (".symtab");
438 shdr[symtab_section].sh_type = SHT_SYMTAB;
439 shdr[symtab_section].sh_offset = offset;
440 shdr[symtab_section].sh_size = num_syms * sizeof (Elf_Sym);
441 shdr[symtab_section].sh_entsize = sizeof (Elf_Sym);
442 shdr[symtab_section].sh_link = strtab_section;
443 shdr[symtab_section].sh_addralign = 4;
445 grub_util_write_image_at (symtab, shdr[symtab_section].sh_size,
446 offset, fp, name);
447 offset += shdr[symtab_section].sh_size;
449 free (symtab);
450 free (symtab_map);
453 static void
454 write_string_table (FILE *fp, const char *name)
456 shdr[strtab_section].sh_name = insert_string (".strtab");
457 shdr[strtab_section].sh_type = SHT_STRTAB;
458 shdr[strtab_section].sh_offset = offset;
459 shdr[strtab_section].sh_size = strtab_len;
460 shdr[strtab_section].sh_addralign = 1;
461 grub_util_write_image_at (strtab, strtab_len, offset, fp,
462 name);
463 offset += strtab_len;
465 free (strtab);
468 static void
469 write_section_header (FILE *fp, const char *name)
471 ehdr.e_ident[EI_MAG0] = ELFMAG0;
472 ehdr.e_ident[EI_MAG1] = ELFMAG1;
473 ehdr.e_ident[EI_MAG2] = ELFMAG2;
474 ehdr.e_ident[EI_MAG3] = ELFMAG3;
475 ehdr.e_ident[EI_VERSION] = EV_CURRENT;
476 ehdr.e_version = EV_CURRENT;
477 ehdr.e_type = ET_REL;
479 #if GRUB_TARGET_WORDSIZE == 64
480 ehdr.e_ident[EI_CLASS] = ELFCLASS64;
481 ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
482 ehdr.e_machine = EM_X86_64;
483 #else
484 ehdr.e_ident[EI_CLASS] = ELFCLASS32;
485 ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
486 ehdr.e_machine = EM_386;
487 #endif
488 ehdr.e_ehsize = sizeof (ehdr);
489 ehdr.e_shentsize = sizeof (Elf_Shdr);
490 ehdr.e_shstrndx = strtab_section;
492 ehdr.e_shoff = offset;
493 ehdr.e_shnum = num_sections;
494 grub_util_write_image_at (shdr, sizeof (Elf_Shdr) * num_sections,
495 offset, fp, name);
497 grub_util_write_image_at (&ehdr, sizeof (Elf_Ehdr), 0, fp, name);
500 static void
501 convert_pe (FILE* fp, const char *name, char *image)
503 struct grub_pe32_coff_header *pe_chdr;
504 struct grub_pe32_section_table *pe_shdr;
505 int *section_map;
507 if (image[0] == 'M' && image[1] == 'Z')
508 pe_chdr = (struct grub_pe32_coff_header *) (image + (grub_le_to_cpu32 (((grub_uint32_t *)image)[0xf]) + 4));
509 else
510 pe_chdr = (struct grub_pe32_coff_header *) image;
511 if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE)
512 grub_util_error ("invalid coff image (%x != %x)",
513 grub_le_to_cpu16 (pe_chdr->machine), GRUB_PE32_MACHINE);
515 strtab = xmalloc (STRTAB_BLOCK);
516 strtab_max = STRTAB_BLOCK;
517 strtab[0] = 0;
518 strtab_len = 1;
520 offset = sizeof (ehdr);
521 if (pe_chdr->optional_header_size)
523 #if GRUB_TARGET_WORDSIZE == 64
524 struct grub_pe64_optional_header *o;
525 #else
526 struct grub_pe32_optional_header *o;
527 #endif
528 o = (void *) (pe_chdr + 1);
529 image_base = o->image_base;
531 pe_shdr = (struct grub_pe32_section_table *) ((char *) (pe_chdr + 1) + pe_chdr->optional_header_size);
533 section_map = write_section_data (fp, name, image, pe_chdr, pe_shdr);
535 write_symbol_table (fp, name, image, pe_chdr, pe_shdr, section_map);
536 free (section_map);
538 write_string_table (fp, name);
540 write_section_header (fp, name);
544 main (int argc, char *argv[])
546 char *image;
547 FILE* fp;
548 char *in, *out;
550 /* Obtain PATH. */
551 if (1 >= argc)
553 fprintf (stderr, "Filename not specified.\n");
554 return 1;
557 in = argv[1];
558 if (argc > 2)
559 out = argv[2];
560 else
561 out = in;
562 image = grub_util_read_image (in);
564 fp = grub_util_fopen (out, "wb");
565 if (! fp)
566 grub_util_error ("cannot open %s", out);
568 convert_pe (fp, out, image);
570 fclose (fp);
572 return 0;