]> git.proxmox.com Git - grub2.git/blob - util/grub-pe2elf.c
Initial integration of hints
[grub2.git] / util / grub-pe2elf.c
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.
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
32 #include "progname.h"
33
34 static 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
41 static void
42 usage (int status)
43 {
44 if (status)
45 fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
46 else
47 printf ("\
48 Usage: %s [OPTIONS] input [output]\n\
49 \n\
50 Tool 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\
56 Report bugs to <%s>.\n", program_name, PACKAGE_BUGREPORT);
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 MODLICENSE_SECTION 7
83 #define SYMTAB_SECTION 8
84 #define STRTAB_SECTION 9
85
86 #define REL_SECTION 10
87 #define MAX_SECTIONS 16
88
89 #define STRTAB_BLOCK 256
90
91 static char *strtab;
92 static int strtab_max, strtab_len;
93
94 Elf32_Ehdr ehdr;
95 Elf32_Shdr shdr[MAX_SECTIONS];
96 int num_sections;
97 grub_uint32_t offset;
98
99 static int
100 insert_string (const char *name)
101 {
102 int len, result;
103
104 if (*name == '_')
105 name++;
106
107 len = strlen (name);
108 if (strtab_len + len >= strtab_max)
109 {
110 strtab_max += STRTAB_BLOCK;
111 strtab = xrealloc (strtab, strtab_max);
112 }
113
114 strcpy (strtab + strtab_len, name);
115 result = strtab_len;
116 strtab_len += len + 1;
117
118 return result;
119 }
120
121 static int *
122 write_section_data (FILE* fp, char *image,
123 struct grub_pe32_coff_header *pe_chdr,
124 struct grub_pe32_section_table *pe_shdr)
125 {
126 int *section_map;
127 int i;
128 char *pe_strtab = (image + pe_chdr->symtab_offset
129 + pe_chdr->num_symbols * sizeof (struct grub_pe32_symbol));
130
131 section_map = xmalloc ((pe_chdr->num_sections + 1) * sizeof (int));
132 section_map[0] = 0;
133
134 for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++)
135 {
136 grub_uint32_t idx;
137 const char *name = pe_shdr->name;
138
139 if (name[0] == '/' && isdigit (name[1]))
140 {
141 char t[sizeof (pe_shdr->name) + 1];
142 memcpy (t, name, sizeof (pe_shdr->name));
143 t[sizeof (pe_shdr->name)] = 0;
144 name = pe_strtab + atoi (t + 1);
145 }
146
147 if (! strcmp (name, ".text"))
148 {
149 idx = TEXT_SECTION;
150 shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
151 }
152 else if (! strcmp (name, ".rdata"))
153 {
154 idx = RDATA_SECTION;
155 shdr[idx].sh_flags = SHF_ALLOC;
156 }
157 else if (! strcmp (name, ".data"))
158 {
159 idx = DATA_SECTION;
160 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
161 }
162 else if (! strcmp (name, ".bss"))
163 {
164 idx = BSS_SECTION;
165 shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
166 }
167 else if (! strcmp (name, ".modname"))
168 idx = MODNAME_SECTION;
169 else if (! strcmp (name, ".moddeps"))
170 idx = MODDEPS_SECTION;
171 else if (strcmp (name, ".module_license") == 0)
172 idx = MODLICENSE_SECTION;
173 else
174 {
175 section_map[i + 1] = -1;
176 continue;
177 }
178
179 section_map[i + 1] = idx;
180
181 shdr[idx].sh_type = (idx == BSS_SECTION) ? SHT_NOBITS : SHT_PROGBITS;
182 shdr[idx].sh_size = pe_shdr->raw_data_size;
183 shdr[idx].sh_addralign = 1 << (((pe_shdr->characteristics >>
184 GRUB_PE32_SCN_ALIGN_SHIFT) &
185 GRUB_PE32_SCN_ALIGN_MASK) - 1);
186
187 if (idx != BSS_SECTION)
188 {
189 shdr[idx].sh_offset = offset;
190 grub_util_write_image_at (image + pe_shdr->raw_data_offset,
191 pe_shdr->raw_data_size, offset, fp);
192
193 offset += pe_shdr->raw_data_size;
194 }
195
196 if (pe_shdr->relocations_offset)
197 {
198 char relname[5 + strlen (name)];
199
200 if (num_sections >= MAX_SECTIONS)
201 grub_util_error ("too many sections");
202
203 sprintf (relname, ".rel%s", name);
204
205 shdr[num_sections].sh_name = insert_string (relname);
206 shdr[num_sections].sh_link = i;
207 shdr[num_sections].sh_info = idx;
208
209 shdr[idx].sh_name = shdr[num_sections].sh_name + 4;
210
211 num_sections++;
212 }
213 else
214 shdr[idx].sh_name = insert_string (name);
215 }
216
217 return section_map;
218 }
219
220 static void
221 write_reloc_section (FILE* fp, char *image,
222 struct grub_pe32_coff_header *pe_chdr,
223 struct grub_pe32_section_table *pe_shdr,
224 Elf32_Sym *symtab,
225 int *symtab_map)
226 {
227 int i;
228
229 for (i = REL_SECTION; i < num_sections; i++)
230 {
231 struct grub_pe32_section_table *pe_sec;
232 struct grub_pe32_reloc *pe_rel;
233 Elf32_Rel *rel;
234 int num_rels, j, modified;
235
236 pe_sec = pe_shdr + shdr[i].sh_link;
237 pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
238 rel = (Elf32_Rel *) xmalloc (pe_sec->num_relocations * sizeof (Elf32_Rel));
239 num_rels = 0;
240 modified = 0;
241
242 for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
243 {
244 int type;
245 grub_uint32_t ofs, *addr;
246
247 if ((pe_rel->symtab_index >= pe_chdr->num_symbols) ||
248 (symtab_map[pe_rel->symtab_index] == -1))
249 grub_util_error ("invalid symbol");
250
251 if (pe_rel->type == GRUB_PE32_REL_I386_DIR32)
252 type = R_386_32;
253 else if (pe_rel->type == GRUB_PE32_REL_I386_REL32)
254 type = R_386_PC32;
255 else
256 grub_util_error ("unknown pe relocation type %d\n", pe_rel->type);
257
258 ofs = pe_rel->offset - pe_sec->virtual_address;
259 addr = (grub_uint32_t *)(image + pe_sec->raw_data_offset + ofs);
260 if (type == R_386_PC32)
261 {
262 unsigned char code;
263
264 code = image[pe_sec->raw_data_offset + ofs - 1];
265
266 if (((code != 0xe8) && (code != 0xe9)) || (*addr))
267 grub_util_error ("invalid relocation (%x %x)", code, *addr);
268
269 modified = 1;
270 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx)
271 {
272 if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx
273 != shdr[i].sh_info)
274 grub_util_error ("cross section call is not allowed");
275
276 *addr = (symtab[symtab_map[pe_rel->symtab_index]].st_value
277 - ofs - 4);
278
279 continue;
280 }
281 else
282 *addr = -4;
283 }
284
285 rel[num_rels].r_offset = ofs;
286 rel[num_rels].r_info = ELF32_R_INFO (symtab_map[pe_rel->symtab_index],
287 type);
288 num_rels++;
289 }
290
291 if (modified)
292 grub_util_write_image_at (image + pe_sec->raw_data_offset,
293 shdr[shdr[i].sh_info].sh_size,
294 shdr[shdr[i].sh_info].sh_offset,
295 fp);
296
297 shdr[i].sh_type = SHT_REL;
298 shdr[i].sh_offset = offset;
299 shdr[i].sh_link = SYMTAB_SECTION;
300 shdr[i].sh_addralign = 4;
301 shdr[i].sh_entsize = sizeof (Elf32_Rel);
302 shdr[i].sh_size = num_rels * sizeof (Elf32_Rel);
303
304 grub_util_write_image_at (rel, shdr[i].sh_size, offset, fp);
305 offset += shdr[i].sh_size;
306 free (rel);
307 }
308 }
309
310 static void
311 write_symbol_table (FILE* fp, char *image,
312 struct grub_pe32_coff_header *pe_chdr,
313 struct grub_pe32_section_table *pe_shdr,
314 int *section_map)
315 {
316 struct grub_pe32_symbol *pe_symtab;
317 char *pe_strtab;
318 Elf32_Sym *symtab;
319 int *symtab_map, num_syms;
320 int i;
321
322 pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
323 pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
324
325 symtab = (Elf32_Sym *) xmalloc ((pe_chdr->num_symbols + 1) *
326 sizeof (Elf32_Sym));
327 memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf32_Sym));
328 num_syms = 1;
329
330 symtab_map = (int *) xmalloc (pe_chdr->num_symbols * sizeof (int));
331
332 for (i = 0; i < (int) pe_chdr->num_symbols;
333 i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
334 {
335 int bind, type;
336
337 symtab_map[i] = -1;
338 if ((pe_symtab->section > pe_chdr->num_sections) ||
339 (section_map[pe_symtab->section] == -1))
340 continue;
341
342 if (! pe_symtab->section)
343 type = STT_NOTYPE;
344 else if (pe_symtab->type == GRUB_PE32_DT_FUNCTION)
345 type = STT_FUNC;
346 else
347 type = STT_OBJECT;
348
349 if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
350 bind = STB_GLOBAL;
351 else
352 bind = STB_LOCAL;
353
354 if ((pe_symtab->type != GRUB_PE32_DT_FUNCTION) && (pe_symtab->num_aux))
355 {
356 if (! pe_symtab->value)
357 type = STT_SECTION;
358
359 symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name;
360 }
361 else
362 {
363 char short_name[9];
364 char *name;
365
366 if (pe_symtab->long_name[0])
367 {
368 strncpy (short_name, pe_symtab->short_name, 8);
369 short_name[8] = 0;
370 name = short_name;
371 }
372 else
373 name = pe_strtab + pe_symtab->long_name[1];
374
375 if ((strcmp (name, "_grub_mod_init")) &&
376 (strcmp (name, "_grub_mod_fini")) &&
377 (bind == STB_LOCAL))
378 continue;
379
380 symtab[num_syms].st_name = insert_string (name);
381 }
382
383 symtab[num_syms].st_shndx = section_map[pe_symtab->section];
384 symtab[num_syms].st_value = pe_symtab->value;
385 symtab[num_syms].st_info = ELF32_ST_INFO (bind, type);
386
387 symtab_map[i] = num_syms;
388 num_syms++;
389 }
390
391 write_reloc_section (fp, image, pe_chdr, pe_shdr, symtab, symtab_map);
392
393 shdr[SYMTAB_SECTION].sh_name = insert_string (".symtab");
394 shdr[SYMTAB_SECTION].sh_type = SHT_SYMTAB;
395 shdr[SYMTAB_SECTION].sh_offset = offset;
396 shdr[SYMTAB_SECTION].sh_size = num_syms * sizeof (Elf32_Sym);
397 shdr[SYMTAB_SECTION].sh_entsize = sizeof (Elf32_Sym);
398 shdr[SYMTAB_SECTION].sh_link = STRTAB_SECTION;
399 shdr[SYMTAB_SECTION].sh_addralign = 4;
400
401 grub_util_write_image_at (symtab, shdr[SYMTAB_SECTION].sh_size,
402 offset, fp);
403 offset += shdr[SYMTAB_SECTION].sh_size;
404
405 free (symtab);
406 free (symtab_map);
407 }
408
409 static void
410 write_string_table (FILE* fp)
411 {
412 shdr[STRTAB_SECTION].sh_name = insert_string (".strtab");
413 shdr[STRTAB_SECTION].sh_type = SHT_STRTAB;
414 shdr[STRTAB_SECTION].sh_offset = offset;
415 shdr[STRTAB_SECTION].sh_size = strtab_len;
416 shdr[STRTAB_SECTION].sh_addralign = 1;
417 grub_util_write_image_at (strtab, strtab_len, offset, fp);
418 offset += strtab_len;
419
420 free (strtab);
421 }
422
423 static void
424 write_section_header (FILE* fp)
425 {
426 ehdr.e_ident[EI_MAG0] = ELFMAG0;
427 ehdr.e_ident[EI_MAG1] = ELFMAG1;
428 ehdr.e_ident[EI_MAG2] = ELFMAG2;
429 ehdr.e_ident[EI_MAG3] = ELFMAG3;
430 ehdr.e_ident[EI_VERSION] = EV_CURRENT;
431 ehdr.e_version = EV_CURRENT;
432 ehdr.e_type = ET_REL;
433
434 ehdr.e_ident[EI_CLASS] = ELFCLASS32;
435 ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
436 ehdr.e_machine = EM_386;
437
438 ehdr.e_ehsize = sizeof (ehdr);
439 ehdr.e_shentsize = sizeof (Elf32_Shdr);
440 ehdr.e_shstrndx = STRTAB_SECTION;
441
442 ehdr.e_shoff = offset;
443 ehdr.e_shnum = num_sections;
444 grub_util_write_image_at (&shdr, sizeof (Elf32_Shdr) * num_sections,
445 offset, fp);
446
447 grub_util_write_image_at (&ehdr, sizeof (Elf32_Ehdr), 0, fp);
448 }
449
450 static void
451 convert_pe (FILE* fp, char *image)
452 {
453 struct grub_pe32_coff_header *pe_chdr;
454 struct grub_pe32_section_table *pe_shdr;
455 int *section_map;
456
457 pe_chdr = (struct grub_pe32_coff_header *) image;
458 if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE_I386)
459 grub_util_error ("invalid coff image");
460
461 strtab = xmalloc (STRTAB_BLOCK);
462 strtab_max = STRTAB_BLOCK;
463 strtab[0] = 0;
464 strtab_len = 1;
465
466 offset = sizeof (ehdr);
467 pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1);
468 num_sections = REL_SECTION;
469
470 section_map = write_section_data (fp, image, pe_chdr, pe_shdr);
471
472 write_symbol_table (fp, image, pe_chdr, pe_shdr, section_map);
473 free (section_map);
474
475 write_string_table (fp);
476
477 write_section_header (fp);
478 }
479
480 int
481 main (int argc, char *argv[])
482 {
483 char *image;
484 FILE* fp;
485
486 set_program_name (argv[0]);
487
488 /* Check for options. */
489 while (1)
490 {
491 int c = getopt_long (argc, argv, "hVv", options, 0);
492
493 if (c == -1)
494 break;
495 else
496 switch (c)
497 {
498 case 'h':
499 usage (0);
500 break;
501
502 case 'V':
503 printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
504 return 0;
505
506 case 'v':
507 verbosity++;
508 break;
509
510 default:
511 usage (1);
512 break;
513 }
514 }
515
516 /* Obtain PATH. */
517 if (optind >= argc)
518 {
519 fprintf (stderr, "Filename not specified.\n");
520 usage (1);
521 }
522
523 image = grub_util_read_image (argv[optind]);
524
525 if (optind + 1 < argc)
526 optind++;
527
528 fp = fopen (argv[optind], "wb");
529 if (! fp)
530 grub_util_error ("cannot open %s", argv[optind]);
531
532 convert_pe (fp, image);
533
534 fclose (fp);
535
536 return 0;
537 }