1 /* grub-pe2elf.c - tool to convert pe image to elf. */
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/>.
21 #include <grub/types.h>
22 #include <grub/util/misc.h>
24 #include <grub/efi/pe32.h>
25 #include <grub/misc.h>
34 /* Please don't internationalise this file. It's pointless. */
51 #if GRUB_TARGET_WORDSIZE == 64
52 typedef Elf64_Rela elf_reloc_t
;
53 #define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_X86_64
55 typedef Elf32_Rel elf_reloc_t
;
56 #define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_I386
59 #define STRTAB_BLOCK 256
62 static int strtab_max
, strtab_len
;
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
;
70 insert_string (const char *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
);
86 strtab_len
+= len
+ 1;
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
)
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
= xmalloc ((2 * pe_chdr
->num_sections
+ 5) * sizeof (int));
105 shdr
= xmalloc ((2 * pe_chdr
->num_sections
+ 5) * sizeof (shdr
[0]));
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
;
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"))
130 shdr
[idx
].sh_flags
= SHF_ALLOC
| SHF_EXECINSTR
;
132 else if (! strncmp (shname
, ".rdata", 6))
135 shdr
[idx
].sh_flags
= SHF_ALLOC
;
137 else if (! strcmp (shname
, ".data"))
140 shdr
[idx
].sh_flags
= SHF_ALLOC
| SHF_WRITE
;
142 else if (! strcmp (shname
, ".bss"))
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)
155 section_map
[i
+ 1] = -1;
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
,
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;
199 shdr
[idx
].sh_name
= insert_string (shname
);
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;
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
,
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
;
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
*) xmalloc (pe_sec
->num_relocations
* sizeof (elf_reloc_t
));
240 for (j
= 0; j
< pe_sec
->num_relocations
; j
++, pe_rel
++)
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
257 rel
[num_rels
].r_addend
= *(grub_int64_t
*)addr
;
258 *(grub_int64_t
*)addr
= 0;
262 type
= R_X86_64_PC32
;
263 rel
[num_rels
].r_addend
= *(grub_int32_t
*)addr
;
268 type
= R_X86_64_PC64
;
269 rel
[num_rels
].r_addend
= *(grub_uint64_t
*)addr
- 8;
270 *(grub_uint64_t
*)addr
= 0;
274 case GRUB_PE32_REL_I386_DIR32
:
277 case GRUB_PE32_REL_I386_REL32
:
282 grub_util_error ("unknown pe relocation type %d\n", pe_rel
->type
);
286 #if GRUB_TARGET_WORDSIZE == 64
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
);
303 if (symtab
[symtab_map
[pe_rel
->symtab_index
]].st_shndx
304 && symtab
[symtab_map
[pe_rel
->symtab_index
]].st_shndx
308 *addr
+= (symtab
[symtab_map
[pe_rel
->symtab_index
]].st_value
315 #if GRUB_TARGET_WORDSIZE == 64
316 rel
[num_rels
].r_addend
-= 4;
324 rel
[num_rels
].r_offset
= ofs
;
325 rel
[num_rels
].r_info
= ELF_R_INFO (symtab_map
[pe_rel
->symtab_index
],
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
,
336 #if GRUB_TARGET_WORDSIZE == 64
337 shdr
[i
].sh_type
= SHT_RELA
;
339 shdr
[i
].sh_type
= SHT_REL
;
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
;
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
,
359 struct grub_pe32_symbol
*pe_symtab
;
362 int *symtab_map
, num_syms
;
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
*) xmalloc ((pe_chdr
->num_symbols
+ 1) *
370 memset (symtab
, 0, (pe_chdr
->num_symbols
+ 1) * sizeof (Elf_Sym
));
373 symtab_map
= (int *) xmalloc (pe_chdr
->num_symbols
* sizeof (int));
375 for (i
= 0; i
< (int) pe_chdr
->num_symbols
;
376 i
+= pe_symtab
->num_aux
+ 1, pe_symtab
+= pe_symtab
->num_aux
+ 1)
381 if ((pe_symtab
->section
> pe_chdr
->num_sections
) ||
382 (section_map
[pe_symtab
->section
] == -1))
385 if (! pe_symtab
->section
)
387 else if (pe_symtab
->type
== GRUB_PE32_DT_FUNCTION
)
392 if (pe_symtab
->storage_class
== GRUB_PE32_SYM_CLASS_EXTERNAL
)
397 if ((pe_symtab
->type
!= GRUB_PE32_DT_FUNCTION
) && (pe_symtab
->num_aux
))
399 if (! pe_symtab
->value
)
402 symtab
[num_syms
].st_name
= shdr
[section_map
[pe_symtab
->section
]].sh_name
;
409 if (pe_symtab
->long_name
[0])
411 strncpy (short_name
, pe_symtab
->short_name
, 8);
413 symname
= short_name
;
416 symname
= pe_strtab
+ pe_symtab
->long_name
[1];
418 if ((strcmp (symname
, "_grub_mod_init")) &&
419 (strcmp (symname
, "_grub_mod_fini")) &&
420 (strcmp (symname
, "grub_mod_init")) &&
421 (strcmp (symname
, "grub_mod_fini")) &&
425 symtab
[num_syms
].st_name
= insert_string (symname
);
428 symtab
[num_syms
].st_shndx
= section_map
[pe_symtab
->section
];
429 symtab
[num_syms
].st_value
= pe_symtab
->value
;
430 symtab
[num_syms
].st_info
= ELF_ST_INFO (bind
, type
);
432 symtab_map
[i
] = num_syms
;
436 write_reloc_section (fp
, name
, image
, pe_chdr
, pe_shdr
,
439 shdr
[symtab_section
].sh_name
= insert_string (".symtab");
440 shdr
[symtab_section
].sh_type
= SHT_SYMTAB
;
441 shdr
[symtab_section
].sh_offset
= offset
;
442 shdr
[symtab_section
].sh_size
= num_syms
* sizeof (Elf_Sym
);
443 shdr
[symtab_section
].sh_entsize
= sizeof (Elf_Sym
);
444 shdr
[symtab_section
].sh_link
= strtab_section
;
445 shdr
[symtab_section
].sh_addralign
= 4;
447 grub_util_write_image_at (symtab
, shdr
[symtab_section
].sh_size
,
449 offset
+= shdr
[symtab_section
].sh_size
;
456 write_string_table (FILE *fp
, const char *name
)
458 shdr
[strtab_section
].sh_name
= insert_string (".strtab");
459 shdr
[strtab_section
].sh_type
= SHT_STRTAB
;
460 shdr
[strtab_section
].sh_offset
= offset
;
461 shdr
[strtab_section
].sh_size
= strtab_len
;
462 shdr
[strtab_section
].sh_addralign
= 1;
463 grub_util_write_image_at (strtab
, strtab_len
, offset
, fp
,
465 offset
+= strtab_len
;
471 write_section_header (FILE *fp
, const char *name
)
473 ehdr
.e_ident
[EI_MAG0
] = ELFMAG0
;
474 ehdr
.e_ident
[EI_MAG1
] = ELFMAG1
;
475 ehdr
.e_ident
[EI_MAG2
] = ELFMAG2
;
476 ehdr
.e_ident
[EI_MAG3
] = ELFMAG3
;
477 ehdr
.e_ident
[EI_VERSION
] = EV_CURRENT
;
478 ehdr
.e_version
= EV_CURRENT
;
479 ehdr
.e_type
= ET_REL
;
481 #if GRUB_TARGET_WORDSIZE == 64
482 ehdr
.e_ident
[EI_CLASS
] = ELFCLASS64
;
483 ehdr
.e_ident
[EI_DATA
] = ELFDATA2LSB
;
484 ehdr
.e_machine
= EM_X86_64
;
486 ehdr
.e_ident
[EI_CLASS
] = ELFCLASS32
;
487 ehdr
.e_ident
[EI_DATA
] = ELFDATA2LSB
;
488 ehdr
.e_machine
= EM_386
;
490 ehdr
.e_ehsize
= sizeof (ehdr
);
491 ehdr
.e_shentsize
= sizeof (Elf_Shdr
);
492 ehdr
.e_shstrndx
= strtab_section
;
494 ehdr
.e_shoff
= offset
;
495 ehdr
.e_shnum
= num_sections
;
496 grub_util_write_image_at (shdr
, sizeof (Elf_Shdr
) * num_sections
,
499 grub_util_write_image_at (&ehdr
, sizeof (Elf_Ehdr
), 0, fp
, name
);
503 convert_pe (FILE* fp
, const char *name
, char *image
)
505 struct grub_pe32_coff_header
*pe_chdr
;
506 struct grub_pe32_section_table
*pe_shdr
;
509 if (image
[0] == 'M' && image
[1] == 'Z')
510 pe_chdr
= (struct grub_pe32_coff_header
*) (image
+ (grub_le_to_cpu32 (((grub_uint32_t
*)image
)[0xf]) + 4));
512 pe_chdr
= (struct grub_pe32_coff_header
*) image
;
513 if (grub_le_to_cpu16 (pe_chdr
->machine
) != GRUB_PE32_MACHINE
)
514 grub_util_error ("invalid coff image (%x != %x)",
515 grub_le_to_cpu16 (pe_chdr
->machine
), GRUB_PE32_MACHINE
);
517 strtab
= xmalloc (STRTAB_BLOCK
);
518 strtab_max
= STRTAB_BLOCK
;
522 offset
= sizeof (ehdr
);
523 if (pe_chdr
->optional_header_size
)
525 #if GRUB_TARGET_WORDSIZE == 64
526 struct grub_pe64_optional_header
*o
;
528 struct grub_pe32_optional_header
*o
;
530 o
= (void *) (pe_chdr
+ 1);
531 image_base
= o
->image_base
;
533 pe_shdr
= (struct grub_pe32_section_table
*) ((char *) (pe_chdr
+ 1) + pe_chdr
->optional_header_size
);
535 section_map
= write_section_data (fp
, name
, image
, pe_chdr
, pe_shdr
);
537 write_symbol_table (fp
, name
, image
, pe_chdr
, pe_shdr
, section_map
);
540 write_string_table (fp
, name
);
542 write_section_header (fp
, name
);
546 main (int argc
, char *argv
[])
555 fprintf (stderr
, "Filename not specified.\n");
564 image
= grub_util_read_image (in
);
566 fp
= grub_util_fopen (out
, "wb");
568 grub_util_error ("cannot open %s", out
);
570 convert_pe (fp
, out
, image
);