123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- /* grub-pe2elf.c - tool to convert pe image to elf. */
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2008,2009 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <config.h>
- #include <grub/types.h>
- #include <grub/util/misc.h>
- #include <grub/elf.h>
- #include <grub/efi/pe32.h>
- #include <grub/misc.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- #include <getopt.h>
- /* Please don't internationalise this file. It's pointless. */
- /*
- * Section layout
- *
- * null
- * .text
- * .rdata
- * .data
- * .bss
- * .modname
- * .moddeps
- * .symtab
- * .strtab
- * relocation sections
- */
- #if GRUB_TARGET_WORDSIZE == 64
- typedef Elf64_Rela elf_reloc_t;
- #define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_X86_64
- #else
- typedef Elf32_Rel elf_reloc_t;
- #define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_I386
- #endif
- #define STRTAB_BLOCK 256
- static char *strtab;
- static int strtab_max, strtab_len;
- static Elf_Ehdr ehdr;
- static Elf_Shdr *shdr;
- static int num_sections, first_reloc_section, reloc_sections_end, symtab_section, strtab_section;
- static grub_uint32_t offset, image_base;
- static int
- insert_string (const char *name)
- {
- int len, result;
- if (*name == '_')
- name++;
- len = strlen (name);
- if (strtab_len + len >= strtab_max)
- {
- strtab_max += STRTAB_BLOCK;
- strtab = xrealloc (strtab, strtab_max);
- }
- strcpy (strtab + strtab_len, name);
- result = strtab_len;
- strtab_len += len + 1;
- return result;
- }
- static int *
- write_section_data (FILE* fp, const char *name, char *image,
- struct grub_pe32_coff_header *pe_chdr,
- struct grub_pe32_section_table *pe_shdr)
- {
- int *section_map;
- int i;
- grub_uint32_t last_category = 0;
- grub_uint32_t idx, idx_reloc;
- char *pe_strtab = (image + pe_chdr->symtab_offset
- + pe_chdr->num_symbols * sizeof (struct grub_pe32_symbol));
- section_map = xmalloc ((2 * pe_chdr->num_sections + 5) * sizeof (int));
- section_map[0] = 0;
- shdr = xmalloc ((2 * pe_chdr->num_sections + 5) * sizeof (shdr[0]));
- idx = 1;
- idx_reloc = pe_chdr->num_sections + 1;
- for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++)
- {
- grub_uint32_t category;
- const char *shname = pe_shdr->name;
- grub_size_t secsize;
- if (shname[0] == '/' && grub_isdigit (shname[1]))
- {
- char t[sizeof (pe_shdr->name) + 1];
- memcpy (t, shname, sizeof (pe_shdr->name));
- t[sizeof (pe_shdr->name)] = 0;
- shname = pe_strtab + atoi (t + 1);
- }
- secsize = pe_shdr->raw_data_size;
- shdr[idx].sh_type = SHT_PROGBITS;
- if (! strcmp (shname, ".text"))
- {
- category = 0;
- shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
- }
- else if (! strncmp (shname, ".rdata", 6))
- {
- category = 1;
- shdr[idx].sh_flags = SHF_ALLOC;
- }
- else if (! strcmp (shname, ".data"))
- {
- category = 2;
- shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
- }
- else if (! strcmp (shname, ".bss"))
- {
- category = 3;
- shdr[idx].sh_type = SHT_NOBITS;
- shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
- if (secsize < pe_shdr->virtual_size)
- secsize = pe_shdr->virtual_size;
- }
- else if (strcmp (shname, ".modname") == 0 || strcmp (shname, ".moddeps") == 0
- || strcmp (shname, ".module_license") == 0)
- category = 4;
- else
- {
- section_map[i + 1] = -1;
- continue;
- }
- if (category < last_category)
- grub_util_error ("out of order sections");
- section_map[i + 1] = idx;
- if (pe_shdr->virtual_size
- && pe_shdr->virtual_size < secsize)
- secsize = pe_shdr->virtual_size;
- shdr[idx].sh_size = secsize;
- shdr[idx].sh_addralign = 1 << (((pe_shdr->characteristics >>
- GRUB_PE32_SCN_ALIGN_SHIFT) &
- GRUB_PE32_SCN_ALIGN_MASK) - 1);
- shdr[idx].sh_addr = pe_shdr->virtual_address + image_base;
- if (shdr[idx].sh_type != SHT_NOBITS)
- {
- shdr[idx].sh_offset = offset;
- grub_util_write_image_at (image + pe_shdr->raw_data_offset,
- pe_shdr->raw_data_size, offset, fp,
- shname);
- offset += secsize;
- }
- if (pe_shdr->relocations_offset)
- {
- char relname[5 + strlen (shname)];
- sprintf (relname, ".rel%s", shname);
- shdr[idx_reloc].sh_name = insert_string (relname);
- shdr[idx_reloc].sh_link = i;
- shdr[idx_reloc].sh_info = idx;
- shdr[idx].sh_name = shdr[idx_reloc].sh_name + 4;
- idx_reloc++;
- }
- else
- shdr[idx].sh_name = insert_string (shname);
- idx++;
- }
- idx_reloc -= pe_chdr->num_sections + 1;
- num_sections = idx + idx_reloc + 2;
- first_reloc_section = idx;
- reloc_sections_end = idx + idx_reloc;
- memmove (shdr + idx, shdr + pe_chdr->num_sections + 1,
- idx_reloc * sizeof (shdr[0]));
- memset (shdr + idx + idx_reloc, 0, 3 * sizeof (shdr[0]));
- memset (shdr, 0, sizeof (shdr[0]));
- symtab_section = idx + idx_reloc;
- strtab_section = idx + idx_reloc + 1;
- return section_map;
- }
- static void
- write_reloc_section (FILE* fp, const char *name, char *image,
- struct grub_pe32_coff_header *pe_chdr,
- struct grub_pe32_section_table *pe_shdr,
- Elf_Sym *symtab,
- int *symtab_map)
- {
- int i;
- for (i = first_reloc_section; i < reloc_sections_end; i++)
- {
- struct grub_pe32_section_table *pe_sec;
- struct grub_pe32_reloc *pe_rel;
- elf_reloc_t *rel;
- int num_rels, j, modified;
- pe_sec = pe_shdr + shdr[i].sh_link;
- pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
- rel = (elf_reloc_t *) xmalloc (pe_sec->num_relocations * sizeof (elf_reloc_t));
- num_rels = 0;
- modified = 0;
- for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
- {
- int type;
- grub_uint32_t ofs, *addr;
- if ((pe_rel->symtab_index >= pe_chdr->num_symbols) ||
- (symtab_map[pe_rel->symtab_index] == -1))
- grub_util_error ("invalid symbol");
- ofs = pe_rel->offset - pe_sec->virtual_address;
- addr = (grub_uint32_t *)(image + pe_sec->raw_data_offset + ofs);
- switch (pe_rel->type)
- {
- #if GRUB_TARGET_WORDSIZE == 64
- case 1:
- type = R_X86_64_64;
- rel[num_rels].r_addend = *(grub_int64_t *)addr;
- *(grub_int64_t *)addr = 0;
- modified = 1;
- break;
- case 4:
- type = R_X86_64_PC32;
- rel[num_rels].r_addend = *(grub_int32_t *)addr;
- *addr = 0;
- modified = 1;
- break;
- case 14:
- type = R_X86_64_PC64;
- rel[num_rels].r_addend = *(grub_uint64_t *)addr - 8;
- *(grub_uint64_t *)addr = 0;
- modified = 1;
- break;
- #else
- case GRUB_PE32_REL_I386_DIR32:
- type = R_386_32;
- break;
- case GRUB_PE32_REL_I386_REL32:
- type = R_386_PC32;
- break;
- #endif
- default:
- grub_util_error ("unknown pe relocation type %d", pe_rel->type);
- }
- if (type ==
- #if GRUB_TARGET_WORDSIZE == 64
- R_386_PC32
- #else
- R_X86_64_PC32
- #endif
- )
- {
- unsigned char code;
- code = image[pe_sec->raw_data_offset + ofs - 1];
- #if GRUB_TARGET_WORDSIZE == 32
- if (((code != 0xe8) && (code != 0xe9)) || (*addr))
- grub_util_error ("invalid relocation (%x %x)", code, *addr);
- #endif
- if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx
- && symtab[symtab_map[pe_rel->symtab_index]].st_shndx
- == shdr[i].sh_info)
- {
- modified = 1;
- *addr += (symtab[symtab_map[pe_rel->symtab_index]].st_value
- - ofs - 4);
- continue;
- }
- else
- {
- #if GRUB_TARGET_WORDSIZE == 64
- rel[num_rels].r_addend -= 4;
- #else
- modified = 1;
- *addr = -4;
- #endif
- }
- }
- rel[num_rels].r_offset = ofs;
- rel[num_rels].r_info = ELF_R_INFO (symtab_map[pe_rel->symtab_index],
- type);
- num_rels++;
- }
- if (modified)
- grub_util_write_image_at (image + pe_sec->raw_data_offset,
- shdr[shdr[i].sh_info].sh_size,
- shdr[shdr[i].sh_info].sh_offset,
- fp, name);
- #if GRUB_TARGET_WORDSIZE == 64
- shdr[i].sh_type = SHT_RELA;
- #else
- shdr[i].sh_type = SHT_REL;
- #endif
- shdr[i].sh_offset = offset;
- shdr[i].sh_link = symtab_section;
- shdr[i].sh_addralign = 4;
- shdr[i].sh_entsize = sizeof (elf_reloc_t);
- shdr[i].sh_size = num_rels * sizeof (elf_reloc_t);
- grub_util_write_image_at (rel, shdr[i].sh_size, offset, fp, name);
- offset += shdr[i].sh_size;
- free (rel);
- }
- }
- static void
- write_symbol_table (FILE* fp, const char *name, char *image,
- struct grub_pe32_coff_header *pe_chdr,
- struct grub_pe32_section_table *pe_shdr,
- int *section_map)
- {
- struct grub_pe32_symbol *pe_symtab;
- char *pe_strtab;
- Elf_Sym *symtab;
- int *symtab_map, num_syms;
- int i;
- pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
- pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
- symtab = (Elf_Sym *) xmalloc ((pe_chdr->num_symbols + 1) *
- sizeof (Elf_Sym));
- memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf_Sym));
- num_syms = 1;
- symtab_map = (int *) xmalloc (pe_chdr->num_symbols * sizeof (int));
- for (i = 0; i < (int) pe_chdr->num_symbols;
- i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
- {
- int bind, type;
- symtab_map[i] = -1;
- if ((pe_symtab->section > pe_chdr->num_sections) ||
- (section_map[pe_symtab->section] == -1))
- continue;
- if (! pe_symtab->section)
- type = STT_NOTYPE;
- else if (pe_symtab->type == GRUB_PE32_DT_FUNCTION)
- type = STT_FUNC;
- else
- type = STT_OBJECT;
- if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
- bind = STB_GLOBAL;
- else
- bind = STB_LOCAL;
- if ((pe_symtab->type != GRUB_PE32_DT_FUNCTION) && (pe_symtab->num_aux))
- {
- if (! pe_symtab->value)
- type = STT_SECTION;
- symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name;
- }
- else
- {
- char short_name[9];
- char *symname;
- if (pe_symtab->long_name[0])
- {
- strncpy (short_name, pe_symtab->short_name, 8);
- short_name[8] = 0;
- symname = short_name;
- }
- else
- symname = pe_strtab + pe_symtab->long_name[1];
- if ((strcmp (symname, "_grub_mod_init")) &&
- (strcmp (symname, "_grub_mod_fini")) &&
- (strcmp (symname, "grub_mod_init")) &&
- (strcmp (symname, "grub_mod_fini")) &&
- (bind == STB_LOCAL))
- continue;
- symtab[num_syms].st_name = insert_string (symname);
- }
- symtab[num_syms].st_shndx = section_map[pe_symtab->section];
- symtab[num_syms].st_value = pe_symtab->value;
- symtab[num_syms].st_info = ELF_ST_INFO (bind, type);
- symtab_map[i] = num_syms;
- num_syms++;
- }
- write_reloc_section (fp, name, image, pe_chdr, pe_shdr,
- symtab, symtab_map);
- shdr[symtab_section].sh_name = insert_string (".symtab");
- shdr[symtab_section].sh_type = SHT_SYMTAB;
- shdr[symtab_section].sh_offset = offset;
- shdr[symtab_section].sh_size = num_syms * sizeof (Elf_Sym);
- shdr[symtab_section].sh_entsize = sizeof (Elf_Sym);
- shdr[symtab_section].sh_link = strtab_section;
- shdr[symtab_section].sh_addralign = 4;
- grub_util_write_image_at (symtab, shdr[symtab_section].sh_size,
- offset, fp, name);
- offset += shdr[symtab_section].sh_size;
- free (symtab);
- free (symtab_map);
- }
- static void
- write_string_table (FILE *fp, const char *name)
- {
- shdr[strtab_section].sh_name = insert_string (".strtab");
- shdr[strtab_section].sh_type = SHT_STRTAB;
- shdr[strtab_section].sh_offset = offset;
- shdr[strtab_section].sh_size = strtab_len;
- shdr[strtab_section].sh_addralign = 1;
- grub_util_write_image_at (strtab, strtab_len, offset, fp,
- name);
- offset += strtab_len;
- free (strtab);
- }
- static void
- write_section_header (FILE *fp, const char *name)
- {
- ehdr.e_ident[EI_MAG0] = ELFMAG0;
- ehdr.e_ident[EI_MAG1] = ELFMAG1;
- ehdr.e_ident[EI_MAG2] = ELFMAG2;
- ehdr.e_ident[EI_MAG3] = ELFMAG3;
- ehdr.e_ident[EI_VERSION] = EV_CURRENT;
- ehdr.e_version = EV_CURRENT;
- ehdr.e_type = ET_REL;
- #if GRUB_TARGET_WORDSIZE == 64
- ehdr.e_ident[EI_CLASS] = ELFCLASS64;
- ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
- ehdr.e_machine = EM_X86_64;
- #else
- ehdr.e_ident[EI_CLASS] = ELFCLASS32;
- ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
- ehdr.e_machine = EM_386;
- #endif
- ehdr.e_ehsize = sizeof (ehdr);
- ehdr.e_shentsize = sizeof (Elf_Shdr);
- ehdr.e_shstrndx = strtab_section;
- ehdr.e_shoff = offset;
- ehdr.e_shnum = num_sections;
- grub_util_write_image_at (shdr, sizeof (Elf_Shdr) * num_sections,
- offset, fp, name);
- grub_util_write_image_at (&ehdr, sizeof (Elf_Ehdr), 0, fp, name);
- }
- static void
- convert_pe (FILE* fp, const char *name, char *image)
- {
- struct grub_pe32_coff_header *pe_chdr;
- struct grub_pe32_section_table *pe_shdr;
- int *section_map;
- if (image[0] == 'M' && image[1] == 'Z')
- pe_chdr = (struct grub_pe32_coff_header *) (image + (grub_le_to_cpu32 (((grub_uint32_t *)image)[0xf]) + 4));
- else
- pe_chdr = (struct grub_pe32_coff_header *) image;
- if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE)
- grub_util_error ("invalid coff image (%x != %x)",
- grub_le_to_cpu16 (pe_chdr->machine), GRUB_PE32_MACHINE);
- strtab = xmalloc (STRTAB_BLOCK);
- strtab_max = STRTAB_BLOCK;
- strtab[0] = 0;
- strtab_len = 1;
- offset = sizeof (ehdr);
- if (pe_chdr->optional_header_size)
- {
- #if GRUB_TARGET_WORDSIZE == 64
- struct grub_pe64_optional_header *o;
- #else
- struct grub_pe32_optional_header *o;
- #endif
- o = (void *) (pe_chdr + 1);
- image_base = o->image_base;
- }
- pe_shdr = (struct grub_pe32_section_table *) ((char *) (pe_chdr + 1) + pe_chdr->optional_header_size);
- section_map = write_section_data (fp, name, image, pe_chdr, pe_shdr);
- write_symbol_table (fp, name, image, pe_chdr, pe_shdr, section_map);
- free (section_map);
- write_string_table (fp, name);
- write_section_header (fp, name);
- }
- int
- main (int argc, char *argv[])
- {
- char *image;
- FILE* fp;
- char *in, *out;
- /* Obtain PATH. */
- if (1 >= argc)
- {
- fprintf (stderr, "Filename not specified.\n");
- return 1;
- }
- in = argv[1];
- if (argc > 2)
- out = argv[2];
- else
- out = in;
- image = grub_util_read_image (in);
- fp = grub_util_fopen (out, "wb");
- if (! fp)
- grub_util_error ("cannot open %s", out);
- convert_pe (fp, out, image);
- fclose (fp);
- return 0;
- }
|