diff --git a/ChangeLog b/ChangeLog index cfab3f1ce..2bf4ef182 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-12-16 Vladimir Serbinenko + + Add support for converting PE+ to Elf64. + 2013-12-16 Vladimir Serbinenko * grub-core/commands/minicmd.c (grub_mini_cmd_dump): Handle LLP case. diff --git a/configure.ac b/configure.ac index 59bd8736b..be08d55be 100644 --- a/configure.ac +++ b/configure.ac @@ -699,7 +699,7 @@ AC_ARG_ENABLE([efiemu], if test x"$enable_efiemu" = xno ; then efiemu_excuse="explicitly disabled" fi -if test x"$grub_cv_target_cc_link_format" = x-mi386pe ; then +if test x"$grub_cv_target_cc_link_format" = x-mi386pe || test x"$grub_cv_target_cc_link_format" = x-mi386pep ; then efiemu_excuse="not available on cygwin" fi if test x"$target_cpu" != xi386 ; then @@ -759,8 +759,11 @@ CFLAGS="$TARGET_CFLAGS" if test x"$target_cpu" = xi386 || test x"$target_cpu" = xx86_64; then AC_CACHE_CHECK([for target linking format], [grub_cv_target_cc_link_format], [ grub_cv_target_cc_link_format=unknown - for format in -melf_${target_cpu} -melf_${target_cpu}_fbsd -melf_${target_cpu}_obsd -melf_${target_cpu}_haiku -m${target_cpu}pe -arch,${target_cpu}; do - if test x${target_cpu} != xi386 && test x$format = x${target_cpu}pe; then + for format in -melf_${target_cpu} -melf_${target_cpu}_fbsd -melf_${target_cpu}_obsd -melf_${target_cpu}_haiku -mi386pe -mi386pep -arch,${target_cpu}; do + if test x${target_cpu} != xi386 && test x$format = xi386pe; then + continue + fi + if test x${target_cpu} != xx86_64 && test x$format = xi386pep; then continue fi CFLAGS="$TARGET_CFLAGS" @@ -781,9 +784,12 @@ if test x"$target_cpu" = xi386 || test x"$target_cpu" = xx86_64; then AC_MSG_ERROR([no suitable link format found]) fi TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,$grub_cv_target_cc_link_format" - if test x"$grub_cv_target_cc_link_format" = x-mi386pe; then + if test x"$grub_cv_target_cc_link_format" = x-mi386pe ; then TARGET_OBJ2ELF='./build-grub-pe2elf'; fi + if test x"$grub_cv_target_cc_link_format" = x-mi386pep ; then + TARGET_OBJ2ELF='./build-grub-pep2elf'; + fi fi if test x$grub_cv_target_cc_link_format = x-arch,i386 || test x$grub_cv_target_cc_link_format = x-arch,x86_64; then @@ -801,7 +807,7 @@ if test x$grub_cv_target_cc_link_format = x-arch,i386 || test x$grub_cv_target_c TARGET_IMG_LDFLAGS_AC='-nostdlib -static -Wl,-preload -Wl,-segalign,20' TARGET_IMG_BASE_LDOPT="-Wl,-image_base" TARGET_LDFLAGS_OLDMAGIC="" -elif test x$grub_cv_target_cc_link_format = x-mi386pe; then +elif test x$grub_cv_target_cc_link_format = x-mi386pe || test x$grub_cv_target_cc_link_format = x-mi386pep ; then TARGET_APPLE_LINKER=0 TARGET_LDFLAGS_OLDMAGIC="-Wl,-N" TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/${grub_coredir}/conf/i386-cygwin-img-ld.sc" diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 5b804c7d4..79c63abf1 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -26,8 +26,11 @@ CFLAGS_LIBRARY += $(CFLAGS_PLATFORM) -fno-builtin CPPFLAGS_LIBRARY += $(CPPFLAGS_PLATFORM) CCASFLAGS_LIBRARY += $(CCASFLAGS_PLATFORM) +build-grub-pep2elf: $(top_srcdir)/util/grub-pe2elf.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) -DGRUB_BUILD=1 -DGRUB_TARGET_WORDSIZE=64 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-pep2elf\" $^ + build-grub-pe2elf: $(top_srcdir)/util/grub-pe2elf.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c - $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) -DGRUB_BUILD=1 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-pe2elf\" $^ + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) -DGRUB_BUILD=1 -DGRUB_TARGET_WORDSIZE=32 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-pe2elf\" $^ # gentrigtables gentrigtables: gentrigtables.c diff --git a/util/grub-pe2elf.c b/util/grub-pe2elf.c index a711af982..a48ef2395 100644 --- a/util/grub-pe2elf.c +++ b/util/grub-pe2elf.c @@ -48,29 +48,32 @@ * relocation sections */ -#define TEXT_SECTION 1 -#define RDATA_SECTION 2 -#define DATA_SECTION 3 -#define BSS_SECTION 4 -#define MODNAME_SECTION 5 -#define MODDEPS_SECTION 6 -#define MODLICENSE_SECTION 7 -#define SYMTAB_SECTION 8 -#define STRTAB_SECTION 9 - -#define REL_SECTION 10 - -/* 10 normal section + up to 4 relocation (.text, .rdata, .data, .symtab). */ -#define MAX_SECTIONS 16 +#if GRUB_TARGET_WORDSIZE == 64 +typedef Elf64_Rela elf_reloc_t; +typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Shdr Elf_Shdr; +typedef Elf64_Sym Elf_Sym; +#define ELF_R_INFO ELF64_R_INFO +#define ELF_ST_INFO ELF64_ST_INFO +#define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_X86_64 +#else +typedef Elf32_Rel elf_reloc_t; +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Shdr Elf_Shdr; +typedef Elf32_Sym Elf_Sym; +#define ELF_R_INFO ELF32_R_INFO +#define ELF_ST_INFO ELF32_ST_INFO +#define GRUB_PE32_MACHINE GRUB_PE32_MACHINE_I386 +#endif #define STRTAB_BLOCK 256 static char *strtab; static int strtab_max, strtab_len; -static Elf32_Ehdr ehdr; -static Elf32_Shdr shdr[MAX_SECTIONS]; -static int num_sections; +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 @@ -102,15 +105,20 @@ write_section_data (FILE* fp, const char *name, char *image, { 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 ((pe_chdr->num_sections + 1) * sizeof (int)); + 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 idx; + grub_uint32_t category; const char *shname = pe_shdr->name; grub_size_t secsize; @@ -124,54 +132,56 @@ write_section_data (FILE* fp, const char *name, char *image, secsize = pe_shdr->raw_data_size; + shdr[idx].sh_type = SHT_PROGBITS; + if (! strcmp (shname, ".text")) { - idx = TEXT_SECTION; + category = 0; shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR; } - else if (! strcmp (shname, ".rdata")) + else if (! strncmp (shname, ".rdata", 6)) { - idx = RDATA_SECTION; + category = 1; shdr[idx].sh_flags = SHF_ALLOC; } else if (! strcmp (shname, ".data")) { - idx = DATA_SECTION; + category = 2; shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE; } else if (! strcmp (shname, ".bss")) { - idx = BSS_SECTION; + 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")) - idx = MODNAME_SECTION; - else if (! strcmp (shname, ".moddeps")) - idx = MODDEPS_SECTION; - else if (strcmp (shname, ".module_license") == 0) - idx = MODLICENSE_SECTION; + 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_type = (idx == BSS_SECTION) ? SHT_NOBITS : SHT_PROGBITS; 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 (idx != BSS_SECTION) + if (shdr[idx].sh_type != SHT_NOBITS) { shdr[idx].sh_offset = offset; grub_util_write_image_at (image + pe_shdr->raw_data_offset, @@ -185,45 +195,55 @@ write_section_data (FILE* fp, const char *name, char *image, { char relname[5 + strlen (shname)]; - if (num_sections >= MAX_SECTIONS) - grub_util_error ("too many sections"); - sprintf (relname, ".rel%s", shname); - shdr[num_sections].sh_name = insert_string (relname); - shdr[num_sections].sh_link = i; - shdr[num_sections].sh_info = idx; + 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[num_sections].sh_name + 4; + shdr[idx].sh_name = shdr[idx_reloc].sh_name + 4; - num_sections++; + 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, - Elf32_Sym *symtab, + struct grub_pe32_section_table *pe_shdr, + Elf_Sym *symtab, int *symtab_map) { int i; - for (i = REL_SECTION; i < num_sections; i++) + for (i = first_reloc_section; i < reloc_sections_end; i++) { struct grub_pe32_section_table *pe_sec; struct grub_pe32_reloc *pe_rel; - Elf32_Rel *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 = (Elf32_Rel *) xmalloc (pe_sec->num_relocations * sizeof (Elf32_Rel)); + rel = (elf_reloc_t *) xmalloc (pe_sec->num_relocations * sizeof (elf_reloc_t)); num_rels = 0; modified = 0; @@ -236,43 +256,84 @@ write_reloc_section (FILE* fp, const char *name, char *image, (symtab_map[pe_rel->symtab_index] == -1)) grub_util_error ("invalid symbol"); - if (pe_rel->type == GRUB_PE32_REL_I386_DIR32) - type = R_386_32; - else if (pe_rel->type == GRUB_PE32_REL_I386_REL32) - type = R_386_PC32; - else - grub_util_error ("unknown pe relocation type %d\n", pe_rel->type); - ofs = pe_rel->offset - pe_sec->virtual_address; addr = (grub_uint32_t *)(image + pe_sec->raw_data_offset + ofs); - if (type == R_386_PC32) + + 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\n", 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 - modified = 1; - if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx) + if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx + && symtab[symtab_map[pe_rel->symtab_index]].st_shndx + == shdr[i].sh_info) { - if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx - != shdr[i].sh_info) - grub_util_error ("cross section call is not allowed"); - - *addr = (symtab[symtab_map[pe_rel->symtab_index]].st_value - - ofs - 4); + modified = 1; + *addr += (symtab[symtab_map[pe_rel->symtab_index]].st_value + - ofs - 4); continue; } else - *addr = -4; + { +#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 = ELF32_R_INFO (symtab_map[pe_rel->symtab_index], - type); + rel[num_rels].r_info = ELF_R_INFO (symtab_map[pe_rel->symtab_index], + type); num_rels++; } @@ -282,12 +343,16 @@ write_reloc_section (FILE* fp, const char *name, char *image, 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_link = symtab_section; shdr[i].sh_addralign = 4; - shdr[i].sh_entsize = sizeof (Elf32_Rel); - shdr[i].sh_size = num_rels * sizeof (Elf32_Rel); + 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; @@ -303,16 +368,16 @@ write_symbol_table (FILE* fp, const char *name, char *image, { struct grub_pe32_symbol *pe_symtab; char *pe_strtab; - Elf32_Sym *symtab; + 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 = (Elf32_Sym *) xmalloc ((pe_chdr->num_symbols + 1) * - sizeof (Elf32_Sym)); - memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf32_Sym)); + 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)); @@ -362,6 +427,8 @@ write_symbol_table (FILE* fp, const char *name, char *image, 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; @@ -370,7 +437,7 @@ write_symbol_table (FILE* fp, const char *name, char *image, symtab[num_syms].st_shndx = section_map[pe_symtab->section]; symtab[num_syms].st_value = pe_symtab->value; - symtab[num_syms].st_info = ELF32_ST_INFO (bind, type); + symtab[num_syms].st_info = ELF_ST_INFO (bind, type); symtab_map[i] = num_syms; num_syms++; @@ -379,17 +446,17 @@ write_symbol_table (FILE* fp, const char *name, char *image, 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 (Elf32_Sym); - shdr[SYMTAB_SECTION].sh_entsize = sizeof (Elf32_Sym); - shdr[SYMTAB_SECTION].sh_link = STRTAB_SECTION; - shdr[SYMTAB_SECTION].sh_addralign = 4; + 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, + grub_util_write_image_at (symtab, shdr[symtab_section].sh_size, offset, fp, name); - offset += shdr[SYMTAB_SECTION].sh_size; + offset += shdr[symtab_section].sh_size; free (symtab); free (symtab_map); @@ -398,11 +465,11 @@ write_symbol_table (FILE* fp, const char *name, char *image, 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; + 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; @@ -421,20 +488,25 @@ write_section_header (FILE *fp, const char *name) 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 (Elf32_Shdr); - ehdr.e_shstrndx = STRTAB_SECTION; + 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 (Elf32_Shdr) * num_sections, + grub_util_write_image_at (shdr, sizeof (Elf_Shdr) * num_sections, offset, fp, name); - grub_util_write_image_at (&ehdr, sizeof (Elf32_Ehdr), 0, fp, name); + grub_util_write_image_at (&ehdr, sizeof (Elf_Ehdr), 0, fp, name); } static void @@ -448,9 +520,9 @@ convert_pe (FILE* fp, const char *name, char *image) 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_I386) + 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_I386); + grub_le_to_cpu16 (pe_chdr->machine), GRUB_PE32_MACHINE); strtab = xmalloc (STRTAB_BLOCK); strtab_max = STRTAB_BLOCK; @@ -460,12 +532,15 @@ convert_pe (FILE* fp, const char *name, char *image) 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; - o = (struct grub_pe32_optional_header *) (pe_chdr + 1); +#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); - num_sections = REL_SECTION; section_map = write_section_data (fp, name, image, pe_chdr, pe_shdr);