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
= xcalloc (2 * pe_chdr
->num_sections
+ 5, sizeof (int));
105 shdr
= xcalloc (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
*) xcalloc (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", 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
*) xcalloc (pe_chdr
->num_symbols
+ 1, sizeof (Elf_Sym
));
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)
379 if ((pe_symtab
->section
> pe_chdr
->num_sections
) ||
380 (section_map
[pe_symtab
->section
] == -1))
383 if (! pe_symtab
->section
)
385 else if (pe_symtab
->type
== GRUB_PE32_DT_FUNCTION
)
390 if (pe_symtab
->storage_class
== GRUB_PE32_SYM_CLASS_EXTERNAL
)
395 if ((pe_symtab
->type
!= GRUB_PE32_DT_FUNCTION
) && (pe_symtab
->num_aux
))
397 if (! pe_symtab
->value
)
400 symtab
[num_syms
].st_name
= shdr
[section_map
[pe_symtab
->section
]].sh_name
;
407 if (pe_symtab
->long_name
[0])
409 strncpy (short_name
, pe_symtab
->short_name
, 8);
411 symname
= short_name
;
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")) &&
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
;
434 write_reloc_section (fp
, name
, image
, pe_chdr
, pe_shdr
,
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
,
447 offset
+= shdr
[symtab_section
].sh_size
;
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
,
463 offset
+= strtab_len
;
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
;
484 ehdr
.e_ident
[EI_CLASS
] = ELFCLASS32
;
485 ehdr
.e_ident
[EI_DATA
] = ELFDATA2LSB
;
486 ehdr
.e_machine
= EM_386
;
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
,
497 grub_util_write_image_at (&ehdr
, sizeof (Elf_Ehdr
), 0, fp
, name
);
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
;
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));
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
;
520 offset
= sizeof (ehdr
);
521 if (pe_chdr
->optional_header_size
)
523 #if GRUB_TARGET_WORDSIZE == 64
524 struct grub_pe64_optional_header
*o
;
526 struct grub_pe32_optional_header
*o
;
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
);
538 write_string_table (fp
, name
);
540 write_section_header (fp
, name
);
544 main (int argc
, char *argv
[])
553 fprintf (stderr
, "Filename not specified.\n");
562 image
= grub_util_read_image (in
);
564 fp
= grub_util_fopen (out
, "wb");
566 grub_util_error ("cannot open %s", out
);
568 convert_pe (fp
, out
, image
);