diff --git a/Makefile.in b/Makefile.in index 6f9474c..ccb5497 100644 --- a/Makefile.in +++ b/Makefile.in @@ -68,7 +68,11 @@ TARGET_CFLAGS = @TARGET_CFLAGS@ TARGET_CPPFLAGS = @TARGET_CPPFLAGS@ -I. -Iinclude -I$(srcdir)/include \ -Wall -W TARGET_LDFLAGS = @TARGET_LDFLAGS@ +TARGET_IMG_LDSCRIPT = @TARGET_IMG_LDSCRIPT@ +TARGET_IMG_LDFLAGS = @TARGET_IMG_LDFLAGS@ +TARGET_OBJ2ELF = @TARGET_OBJ2ELF@ MODULE_LDFLAGS = @MODULE_LDFLAGS@ +EXEEXT = @EXEEXT@ OBJCOPY = @OBJCOPY@ STRIP = @STRIP@ NM = @NM@ diff --git a/aclocal.m4 b/aclocal.m4 index b7cc0a2..ee6c4db 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -73,7 +73,7 @@ else fi grub_cv_prog_objcopy_absolute=yes for link_addr in 2000 8000 7C00; do - if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} ${LDFLAGS} -nostdlib -Wl,-N -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec]); then : + if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -nostdlib ${TARGET_IMG_LDFLAGS_AC} -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec]); then : else AC_MSG_ERROR([${CC-cc} cannot link at address $link_addr]) fi @@ -398,3 +398,19 @@ else AC_MSG_RESULT([no]) [fi] ]) + +dnl Check if the C compiler supports `-mstack-arg-probe' (Cygwin). +AC_DEFUN(grub_CHECK_STACK_ARG_PROBE,[ +[# Smashing stack arg probe. +sap_possible=yes] +AC_MSG_CHECKING([whether `$CC' accepts `-mstack-arg-probe']) +AC_LANG_CONFTEST([[void foo (void) { volatile char a[8]; a[3]; }]]) +[if eval "$ac_compile -S -mstack-arg-probe -o conftest.s" 2> /dev/null; then] + AC_MSG_RESULT([yes]) + [# Should we clear up other files as well, having called `AC_LANG_CONFTEST'? + rm -f conftest.s +else + sap_possible=no] + AC_MSG_RESULT([no]) +[fi] +]) diff --git a/conf/common.rmk b/conf/common.rmk index b403b04..c0087f5 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -99,6 +99,11 @@ bin_UTILITIES += grub-editenv grub_editenv_SOURCES = util/grub-editenv.c util/envblk.c util/misc.c kern/misc.c kern/err.c CLEANFILES += grub-editenv +# for grub-pe2elf +bin_UTILITIES += grub-pe2elf +grub_pe2elf_SOURCES = util/grub-pe2elf.c util/misc.c +CLEANFILES += grub-pe2elf + # For update-grub update-grub: util/update-grub.in config.status ./config.status --file=$@:$< diff --git a/conf/i386-pc-cygwin-img-ld.sc b/conf/i386-pc-cygwin-img-ld.sc new file mode 100644 index 0000000..a41cac7 --- /dev/null +++ b/conf/i386-pc-cygwin-img-ld.sc @@ -0,0 +1,53 @@ +/* Linker script to create grub .img files on Cygwin. */ + +SECTIONS +{ + .text : + { + start = . ; + *(.text) + etext = . ; + } + .data : + { + __data_start__ = . ; + *(.data) + __data_end__ = . ; + } + .rdata : + { + __rdata_start__ = . ; + *(.rdata) + __rdata_end__ = . ; + } + .pdata : + { + *(.pdata) + edata = . ; + } + .bss : + { + __bss_start__ = . ; + *(.bss) + __common_start__ = . ; + *(COMMON) + __bss_end__ = . ; + } + .edata : + { + *(.edata) + end = . ; + } + .stab : + { + *(.stab) + } + .stabstr : + { + *(.stabstr) + } +} + +ASSERT("__rdata_end__"=="edata", ".pdata not empty") +ASSERT("__bss_end__" =="end" , ".edata not empty") + diff --git a/configure.ac b/configure.ac index 1d8bd08..bac5c9b 100644 --- a/configure.ac +++ b/configure.ac @@ -188,6 +188,30 @@ AC_CHECK_FUNCS(posix_memalign memalign) # Check for target programs. # + +# Use linker script if present, otherwise use builtin -N script. +AC_MSG_CHECKING([for option to link raw image]) +if test -f "${srcdir}/conf/${target_cpu}-${platform}-${target_os}-img-ld.sc"; then + TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/conf/${target_cpu}-${platform}-${target_os}-img-ld.sc" + TARGET_IMG_LDFLAGS="-Wl,-T${TARGET_IMG_LDSCRIPT}" + TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/${target_cpu}-${platform}-${target_os}-img-ld.sc" +else + TARGET_IMG_LDSCRIPT= + TARGET_IMG_LDFLAGS='-Wl,-N' + TARGET_IMG_LDFLAGS_AC='-Wl,-N' +fi +AC_SUBST(TARGET_IMG_LDSCRIPT) +AC_SUBST(TARGET_IMG_LDFLAGS) +AC_MSG_RESULT([$TARGET_IMG_LDFLAGS_AC]) + +# For platforms where ELF is not the default link format. +AC_MSG_CHECKING([for command to convert module to ELF format]) +if test "$host_os" = cygwin; then + TARGET_OBJ2ELF='grub-pe2elf.exe' +fi +AC_SUBST(TARGET_OBJ2ELF) +AC_MSG_RESULT([$TARGET_OBJ2ELF]) + # For cross-compiling. if test "x$target" != "x$host"; then # XXX this depends on the implementation of autoconf! @@ -278,6 +302,12 @@ grub_CHECK_STACK_PROTECTOR if test "x$ssp_possible" = xyes; then TARGET_CFLAGS="$TARGET_CFLAGS -fno-stack-protector" fi +grub_CHECK_STACK_ARG_PROBE +# Cygwin's GCC uses alloca() to probe the stackframe on static +# stack allocations above some threshold. +if test x"$sap_possible" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mno-stack-arg-probe" +fi AC_SUBST(TARGET_CFLAGS) AC_SUBST(TARGET_CPPFLAGS) @@ -296,9 +326,14 @@ grub_PROG_OBJCOPY_ABSOLUTE grub_PROG_LD_BUILD_ID_NONE grub_ASM_USCORE if test "x$target_cpu" = xi386; then + if test ! -z "$TARGET_IMG_LDSCRIPT"; then + # Check symbols provided by linker script. + CFLAGS="$TARGET_CFLAGS -nostdlib $TARGET_IMG_LDFLAGS_AC -Wl,-Ttext,8000,--defsym,___main=0x8100" + fi grub_CHECK_START_SYMBOL grub_CHECK_BSS_START_SYMBOL grub_CHECK_END_SYMBOL + CFLAGS="$TARGET_CFLAGS" grub_I386_ASM_PREFIX_REQUIREMENT grub_I386_ASM_ADDR32 grub_I386_ASM_ABSOLUTE_WITHOUT_ASTERISK diff --git a/genkernsyms.sh.in b/genkernsyms.sh.in index a5e1271..0df0bbf 100644 --- a/genkernsyms.sh.in +++ b/genkernsyms.sh.in @@ -16,9 +16,12 @@ : address@hidden@} : address@hidden@} +u= +grep "^#define HAVE_ASM_USCORE" config.h >/dev/null 2>&1 && u="_" + $CC -DGRUB_SYMBOL_GENERATOR=1 -E -I. -Iinclude -I"$srcdir/include" $* \ | grep -v '^#' \ | sed -n \ - -e '/EXPORT_FUNC *([a-zA-Z0-9_]*)/{s/.*EXPORT_FUNC *(\([a-zA-Z0-9_]*\)).*/\1 kernel/;p;}' \ - -e '/EXPORT_VAR *([a-zA-Z0-9_]*)/{s/.*EXPORT_VAR *(\([a-zA-Z0-9_]*\)).*/\1 kernel/;p;}' \ + -e '/EXPORT_FUNC *([a-zA-Z0-9_]*)/{s/.*EXPORT_FUNC *(\([a-zA-Z0-9_]*\)).*/'"$u"'\1 kernel/;p;}' \ + -e '/EXPORT_VAR *([a-zA-Z0-9_]*)/{s/.*EXPORT_VAR *(\([a-zA-Z0-9_]*\)).*/'"$u"'\1 kernel/;p;}' \ | sort -u diff --git a/genmk.rb b/genmk.rb index 56dee5c..71e9a4b 100644 --- a/genmk.rb +++ b/genmk.rb @@ -112,10 +112,11 @@ endif MOSTLYCLEANFILES += #{deps_str} UNDSYMFILES += #{undsym} address@hidden: #{pre_obj} #{mod_obj} address@hidden: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF) -rm -f $@ - $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ $^ - $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@ + $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ #{pre_obj} #{mod_obj} + if [ ! -z $(TARGET_OBJ2ELF) ]; then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ #{pre_obj}: $(#{prefix}_DEPENDENCIES) #{objs_str} -rm -f $@ @@ -194,7 +195,7 @@ class Utility deps = objs.collect {|obj| obj.suffix('d')} deps_str = deps.join(' '); - "CLEANFILES += address@hidden #{objs_str} + "CLEANFILES += address@hidden(EXEEXT) #{objs_str} MOSTLYCLEANFILES += #{deps_str} address@hidden: $(#{prefix}_DEPENDENCIES) #{objs_str} diff --git a/include/grub/dl.h b/include/grub/dl.h index b630c6f..bdde089 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -41,10 +41,10 @@ static void \ grub_mod_fini (void) #define GRUB_MOD_NAME(name) \ -__asm__ (".section .modname,\"S\"\n.string \"" #name "\"\n.previous") +__asm__ (".section .modname\n.string \"" #name "\"\n") #define GRUB_MOD_DEP(name) \ -__asm__ (".section .moddeps,\"S\"\n.string \"" #name "\"\n.previous") +__asm__ (".section .moddeps\n.string \"" #name "\"\n") struct grub_dl_segment { diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index c36d859..4b57ade 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -194,6 +194,18 @@ struct grub_pe32_section_table #define GRUB_PE32_SCN_MEM_READ 0x40000000 #define GRUB_PE32_SCN_MEM_WRITE 0x80000000 +#define GRUB_PE32_SCN_ALIGN_1BYTES 0x00100000 +#define GRUB_PE32_SCN_ALIGN_2BYTES 0x00200000 +#define GRUB_PE32_SCN_ALIGN_4BYTES 0x00300000 +#define GRUB_PE32_SCN_ALIGN_8BYTES 0x00400000 +#define GRUB_PE32_SCN_ALIGN_16BYTES 0x00500000 +#define GRUB_PE32_SCN_ALIGN_32BYTES 0x00600000 +#define GRUB_PE32_SCN_ALIGN_64BYTES 0x00700000 + +#define GRUB_PE32_SCN_ALIGN_SHIFT 20 +#define GRUB_PE32_SCN_ALIGN_MASK 7 + + struct grub_pe32_header { /* This should be filled in with GRUB_PE32_MSDOS_STUB. */ @@ -221,4 +233,35 @@ struct grub_pe32_fixup_block #define GRUB_PE32_REL_BASED_ABSOLUTE 0 #define GRUB_PE32_REL_BASED_HIGHLOW 3 +struct grub_pe32_symbol +{ + union + { + char short_name[8]; + grub_uint32_t long_name[2]; + }; + + grub_uint32_t value; + grub_uint16_t section; + grub_uint16_t type; + grub_uint8_t storage_class; + grub_uint8_t num_aux; +} __attribute__ ((packed)); + +#define GRUB_PE32_SYM_CLASS_EXTERNAL 2 +#define GRUB_PE32_SYM_CLASS_STATIC 3 +#define GRUB_PE32_SYM_CLASS_FILE 0x67 + +#define GRUB_PE32_DT_FUNCTION 0x20 + +struct grub_pe32_reloc +{ + grub_uint32_t offset; + grub_uint32_t symtab_index; + grub_uint16_t type; +} __attribute__ ((packed)); + +#define GRUB_PE32_REL_I386_DIR32 0x6 +#define GRUB_PE32_REL_I386_REL32 0x14 + #endif /* ! GRUB_EFI_PE32_HEADER */ diff --git a/include/grub/symbol.h b/include/grub/symbol.h index aa0ea5a..e951490 100644 --- a/include/grub/symbol.h +++ b/include/grub/symbol.h @@ -28,8 +28,14 @@ # define EXT_C(sym) sym #endif +#ifndef __CYGWIN__ #define FUNCTION(x) .globl EXT_C(x) ; .type EXT_C(x), "function" ; EXT_C(x): #define VARIABLE(x) .globl EXT_C(x) ; .type EXT_C(x), "object" ; EXT_C(x): +#else +/* .type not supported for non-ELF targets. XXX: Check this in configure? */ +#define FUNCTION(x) .globl EXT_C(x) ; .def EXT_C(x); .scl 2; .type 32; .endef; EXT_C(x): +#define VARIABLE(x) .globl EXT_C(x) ; .def EXT_C(x); .scl 2; .type 0; .endef; EXT_C(x): +#endif /* Mark an exported symbol. */ #ifndef GRUB_SYMBOL_GENERATOR diff --git a/kern/dl.c b/kern/dl.c index c0d9f1d..d606ece 100644 --- a/kern/dl.c +++ b/kern/dl.c @@ -454,7 +454,7 @@ grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e) const char *name = (char *) e + s->sh_offset; const char *max = name + s->sh_size; - while (name < max) + while ((name < max) && (*name)) { grub_dl_t m; grub_dl_dep_t dep; diff --git a/util/grub-pe2elf.c b/util/grub-pe2elf.c new file mode 100755 index 0000000..6ea145a --- /dev/null +++ b/util/grub-pe2elf.c @@ -0,0 +1,494 @@ +/* grub-pe2elf.c - tool to convert pe image to elf. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static struct option options[] = { + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {"verbose", no_argument, 0, 'v'}, + {0, 0, 0, 0} +}; + +static void +usage (int status) +{ + if (status) + fprintf (stderr, "Try ``grub-editenv --help'' for more information.\n"); + else + printf ("\ +Usage: grub-editenv [OPTIONS] input [output]\n\ +\n\ +Tool to convert pe image to elf.\n\ +\nOptions:\n\ + -h, --help display this message and exit\n\ + -V, --version print version information and exit\n\ + -v, --verbose print verbose messages\n\ +\n\ +Report bugs to <%s>.\n", PACKAGE_BUGREPORT); + + exit (status); +} + +/* + * Section layout + * + * null + * .text + * .rdata + * .data + * .bss + * .modname + * .moddeps + * .symtab + * .strtab + * 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 SYMTAB_SECTION 7 +#define STRTAB_SECTION 8 + +#define REL_SECTION 9 +#define MAX_SECTIONS 12 + +#define STRTAB_BLOCK 256 + +static char *strtab; +static int strtab_max, strtab_len; + +Elf32_Ehdr ehdr; +Elf32_Shdr shdr[MAX_SECTIONS]; +int num_sections; +grub_uint32_t offset; + +static int +insert_string (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, char *image, + struct grub_pe32_coff_header *pe_chdr, + struct grub_pe32_section_table *pe_shdr) +{ + int *section_map; + int i; + + section_map = xmalloc ((pe_chdr->num_sections + 1) * sizeof (int)); + section_map[0] = 0; + + for (i = 0; i < pe_chdr->num_sections; i++, pe_shdr++) + { + grub_uint32_t idx; + + if (! strcmp (pe_shdr->name, ".text")) + { + idx = TEXT_SECTION; + shdr[idx].sh_flags = SHF_ALLOC | SHF_EXECINSTR; + } + else if (! strcmp (pe_shdr->name, ".rdata")) + { + idx = RDATA_SECTION; + shdr[idx].sh_flags = SHF_ALLOC; + } + else if (! strcmp (pe_shdr->name, ".data")) + { + idx = DATA_SECTION; + shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE; + } + else if (! strcmp (pe_shdr->name, ".bss")) + { + idx = BSS_SECTION; + shdr[idx].sh_flags = SHF_ALLOC | SHF_WRITE; + } + else if (! strcmp (pe_shdr->name, ".modname")) + idx = MODNAME_SECTION; + else if (! strcmp (pe_shdr->name, ".moddeps")) + idx = MODDEPS_SECTION; + else + { + section_map[i + 1] = -1; + continue; + } + + section_map[i + 1] = idx; + + shdr[idx].sh_type = (idx == BSS_SECTION) ? SHT_NOBITS : SHT_PROGBITS; + shdr[idx].sh_size = pe_shdr->raw_data_size; + shdr[idx].sh_addralign = 1 << (((pe_shdr->characteristics >> + GRUB_PE32_SCN_ALIGN_SHIFT) & + GRUB_PE32_SCN_ALIGN_MASK) - 1); + + if (idx != BSS_SECTION) + { + shdr[idx].sh_offset = offset; + grub_util_write_image_at (image + pe_shdr->raw_data_offset, + pe_shdr->raw_data_size, offset, fp); + + offset += pe_shdr->raw_data_size; + } + + if (pe_shdr->relocations_offset) + { + char name[5 + strlen (pe_shdr->name)]; + + if (num_sections >= MAX_SECTIONS) + grub_util_error ("Too many sections"); + + sprintf (name, ".rel%s", pe_shdr->name); + + shdr[num_sections].sh_name = insert_string (name); + shdr[num_sections].sh_link = i; + shdr[num_sections].sh_info = idx; + + shdr[idx].sh_name = shdr[num_sections].sh_name + 4; + + num_sections++; + } + else + shdr[idx].sh_name = insert_string (pe_shdr->name); + } + + return section_map; +} + +static void +write_reloc_section (FILE* fp, char *image, + struct grub_pe32_coff_header *pe_chdr, + struct grub_pe32_section_table *pe_shdr, + Elf32_Sym *symtab, + int *symtab_map) +{ + int i; + + for (i = REL_SECTION; i < num_sections; i++) + { + struct grub_pe32_section_table *pe_sec; + struct grub_pe32_reloc *pe_rel; + Elf32_Rel *rel; + grub_uint32_t size; + int j, modified; + + modified = 0; + + pe_sec = pe_shdr + shdr[i].sh_link; + pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset); + size = pe_sec->num_relocations * sizeof (Elf32_Rel); + rel = (Elf32_Rel *) xmalloc (size); + + for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++) + { + int type; + + if ((pe_rel->symtab_index >= pe_chdr->num_symbols) || + (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); + + pe_rel->offset -= pe_sec->virtual_address; + if (symtab[symtab_map[pe_rel->symtab_index]].st_shndx == 0) + { + *(grub_uint32_t *)(image + pe_sec->raw_data_offset + + pe_rel->offset) = (grub_uint32_t) -4; + modified = 1; + } + + rel[j].r_offset = pe_rel->offset; + rel[j].r_info = ELF32_R_INFO (symtab_map[pe_rel->symtab_index], + type); + } + + 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); + + shdr[i].sh_type = SHT_REL; + shdr[i].sh_offset = offset; + shdr[i].sh_link = SYMTAB_SECTION; + shdr[i].sh_addralign = 4; + shdr[i].sh_entsize = sizeof (Elf32_Rel); + shdr[i].sh_size = size; + + grub_util_write_image_at (rel, size, offset, fp); + offset += size; + free (rel); + } +} + +static void +write_symbol_table (FILE* fp, 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; + Elf32_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)); + 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 ((type != STT_FUNC) && (pe_symtab->num_aux)) + { + type = STT_SECTION; + + symtab[num_syms].st_name = shdr[section_map[pe_symtab->section]].sh_name; + } + else + { + char *name; + + name = ((pe_symtab->long_name[0]) ? pe_symtab->short_name : + pe_strtab + pe_symtab->long_name[1]); + + if ((strcmp (name, "_grub_mod_init")) && + (strcmp (name, "_grub_mod_fini")) && + (bind == STB_LOCAL)) + continue; + + symtab[num_syms].st_name = insert_string (name); + } + + 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_map[i] = num_syms; + num_syms++; + } + + write_reloc_section (fp, 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; + + grub_util_write_image_at (symtab, shdr[SYMTAB_SECTION].sh_size, + offset, fp); + offset += shdr[SYMTAB_SECTION].sh_size; + + free (symtab); + free (symtab_map); +} + +static void +write_string_table (FILE* fp) +{ + 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); + offset += strtab_len; + + free (strtab); +} + +static void +write_section_header (FILE* fp) +{ + 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; + + ehdr.e_ident[EI_CLASS] = ELFCLASS32; + ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + ehdr.e_machine = EM_386; + + ehdr.e_ehsize = sizeof (ehdr); + ehdr.e_shentsize = sizeof (Elf32_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, + offset, fp); + + grub_util_write_image_at (&ehdr, sizeof (Elf32_Ehdr), 0, fp); +} + +static void +convert_pe (FILE* fp, char *image) +{ + struct grub_pe32_coff_header *pe_chdr; + struct grub_pe32_section_table *pe_shdr; + int *section_map; + + pe_chdr = (struct grub_pe32_coff_header *) image; + if (grub_le_to_cpu16 (pe_chdr->machine) != GRUB_PE32_MACHINE_I386) + grub_util_error ("Invalid coff image"); + + strtab = xmalloc (STRTAB_BLOCK); + strtab_max = STRTAB_BLOCK; + strtab[0] = 0; + strtab_len = 1; + + offset = sizeof (ehdr); + pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1); + num_sections = REL_SECTION; + + section_map = write_section_data (fp, image, pe_chdr, pe_shdr); + + write_symbol_table (fp, image, pe_chdr, pe_shdr, section_map); + free (section_map); + + write_string_table (fp); + + write_section_header (fp); +} + +int +main (int argc, char *argv[]) +{ + char *image; + FILE* fp; + + progname = "grub-pe2elf"; + + /* Check for options. */ + while (1) + { + int c = getopt_long (argc, argv, "hVv", options, 0); + + if (c == -1) + break; + else + switch (c) + { + case 'h': + usage (0); + break; + + case 'V': + printf ("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION); + return 0; + + case 'v': + verbosity++; + break; + + default: + usage (1); + break; + } + } + + /* Obtain PATH. */ + if (optind >= argc) + { + fprintf (stderr, "Filename not specified.\n"); + usage (1); + } + + image = grub_util_read_image (argv[optind]); + + if (optind + 1 < argc) + optind++; + + fp = fopen (argv[optind], "wb"); + if (! fp) + grub_util_error ("cannot open %s", argv[optind]); + + convert_pe (fp, image); + + fclose (fp); + + return 0; +}