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>
34 static struct option options
[] = {
35 {"help", no_argument
, 0, 'h'},
36 {"version", no_argument
, 0, 'V'},
37 {"verbose", no_argument
, 0, 'v'},
45 fprintf (stderr
, "Try `%s --help' for more information.\n", program_name
);
48 Usage: %s [OPTIONS] input [output]\n\
50 Tool to convert pe image to elf.\n\
52 -h, --help display this message and exit\n\
53 -V, --version print version information and exit\n\
54 -v, --verbose print verbose messages\n\
56 Report bugs to <%s>.\n", program_name
, PACKAGE_BUGREPORT
);
76 #define TEXT_SECTION 1
77 #define RDATA_SECTION 2
78 #define DATA_SECTION 3
80 #define MODNAME_SECTION 5
81 #define MODDEPS_SECTION 6
82 #define SYMTAB_SECTION 7
83 #define STRTAB_SECTION 8
86 #define MAX_SECTIONS 12
88 #define STRTAB_BLOCK 256
91 static int strtab_max
, strtab_len
;
94 Elf32_Shdr shdr
[MAX_SECTIONS
];
99 insert_string (char *name
)
107 if (strtab_len
+ len
>= strtab_max
)
109 strtab_max
+= STRTAB_BLOCK
;
110 strtab
= xrealloc (strtab
, strtab_max
);
113 strcpy (strtab
+ strtab_len
, name
);
115 strtab_len
+= len
+ 1;
121 write_section_data (FILE* fp
, char *image
,
122 struct grub_pe32_coff_header
*pe_chdr
,
123 struct grub_pe32_section_table
*pe_shdr
)
128 section_map
= xmalloc ((pe_chdr
->num_sections
+ 1) * sizeof (int));
131 for (i
= 0; i
< pe_chdr
->num_sections
; i
++, pe_shdr
++)
135 if (! strcmp (pe_shdr
->name
, ".text"))
138 shdr
[idx
].sh_flags
= SHF_ALLOC
| SHF_EXECINSTR
;
140 else if (! strcmp (pe_shdr
->name
, ".rdata"))
143 shdr
[idx
].sh_flags
= SHF_ALLOC
;
145 else if (! strcmp (pe_shdr
->name
, ".data"))
148 shdr
[idx
].sh_flags
= SHF_ALLOC
| SHF_WRITE
;
150 else if (! strcmp (pe_shdr
->name
, ".bss"))
153 shdr
[idx
].sh_flags
= SHF_ALLOC
| SHF_WRITE
;
155 else if (! strcmp (pe_shdr
->name
, ".modname"))
156 idx
= MODNAME_SECTION
;
157 else if (! strcmp (pe_shdr
->name
, ".moddeps"))
158 idx
= MODDEPS_SECTION
;
161 section_map
[i
+ 1] = -1;
165 section_map
[i
+ 1] = idx
;
167 shdr
[idx
].sh_type
= (idx
== BSS_SECTION
) ? SHT_NOBITS
: SHT_PROGBITS
;
168 shdr
[idx
].sh_size
= pe_shdr
->raw_data_size
;
169 shdr
[idx
].sh_addralign
= 1 << (((pe_shdr
->characteristics
>>
170 GRUB_PE32_SCN_ALIGN_SHIFT
) &
171 GRUB_PE32_SCN_ALIGN_MASK
) - 1);
173 if (idx
!= BSS_SECTION
)
175 shdr
[idx
].sh_offset
= offset
;
176 grub_util_write_image_at (image
+ pe_shdr
->raw_data_offset
,
177 pe_shdr
->raw_data_size
, offset
, fp
);
179 offset
+= pe_shdr
->raw_data_size
;
182 if (pe_shdr
->relocations_offset
)
184 char name
[5 + strlen (pe_shdr
->name
)];
186 if (num_sections
>= MAX_SECTIONS
)
187 grub_util_error ("too many sections");
189 sprintf (name
, ".rel%s", pe_shdr
->name
);
191 shdr
[num_sections
].sh_name
= insert_string (name
);
192 shdr
[num_sections
].sh_link
= i
;
193 shdr
[num_sections
].sh_info
= idx
;
195 shdr
[idx
].sh_name
= shdr
[num_sections
].sh_name
+ 4;
200 shdr
[idx
].sh_name
= insert_string (pe_shdr
->name
);
207 write_reloc_section (FILE* fp
, char *image
,
208 struct grub_pe32_coff_header
*pe_chdr
,
209 struct grub_pe32_section_table
*pe_shdr
,
215 for (i
= REL_SECTION
; i
< num_sections
; i
++)
217 struct grub_pe32_section_table
*pe_sec
;
218 struct grub_pe32_reloc
*pe_rel
;
220 int num_rels
, j
, modified
;
222 pe_sec
= pe_shdr
+ shdr
[i
].sh_link
;
223 pe_rel
= (struct grub_pe32_reloc
*) (image
+ pe_sec
->relocations_offset
);
224 rel
= (Elf32_Rel
*) xmalloc (pe_sec
->num_relocations
* sizeof (Elf32_Rel
));
228 for (j
= 0; j
< pe_sec
->num_relocations
; j
++, pe_rel
++)
231 grub_uint32_t ofs
, *addr
;
233 if ((pe_rel
->symtab_index
>= pe_chdr
->num_symbols
) ||
234 (symtab_map
[pe_rel
->symtab_index
] == -1))
235 grub_util_error ("invalid symbol");
237 if (pe_rel
->type
== GRUB_PE32_REL_I386_DIR32
)
239 else if (pe_rel
->type
== GRUB_PE32_REL_I386_REL32
)
242 grub_util_error ("unknown pe relocation type %d\n", pe_rel
->type
);
244 ofs
= pe_rel
->offset
- pe_sec
->virtual_address
;
245 addr
= (grub_uint32_t
*)(image
+ pe_sec
->raw_data_offset
+ ofs
);
246 if (type
== R_386_PC32
)
250 code
= image
[pe_sec
->raw_data_offset
+ ofs
- 1];
252 if (((code
!= 0xe8) && (code
!= 0xe9)) || (*addr
))
253 grub_util_error ("invalid relocation (%x %x)", code
, *addr
);
256 if (symtab
[symtab_map
[pe_rel
->symtab_index
]].st_shndx
)
258 if (symtab
[symtab_map
[pe_rel
->symtab_index
]].st_shndx
260 grub_util_error ("cross section call is not allowed");
262 *addr
= (symtab
[symtab_map
[pe_rel
->symtab_index
]].st_value
271 rel
[num_rels
].r_offset
= ofs
;
272 rel
[num_rels
].r_info
= ELF32_R_INFO (symtab_map
[pe_rel
->symtab_index
],
278 grub_util_write_image_at (image
+ pe_sec
->raw_data_offset
,
279 shdr
[shdr
[i
].sh_info
].sh_size
,
280 shdr
[shdr
[i
].sh_info
].sh_offset
,
283 shdr
[i
].sh_type
= SHT_REL
;
284 shdr
[i
].sh_offset
= offset
;
285 shdr
[i
].sh_link
= SYMTAB_SECTION
;
286 shdr
[i
].sh_addralign
= 4;
287 shdr
[i
].sh_entsize
= sizeof (Elf32_Rel
);
288 shdr
[i
].sh_size
= num_rels
* sizeof (Elf32_Rel
);
290 grub_util_write_image_at (rel
, shdr
[i
].sh_size
, offset
, fp
);
291 offset
+= shdr
[i
].sh_size
;
297 write_symbol_table (FILE* fp
, char *image
,
298 struct grub_pe32_coff_header
*pe_chdr
,
299 struct grub_pe32_section_table
*pe_shdr
,
302 struct grub_pe32_symbol
*pe_symtab
;
305 int *symtab_map
, num_syms
;
308 pe_symtab
= (struct grub_pe32_symbol
*) (image
+ pe_chdr
->symtab_offset
);
309 pe_strtab
= (char *) (pe_symtab
+ pe_chdr
->num_symbols
);
311 symtab
= (Elf32_Sym
*) xmalloc ((pe_chdr
->num_symbols
+ 1) *
313 memset (symtab
, 0, (pe_chdr
->num_symbols
+ 1) * sizeof (Elf32_Sym
));
316 symtab_map
= (int *) xmalloc (pe_chdr
->num_symbols
* sizeof (int));
318 for (i
= 0; i
< (int) pe_chdr
->num_symbols
;
319 i
+= pe_symtab
->num_aux
+ 1, pe_symtab
+= pe_symtab
->num_aux
+ 1)
324 if ((pe_symtab
->section
> pe_chdr
->num_sections
) ||
325 (section_map
[pe_symtab
->section
] == -1))
328 if (! pe_symtab
->section
)
330 else if (pe_symtab
->type
== GRUB_PE32_DT_FUNCTION
)
335 if (pe_symtab
->storage_class
== GRUB_PE32_SYM_CLASS_EXTERNAL
)
340 if ((type
!= STT_FUNC
) && (pe_symtab
->num_aux
))
342 if (! pe_symtab
->value
)
345 symtab
[num_syms
].st_name
= shdr
[section_map
[pe_symtab
->section
]].sh_name
;
352 if (pe_symtab
->long_name
[0])
354 strncpy (short_name
, pe_symtab
->short_name
, 8);
359 name
= pe_strtab
+ pe_symtab
->long_name
[1];
361 if ((strcmp (name
, "_grub_mod_init")) &&
362 (strcmp (name
, "_grub_mod_fini")) &&
366 symtab
[num_syms
].st_name
= insert_string (name
);
369 symtab
[num_syms
].st_shndx
= section_map
[pe_symtab
->section
];
370 symtab
[num_syms
].st_value
= pe_symtab
->value
;
371 symtab
[num_syms
].st_info
= ELF32_ST_INFO (bind
, type
);
373 symtab_map
[i
] = num_syms
;
377 write_reloc_section (fp
, image
, pe_chdr
, pe_shdr
, symtab
, symtab_map
);
379 shdr
[SYMTAB_SECTION
].sh_name
= insert_string (".symtab");
380 shdr
[SYMTAB_SECTION
].sh_type
= SHT_SYMTAB
;
381 shdr
[SYMTAB_SECTION
].sh_offset
= offset
;
382 shdr
[SYMTAB_SECTION
].sh_size
= num_syms
* sizeof (Elf32_Sym
);
383 shdr
[SYMTAB_SECTION
].sh_entsize
= sizeof (Elf32_Sym
);
384 shdr
[SYMTAB_SECTION
].sh_link
= STRTAB_SECTION
;
385 shdr
[SYMTAB_SECTION
].sh_addralign
= 4;
387 grub_util_write_image_at (symtab
, shdr
[SYMTAB_SECTION
].sh_size
,
389 offset
+= shdr
[SYMTAB_SECTION
].sh_size
;
396 write_string_table (FILE* fp
)
398 shdr
[STRTAB_SECTION
].sh_name
= insert_string (".strtab");
399 shdr
[STRTAB_SECTION
].sh_type
= SHT_STRTAB
;
400 shdr
[STRTAB_SECTION
].sh_offset
= offset
;
401 shdr
[STRTAB_SECTION
].sh_size
= strtab_len
;
402 shdr
[STRTAB_SECTION
].sh_addralign
= 1;
403 grub_util_write_image_at (strtab
, strtab_len
, offset
, fp
);
404 offset
+= strtab_len
;
410 write_section_header (FILE* fp
)
412 ehdr
.e_ident
[EI_MAG0
] = ELFMAG0
;
413 ehdr
.e_ident
[EI_MAG1
] = ELFMAG1
;
414 ehdr
.e_ident
[EI_MAG2
] = ELFMAG2
;
415 ehdr
.e_ident
[EI_MAG3
] = ELFMAG3
;
416 ehdr
.e_ident
[EI_VERSION
] = EV_CURRENT
;
417 ehdr
.e_version
= EV_CURRENT
;
418 ehdr
.e_type
= ET_REL
;
420 ehdr
.e_ident
[EI_CLASS
] = ELFCLASS32
;
421 ehdr
.e_ident
[EI_DATA
] = ELFDATA2LSB
;
422 ehdr
.e_machine
= EM_386
;
424 ehdr
.e_ehsize
= sizeof (ehdr
);
425 ehdr
.e_shentsize
= sizeof (Elf32_Shdr
);
426 ehdr
.e_shstrndx
= STRTAB_SECTION
;
428 ehdr
.e_shoff
= offset
;
429 ehdr
.e_shnum
= num_sections
;
430 grub_util_write_image_at (&shdr
, sizeof (Elf32_Shdr
) * num_sections
,
433 grub_util_write_image_at (&ehdr
, sizeof (Elf32_Ehdr
), 0, fp
);
437 convert_pe (FILE* fp
, char *image
)
439 struct grub_pe32_coff_header
*pe_chdr
;
440 struct grub_pe32_section_table
*pe_shdr
;
443 pe_chdr
= (struct grub_pe32_coff_header
*) image
;
444 if (grub_le_to_cpu16 (pe_chdr
->machine
) != GRUB_PE32_MACHINE_I386
)
445 grub_util_error ("invalid coff image");
447 strtab
= xmalloc (STRTAB_BLOCK
);
448 strtab_max
= STRTAB_BLOCK
;
452 offset
= sizeof (ehdr
);
453 pe_shdr
= (struct grub_pe32_section_table
*) (pe_chdr
+ 1);
454 num_sections
= REL_SECTION
;
456 section_map
= write_section_data (fp
, image
, pe_chdr
, pe_shdr
);
458 write_symbol_table (fp
, image
, pe_chdr
, pe_shdr
, section_map
);
461 write_string_table (fp
);
463 write_section_header (fp
);
467 main (int argc
, char *argv
[])
472 set_program_name (argv
[0]);
474 /* Check for options. */
477 int c
= getopt_long (argc
, argv
, "hVv", options
, 0);
489 printf ("%s (%s) %s\n", program_name
, PACKAGE_NAME
, PACKAGE_VERSION
);
505 fprintf (stderr
, "Filename not specified.\n");
509 image
= grub_util_read_image (argv
[optind
]);
511 if (optind
+ 1 < argc
)
514 fp
= fopen (argv
[optind
], "wb");
516 grub_util_error ("cannot open %s", argv
[optind
]);
518 convert_pe (fp
, image
);