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