]> git.proxmox.com Git - grub2.git/blame - util/grub-pe2elf.c
* util/ieee1275/ofpath.c (grub_util_devname_to_ofpath): Use the
[grub2.git] / util / grub-pe2elf.c
CommitLineData
2a8a80e4 1/* grub-pe2elf.c - tool to convert pe image to elf. */
2/*
3 * GRUB -- GRand Unified Bootloader
8a4c07fd 4 * Copyright (C) 2008,2009 Free Software Foundation, Inc.
2a8a80e4 5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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
26#include <stdio.h>
27#include <unistd.h>
28#include <string.h>
29#include <stdlib.h>
30#include <getopt.h>
31
4760f979
CF
32#include "progname.h"
33
2a8a80e4 34static struct option options[] = {
35 {"help", no_argument, 0, 'h'},
36 {"version", no_argument, 0, 'V'},
37 {"verbose", no_argument, 0, 'v'},
38 {0, 0, 0, 0}
39};
40
41static void
42usage (int status)
43{
44 if (status)
70a14d3d 45 fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
2a8a80e4 46 else
47 printf ("\
8a4c07fd 48Usage: %s [OPTIONS] input [output]\n\
2a8a80e4 49\n\
50Tool to convert pe image to elf.\n\
51\nOptions:\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\
55\n\
8a4c07fd 56Report bugs to <%s>.\n", program_name, PACKAGE_BUGREPORT);
2a8a80e4 57
58 exit (status);
59}
60
61/*
62 * Section layout
63 *
64 * null
65 * .text
66 * .rdata
67 * .data
68 * .bss
69 * .modname
70 * .moddeps
71 * .symtab
72 * .strtab
73 * relocation sections
74 */
75
76#define TEXT_SECTION 1
77#define RDATA_SECTION 2
78#define DATA_SECTION 3
79#define BSS_SECTION 4
80#define MODNAME_SECTION 5
81#define MODDEPS_SECTION 6
82#define SYMTAB_SECTION 7
83#define STRTAB_SECTION 8
84
85#define REL_SECTION 9
86#define MAX_SECTIONS 12
87
88#define STRTAB_BLOCK 256
89
90static char *strtab;
91static int strtab_max, strtab_len;
92
93Elf32_Ehdr ehdr;
94Elf32_Shdr shdr[MAX_SECTIONS];
95int num_sections;
96grub_uint32_t offset;
97
98static int
99insert_string (char *name)
100{
101 int len, result;
102
103 if (*name == '_')
104 name++;
105
106 len = strlen (name);
107 if (strtab_len + len >= strtab_max)
108 {
109 strtab_max += STRTAB_BLOCK;
110 strtab = xrealloc (strtab, strtab_max);
111 }
112
113 strcpy (strtab + strtab_len, name);
114 result = strtab_len;
115 strtab_len += len + 1;
116
117 return result;
118}
119
120static int *
121write_section_data (FILE* fp, char *image,
122 struct grub_pe32_coff_header *pe_chdr,
123 struct grub_pe32_section_table *pe_shdr)
124{
125 int *section_map;
126 int i;
127
128 section_map = xmalloc ((pe_chdr->num_sections + 1) * sizeof (int));
129 section_map[0] = 0;
130
131 for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++)
132 {
133 grub_uint32_t idx;
134
135 if (! strcmp (pe_shdr->name, ".text"))
136 {
137 idx = TEXT_SECTION;
138 shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
139 }
140 else if (! strcmp (pe_shdr->name, ".rdata"))
141 {
142 idx = RDATA_SECTION;
143 shdr[idx].sh_flags = SHF_ALLOC;
144 }
145 else if (! strcmp (pe_shdr->name, ".data"))
146 {
147 idx = DATA_SECTION;
148 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
149 }
150 else if (! strcmp (pe_shdr->name, ".bss"))
151 {
152 idx = BSS_SECTION;
153 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
154 }
155 else if (! strcmp (pe_shdr->name, ".modname"))
156 idx = MODNAME_SECTION;
157 else if (! strcmp (pe_shdr->name, ".moddeps"))
158 idx = MODDEPS_SECTION;
159 else
160 {
161 section_map[i + 1] = -1;
162 continue;
163 }
164
165 section_map[i + 1] = idx;
166
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);
172
173 if (idx != BSS_SECTION)
174 {
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);
178
179 offset += pe_shdr->raw_data_size;
180 }
181
182 if (pe_shdr->relocations_offset)
183 {
184 char name[5 + strlen (pe_shdr->name)];
185
186 if (num_sections >= MAX_SECTIONS)
70a14d3d 187 grub_util_error ("too many sections");
2a8a80e4 188
189 sprintf (name, ".rel%s", pe_shdr->name);
190
191 shdr[num_sections].sh_name = insert_string (name);
192 shdr[num_sections].sh_link = i;
193 shdr[num_sections].sh_info = idx;
194
195 shdr[idx].sh_name = shdr[num_sections].sh_name + 4;
196
197 num_sections++;
198 }
199 else
200 shdr[idx].sh_name = insert_string (pe_shdr->name);
201 }
202
203 return section_map;
204}
205
206static void
207write_reloc_section (FILE* fp, char *image,
208 struct grub_pe32_coff_header *pe_chdr,
209 struct grub_pe32_section_table *pe_shdr,
210 Elf32_Sym *symtab,
211 int *symtab_map)
212{
213 int i;
214
215 for (i = REL_SECTION; i < num_sections; i++)
216 {
217 struct grub_pe32_section_table *pe_sec;
218 struct grub_pe32_reloc *pe_rel;
219 Elf32_Rel *rel;
220 int num_rels, j, modified;
221
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));
225 num_rels = 0;
226 modified = 0;
227
228 for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
229 {
230 int type;
231 grub_uint32_t ofs, *addr;
232
233 if ((pe_rel->symtab_index >= pe_chdr->num_symbols) ||
234 (symtab_map[pe_rel->symtab_index] == -1))
70a14d3d 235 grub_util_error ("invalid symbol");
2a8a80e4 236
237 if (pe_rel->type == GRUB_PE32_REL_I386_DIR32)
238 type = R_386_32;
239 else if (pe_rel->type == GRUB_PE32_REL_I386_REL32)
240 type = R_386_PC32;
241 else
70a14d3d 242 grub_util_error ("unknown pe relocation type %d\n", pe_rel->type);
2a8a80e4 243
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)
247 {
248 unsigned char code;
249
250 code = image[pe_sec->raw_data_offset + ofs - 1];
251
252 if (((code != 0xe8) && (code != 0xe9)) || (*addr))
70a14d3d 253 grub_util_error ("invalid relocation (%x %x)", code, *addr);
2a8a80e4 254
255 modified = 1;
256 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx)
257 {
258 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx
259 != shdr[i].sh_info)
70a14d3d 260 grub_util_error ("cross section call is not allowed");
2a8a80e4 261
262 *addr = (symtab[symtab_map[pe_rel->symtab_index]].st_value
263 - ofs - 4);
264
265 continue;
266 }
267 else
268 *addr = -4;
269 }
270
271 rel[num_rels].r_offset = ofs;
272 rel[num_rels].r_info = ELF32_R_INFO (symtab_map[pe_rel->symtab_index],
273 type);
274 num_rels++;
275 }
276
277 if (modified)
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,
281 fp);
282
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);
289
290 grub_util_write_image_at (rel, shdr[i].sh_size, offset, fp);
291 offset += shdr[i].sh_size;
292 free (rel);
293 }
294}
295
296static void
297write_symbol_table (FILE* fp, char *image,
298 struct grub_pe32_coff_header *pe_chdr,
299 struct grub_pe32_section_table *pe_shdr,
300 int *section_map)
301{
302 struct grub_pe32_symbol *pe_symtab;
303 char *pe_strtab;
304 Elf32_Sym *symtab;
305 int *symtab_map, num_syms;
306 int i;
307
308 pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
309 pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
310
311 symtab = (Elf32_Sym *) xmalloc ((pe_chdr->num_symbols + 1) *
312 sizeof (Elf32_Sym));
313 memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf32_Sym));
314 num_syms = 1;
315
316 symtab_map = (int *) xmalloc (pe_chdr->num_symbols * sizeof (int));
317
318 for (i = 0; i < (int) pe_chdr->num_symbols;
319 i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
320 {
321 int bind, type;
322
323 symtab_map[i] = -1;
324 if ((pe_symtab->section > pe_chdr->num_sections) ||
325 (section_map[pe_symtab->section] == -1))
326 continue;
327
328 if (! pe_symtab->section)
329 type = STT_NOTYPE;
330 else if (pe_symtab->type == GRUB_PE32_DT_FUNCTION)
331 type = STT_FUNC;
332 else
333 type = STT_OBJECT;
334
335 if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
336 bind = STB_GLOBAL;
337 else
338 bind = STB_LOCAL;
339
340 if ((type != STT_FUNC) && (pe_symtab->num_aux))
341 {
342 if (! pe_symtab->value)
343 type = STT_SECTION;
344
345 symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name;
346 }
347 else
348 {
33c846be 349 char short_name[9];
2a8a80e4 350 char *name;
351
33c846be 352 if (pe_symtab->long_name[0])
353 {
354 strncpy (short_name, pe_symtab->short_name, 8);
355 short_name[8] = 0;
356 name = short_name;
357 }
358 else
359 name = pe_strtab + pe_symtab->long_name[1];
2a8a80e4 360
361 if ((strcmp (name, "_grub_mod_init")) &&
362 (strcmp (name, "_grub_mod_fini")) &&
363 (bind == STB_LOCAL))
364 continue;
365
366 symtab[num_syms].st_name = insert_string (name);
367 }
368
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);
372
373 symtab_map[i] = num_syms;
374 num_syms++;
375 }
376
377 write_reloc_section (fp, image, pe_chdr, pe_shdr, symtab, symtab_map);
378
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;
386
387 grub_util_write_image_at (symtab, shdr[SYMTAB_SECTION].sh_size,
388 offset, fp);
389 offset += shdr[SYMTAB_SECTION].sh_size;
390
391 free (symtab);
392 free (symtab_map);
393}
394
395static void
396write_string_table (FILE* fp)
397{
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;
405
406 free (strtab);
407}
408
409static void
410write_section_header (FILE* fp)
411{
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;
419
420 ehdr.e_ident[EI_CLASS] = ELFCLASS32;
421 ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
422 ehdr.e_machine = EM_386;
423
424 ehdr.e_ehsize = sizeof (ehdr);
425 ehdr.e_shentsize = sizeof (Elf32_Shdr);
426 ehdr.e_shstrndx = STRTAB_SECTION;
427
428 ehdr.e_shoff = offset;
429 ehdr.e_shnum = num_sections;
430 grub_util_write_image_at (&shdr, sizeof (Elf32_Shdr) * num_sections,
431 offset, fp);
432
433 grub_util_write_image_at (&ehdr, sizeof (Elf32_Ehdr), 0, fp);
434}
435
436static void
437convert_pe (FILE* fp, char *image)
438{
439 struct grub_pe32_coff_header *pe_chdr;
440 struct grub_pe32_section_table *pe_shdr;
441 int *section_map;
442
443 pe_chdr = (struct grub_pe32_coff_header *) image;
444 if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE_I386)
70a14d3d 445 grub_util_error ("invalid coff image");
2a8a80e4 446
447 strtab = xmalloc (STRTAB_BLOCK);
448 strtab_max = STRTAB_BLOCK;
449 strtab[0] = 0;
450 strtab_len = 1;
451
452 offset = sizeof (ehdr);
453 pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1);
454 num_sections = REL_SECTION;
455
456 section_map = write_section_data (fp, image, pe_chdr, pe_shdr);
457
458 write_symbol_table (fp, image, pe_chdr, pe_shdr, section_map);
459 free (section_map);
460
461 write_string_table (fp);
462
463 write_section_header (fp);
464}
465
466int
467main (int argc, char *argv[])
468{
469 char *image;
470 FILE* fp;
471
8a4c07fd 472 set_program_name (argv[0]);
2a8a80e4 473
474 /* Check for options. */
475 while (1)
476 {
477 int c = getopt_long (argc, argv, "hVv", options, 0);
478
479 if (c == -1)
480 break;
481 else
482 switch (c)
483 {
484 case 'h':
485 usage (0);
486 break;
487
488 case 'V':
8a4c07fd 489 printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
2a8a80e4 490 return 0;
491
492 case 'v':
493 verbosity++;
494 break;
495
496 default:
497 usage (1);
498 break;
499 }
500 }
501
502 /* Obtain PATH. */
503 if (optind >= argc)
504 {
505 fprintf (stderr, "Filename not specified.\n");
506 usage (1);
507 }
508
509 image = grub_util_read_image (argv[optind]);
510
511 if (optind + 1 < argc)
512 optind++;
513
514 fp = fopen (argv[optind], "wb");
515 if (! fp)
516 grub_util_error ("cannot open %s", argv[optind]);
517
518 convert_pe (fp, image);
519
520 fclose (fp);
521
522 return 0;
523}