2N/A/* grub-pe2elf.c - tool to convert pe image to elf. */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2008,2009 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <config.h>
2N/A#include <grub/types.h>
2N/A#include <grub/util/misc.h>
2N/A#include <grub/elf.h>
2N/A#include <grub/efi/pe32.h>
2N/A
2N/A#include <stdio.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <stdlib.h>
2N/A#include <getopt.h>
2N/A
2N/A#include "progname.h"
2N/A
2N/A/* Please don't internationalise this file. It's pointless. */
2N/A
2N/Astatic struct option options[] = {
2N/A {"help", no_argument, 0, 'h'},
2N/A {"version", no_argument, 0, 'V'},
2N/A {"verbose", no_argument, 0, 'v'},
2N/A {0, 0, 0, 0}
2N/A};
2N/A
2N/Astatic void
2N/Ausage (int status)
2N/A{
2N/A if (status)
2N/A fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
2N/A else
2N/A printf ("\
2N/AUsage: %s [OPTIONS] input [output]\n\
2N/A\n\
2N/ATool to convert pe image to elf.\n\
2N/A\nOptions:\n\
2N/A -h, --help display this message and exit\n\
2N/A -V, --version print version information and exit\n\
2N/A -v, --verbose print verbose messages\n\
2N/A\n\
2N/AReport bugs to <%s>.\n", program_name, PACKAGE_BUGREPORT);
2N/A
2N/A exit (status);
2N/A}
2N/A
2N/A/*
2N/A * Section layout
2N/A *
2N/A * null
2N/A * .text
2N/A * .rdata
2N/A * .data
2N/A * .bss
2N/A * .modname
2N/A * .moddeps
2N/A * .symtab
2N/A * .strtab
2N/A * relocation sections
2N/A */
2N/A
2N/A#define TEXT_SECTION 1
2N/A#define RDATA_SECTION 2
2N/A#define DATA_SECTION 3
2N/A#define BSS_SECTION 4
2N/A#define MODNAME_SECTION 5
2N/A#define MODDEPS_SECTION 6
2N/A#define MODLICENSE_SECTION 7
2N/A#define SYMTAB_SECTION 8
2N/A#define STRTAB_SECTION 9
2N/A
2N/A#define REL_SECTION 10
2N/A#define MAX_SECTIONS 16
2N/A
2N/A#define STRTAB_BLOCK 256
2N/A
2N/Astatic char *strtab;
2N/Astatic int strtab_max, strtab_len;
2N/A
2N/AElf32_Ehdr ehdr;
2N/AElf32_Shdr shdr[MAX_SECTIONS];
2N/Aint num_sections;
2N/Agrub_uint32_t offset;
2N/A
2N/Astatic int
2N/Ainsert_string (const char *name)
2N/A{
2N/A int len, result;
2N/A
2N/A if (*name == '_')
2N/A name++;
2N/A
2N/A len = strlen (name);
2N/A if (strtab_len + len >= strtab_max)
2N/A {
2N/A strtab_max += STRTAB_BLOCK;
2N/A strtab = xrealloc (strtab, strtab_max);
2N/A }
2N/A
2N/A strcpy (strtab + strtab_len, name);
2N/A result = strtab_len;
2N/A strtab_len += len + 1;
2N/A
2N/A return result;
2N/A}
2N/A
2N/Astatic int *
2N/Awrite_section_data (FILE* fp, char *image,
2N/A struct grub_pe32_coff_header *pe_chdr,
2N/A struct grub_pe32_section_table *pe_shdr)
2N/A{
2N/A int *section_map;
2N/A int i;
2N/A char *pe_strtab = (image + pe_chdr->symtab_offset
2N/A + pe_chdr->num_symbols * sizeof (struct grub_pe32_symbol));
2N/A
2N/A section_map = xmalloc ((pe_chdr->num_sections + 1) * sizeof (int));
2N/A section_map[0] = 0;
2N/A
2N/A for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++)
2N/A {
2N/A grub_uint32_t idx;
2N/A const char *name = pe_shdr->name;
2N/A
2N/A if (name[0] == '/' && isdigit (name[1]))
2N/A {
2N/A char t[sizeof (pe_shdr->name) + 1];
2N/A memcpy (t, name, sizeof (pe_shdr->name));
2N/A t[sizeof (pe_shdr->name)] = 0;
2N/A name = pe_strtab + atoi (t + 1);
2N/A }
2N/A
2N/A if (! strcmp (name, ".text"))
2N/A {
2N/A idx = TEXT_SECTION;
2N/A shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR;
2N/A }
2N/A else if (! strcmp (name, ".rdata"))
2N/A {
2N/A idx = RDATA_SECTION;
2N/A shdr[idx].sh_flags = SHF_ALLOC;
2N/A }
2N/A else if (! strcmp (name, ".data"))
2N/A {
2N/A idx = DATA_SECTION;
2N/A shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
2N/A }
2N/A else if (! strcmp (name, ".bss"))
2N/A {
2N/A idx = BSS_SECTION;
2N/A shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE;
2N/A }
2N/A else if (! strcmp (name, ".modname"))
2N/A idx = MODNAME_SECTION;
2N/A else if (! strcmp (name, ".moddeps"))
2N/A idx = MODDEPS_SECTION;
2N/A else if (strcmp (name, ".module_license") == 0)
2N/A idx = MODLICENSE_SECTION;
2N/A else
2N/A {
2N/A section_map[i + 1] = -1;
2N/A continue;
2N/A }
2N/A
2N/A section_map[i + 1] = idx;
2N/A
2N/A shdr[idx].sh_type = (idx == BSS_SECTION) ? SHT_NOBITS : SHT_PROGBITS;
2N/A shdr[idx].sh_size = pe_shdr->raw_data_size;
2N/A shdr[idx].sh_addralign = 1 << (((pe_shdr->characteristics >>
2N/A GRUB_PE32_SCN_ALIGN_SHIFT) &
2N/A GRUB_PE32_SCN_ALIGN_MASK) - 1);
2N/A
2N/A if (idx != BSS_SECTION)
2N/A {
2N/A shdr[idx].sh_offset = offset;
2N/A grub_util_write_image_at (image + pe_shdr->raw_data_offset,
2N/A pe_shdr->raw_data_size, offset, fp);
2N/A
2N/A offset += pe_shdr->raw_data_size;
2N/A }
2N/A
2N/A if (pe_shdr->relocations_offset)
2N/A {
2N/A char relname[5 + strlen (name)];
2N/A
2N/A if (num_sections >= MAX_SECTIONS)
2N/A grub_util_error ("too many sections");
2N/A
2N/A sprintf (relname, ".rel%s", name);
2N/A
2N/A shdr[num_sections].sh_name = insert_string (relname);
2N/A shdr[num_sections].sh_link = i;
2N/A shdr[num_sections].sh_info = idx;
2N/A
2N/A shdr[idx].sh_name = shdr[num_sections].sh_name + 4;
2N/A
2N/A num_sections++;
2N/A }
2N/A else
2N/A shdr[idx].sh_name = insert_string (name);
2N/A }
2N/A
2N/A return section_map;
2N/A}
2N/A
2N/Astatic void
2N/Awrite_reloc_section (FILE* fp, char *image,
2N/A struct grub_pe32_coff_header *pe_chdr,
2N/A struct grub_pe32_section_table *pe_shdr,
2N/A Elf32_Sym *symtab,
2N/A int *symtab_map)
2N/A{
2N/A int i;
2N/A
2N/A for (i = REL_SECTION; i < num_sections; i++)
2N/A {
2N/A struct grub_pe32_section_table *pe_sec;
2N/A struct grub_pe32_reloc *pe_rel;
2N/A Elf32_Rel *rel;
2N/A int num_rels, j, modified;
2N/A
2N/A pe_sec = pe_shdr + shdr[i].sh_link;
2N/A pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
2N/A rel = (Elf32_Rel *) xmalloc (pe_sec->num_relocations * sizeof (Elf32_Rel));
2N/A num_rels = 0;
2N/A modified = 0;
2N/A
2N/A for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
2N/A {
2N/A int type;
2N/A grub_uint32_t ofs, *addr;
2N/A
2N/A if ((pe_rel->symtab_index >= pe_chdr->num_symbols) ||
2N/A (symtab_map[pe_rel->symtab_index] == -1))
2N/A grub_util_error ("invalid symbol");
2N/A
2N/A if (pe_rel->type == GRUB_PE32_REL_I386_DIR32)
2N/A type = R_386_32;
2N/A else if (pe_rel->type == GRUB_PE32_REL_I386_REL32)
2N/A type = R_386_PC32;
2N/A else
2N/A grub_util_error ("unknown pe relocation type %d\n", pe_rel->type);
2N/A
2N/A ofs = pe_rel->offset - pe_sec->virtual_address;
2N/A addr = (grub_uint32_t *)(image + pe_sec->raw_data_offset + ofs);
2N/A if (type == R_386_PC32)
2N/A {
2N/A unsigned char code;
2N/A
2N/A code = image[pe_sec->raw_data_offset + ofs - 1];
2N/A
2N/A if (((code != 0xe8) && (code != 0xe9)) || (*addr))
2N/A grub_util_error ("invalid relocation (%x %x)", code, *addr);
2N/A
2N/A modified = 1;
2N/A if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx)
2N/A {
2N/A if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx
2N/A != shdr[i].sh_info)
2N/A grub_util_error ("cross section call is not allowed");
2N/A
2N/A *addr = (symtab[symtab_map[pe_rel->symtab_index]].st_value
2N/A - ofs - 4);
2N/A
2N/A continue;
2N/A }
2N/A else
2N/A *addr = -4;
2N/A }
2N/A
2N/A rel[num_rels].r_offset = ofs;
2N/A rel[num_rels].r_info = ELF32_R_INFO (symtab_map[pe_rel->symtab_index],
2N/A type);
2N/A num_rels++;
2N/A }
2N/A
2N/A if (modified)
2N/A grub_util_write_image_at (image + pe_sec->raw_data_offset,
2N/A shdr[shdr[i].sh_info].sh_size,
2N/A shdr[shdr[i].sh_info].sh_offset,
2N/A fp);
2N/A
2N/A shdr[i].sh_type = SHT_REL;
2N/A shdr[i].sh_offset = offset;
2N/A shdr[i].sh_link = SYMTAB_SECTION;
2N/A shdr[i].sh_addralign = 4;
2N/A shdr[i].sh_entsize = sizeof (Elf32_Rel);
2N/A shdr[i].sh_size = num_rels * sizeof (Elf32_Rel);
2N/A
2N/A grub_util_write_image_at (rel, shdr[i].sh_size, offset, fp);
2N/A offset += shdr[i].sh_size;
2N/A free (rel);
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Awrite_symbol_table (FILE* fp, char *image,
2N/A struct grub_pe32_coff_header *pe_chdr,
2N/A struct grub_pe32_section_table *pe_shdr,
2N/A int *section_map)
2N/A{
2N/A struct grub_pe32_symbol *pe_symtab;
2N/A char *pe_strtab;
2N/A Elf32_Sym *symtab;
2N/A int *symtab_map, num_syms;
2N/A int i;
2N/A
2N/A pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
2N/A pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
2N/A
2N/A symtab = (Elf32_Sym *) xmalloc ((pe_chdr->num_symbols + 1) *
2N/A sizeof (Elf32_Sym));
2N/A memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf32_Sym));
2N/A num_syms = 1;
2N/A
2N/A symtab_map = (int *) xmalloc (pe_chdr->num_symbols * sizeof (int));
2N/A
2N/A for (i = 0; i < (int) pe_chdr->num_symbols;
2N/A i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
2N/A {
2N/A int bind, type;
2N/A
2N/A symtab_map[i] = -1;
2N/A if ((pe_symtab->section > pe_chdr->num_sections) ||
2N/A (section_map[pe_symtab->section] == -1))
2N/A continue;
2N/A
2N/A if (! pe_symtab->section)
2N/A type = STT_NOTYPE;
2N/A else if (pe_symtab->type == GRUB_PE32_DT_FUNCTION)
2N/A type = STT_FUNC;
2N/A else
2N/A type = STT_OBJECT;
2N/A
2N/A if (pe_symtab->storage_class == GRUB_PE32_SYM_CLASS_EXTERNAL)
2N/A bind = STB_GLOBAL;
2N/A else
2N/A bind = STB_LOCAL;
2N/A
2N/A if ((pe_symtab->type != GRUB_PE32_DT_FUNCTION) && (pe_symtab->num_aux))
2N/A {
2N/A if (! pe_symtab->value)
2N/A type = STT_SECTION;
2N/A
2N/A symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name;
2N/A }
2N/A else
2N/A {
2N/A char short_name[9];
2N/A char *name;
2N/A
2N/A if (pe_symtab->long_name[0])
2N/A {
2N/A strncpy (short_name, pe_symtab->short_name, 8);
2N/A short_name[8] = 0;
2N/A name = short_name;
2N/A }
2N/A else
2N/A name = pe_strtab + pe_symtab->long_name[1];
2N/A
2N/A if ((strcmp (name, "_grub_mod_init")) &&
2N/A (strcmp (name, "_grub_mod_fini")) &&
2N/A (bind == STB_LOCAL))
2N/A continue;
2N/A
2N/A symtab[num_syms].st_name = insert_string (name);
2N/A }
2N/A
2N/A symtab[num_syms].st_shndx = section_map[pe_symtab->section];
2N/A symtab[num_syms].st_value = pe_symtab->value;
2N/A symtab[num_syms].st_info = ELF32_ST_INFO (bind, type);
2N/A
2N/A symtab_map[i] = num_syms;
2N/A num_syms++;
2N/A }
2N/A
2N/A write_reloc_section (fp, image, pe_chdr, pe_shdr, symtab, symtab_map);
2N/A
2N/A shdr[SYMTAB_SECTION].sh_name = insert_string (".symtab");
2N/A shdr[SYMTAB_SECTION].sh_type = SHT_SYMTAB;
2N/A shdr[SYMTAB_SECTION].sh_offset = offset;
2N/A shdr[SYMTAB_SECTION].sh_size = num_syms * sizeof (Elf32_Sym);
2N/A shdr[SYMTAB_SECTION].sh_entsize = sizeof (Elf32_Sym);
2N/A shdr[SYMTAB_SECTION].sh_link = STRTAB_SECTION;
2N/A shdr[SYMTAB_SECTION].sh_addralign = 4;
2N/A
2N/A grub_util_write_image_at (symtab, shdr[SYMTAB_SECTION].sh_size,
2N/A offset, fp);
2N/A offset += shdr[SYMTAB_SECTION].sh_size;
2N/A
2N/A free (symtab);
2N/A free (symtab_map);
2N/A}
2N/A
2N/Astatic void
2N/Awrite_string_table (FILE* fp)
2N/A{
2N/A shdr[STRTAB_SECTION].sh_name = insert_string (".strtab");
2N/A shdr[STRTAB_SECTION].sh_type = SHT_STRTAB;
2N/A shdr[STRTAB_SECTION].sh_offset = offset;
2N/A shdr[STRTAB_SECTION].sh_size = strtab_len;
2N/A shdr[STRTAB_SECTION].sh_addralign = 1;
2N/A grub_util_write_image_at (strtab, strtab_len, offset, fp);
2N/A offset += strtab_len;
2N/A
2N/A free (strtab);
2N/A}
2N/A
2N/Astatic void
2N/Awrite_section_header (FILE* fp)
2N/A{
2N/A ehdr.e_ident[EI_MAG0] = ELFMAG0;
2N/A ehdr.e_ident[EI_MAG1] = ELFMAG1;
2N/A ehdr.e_ident[EI_MAG2] = ELFMAG2;
2N/A ehdr.e_ident[EI_MAG3] = ELFMAG3;
2N/A ehdr.e_ident[EI_VERSION] = EV_CURRENT;
2N/A ehdr.e_version = EV_CURRENT;
2N/A ehdr.e_type = ET_REL;
2N/A
2N/A ehdr.e_ident[EI_CLASS] = ELFCLASS32;
2N/A ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
2N/A ehdr.e_machine = EM_386;
2N/A
2N/A ehdr.e_ehsize = sizeof (ehdr);
2N/A ehdr.e_shentsize = sizeof (Elf32_Shdr);
2N/A ehdr.e_shstrndx = STRTAB_SECTION;
2N/A
2N/A ehdr.e_shoff = offset;
2N/A ehdr.e_shnum = num_sections;
2N/A grub_util_write_image_at (&shdr, sizeof (Elf32_Shdr) * num_sections,
2N/A offset, fp);
2N/A
2N/A grub_util_write_image_at (&ehdr, sizeof (Elf32_Ehdr), 0, fp);
2N/A}
2N/A
2N/Astatic void
2N/Aconvert_pe (FILE* fp, char *image)
2N/A{
2N/A struct grub_pe32_coff_header *pe_chdr;
2N/A struct grub_pe32_section_table *pe_shdr;
2N/A int *section_map;
2N/A
2N/A pe_chdr = (struct grub_pe32_coff_header *) image;
2N/A if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE_I386)
2N/A grub_util_error ("invalid coff image");
2N/A
2N/A strtab = xmalloc (STRTAB_BLOCK);
2N/A strtab_max = STRTAB_BLOCK;
2N/A strtab[0] = 0;
2N/A strtab_len = 1;
2N/A
2N/A offset = sizeof (ehdr);
2N/A pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1);
2N/A num_sections = REL_SECTION;
2N/A
2N/A section_map = write_section_data (fp, image, pe_chdr, pe_shdr);
2N/A
2N/A write_symbol_table (fp, image, pe_chdr, pe_shdr, section_map);
2N/A free (section_map);
2N/A
2N/A write_string_table (fp);
2N/A
2N/A write_section_header (fp);
2N/A}
2N/A
2N/Aint
2N/Amain (int argc, char *argv[])
2N/A{
2N/A char *image;
2N/A FILE* fp;
2N/A
2N/A set_program_name (argv[0]);
2N/A
2N/A /* Check for options. */
2N/A while (1)
2N/A {
2N/A int c = getopt_long (argc, argv, "hVv", options, 0);
2N/A
2N/A if (c == -1)
2N/A break;
2N/A else
2N/A switch (c)
2N/A {
2N/A case 'h':
2N/A usage (0);
2N/A break;
2N/A
2N/A case 'V':
2N/A printf ("%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION);
2N/A return 0;
2N/A
2N/A case 'v':
2N/A verbosity++;
2N/A break;
2N/A
2N/A default:
2N/A usage (1);
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /* Obtain PATH. */
2N/A if (optind >= argc)
2N/A {
2N/A fprintf (stderr, "Filename not specified.\n");
2N/A usage (1);
2N/A }
2N/A
2N/A image = grub_util_read_image (argv[optind]);
2N/A
2N/A if (optind + 1 < argc)
2N/A optind++;
2N/A
2N/A fp = fopen (argv[optind], "wb");
2N/A if (! fp)
2N/A grub_util_error ("cannot open %s", argv[optind]);
2N/A
2N/A convert_pe (fp, image);
2N/A
2N/A fclose (fp);
2N/A
2N/A return 0;
2N/A}