All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] New object format
@ 2009-07-14 15:49 Bean
  2009-07-14 17:57 ` please stop this Robert Millan
                   ` (2 more replies)
  0 siblings, 3 replies; 25+ messages in thread
From: Bean @ 2009-07-14 15:49 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 974 bytes --]

Hi,

This patch implement a new object format, the advantages are:

Reduce size dramatically, some result:

Size of all modules:

original: 645575
new: 519093

Dynamic loader:

original
   text    data     bss     dec     hex filename
    412       0       0     412     19c kernel_img-kern___target_cpu__dl.o
   3052       0    2064    5116    13fc kernel_img-kern_dl.o

new:
   text    data     bss     dec     hex filename
   2272       0    2064    4336    10f0 kernel_img-kern_dl.o

Greatly simplify the build process:

1, compile *.c, *.S to *.o
2, use grub-mkmod to merge *.o file to mod
3, use grub-symdb to maintain module dependence

so there is no pre-* mod-* def-* und-* anymore.

grub-mkmod support both pe and elf format, no need to convert with grub-pe2elf.

BTW, this patch breaks other platform. And it seems there is some bug
in elf->mod converter. But the pe->mod works quite nice, I can load
graphic menu and even run osdetect script with lua.

-- 
Bean

[-- Attachment #2: obj.diff --]
[-- Type: text/x-patch, Size: 87812 bytes --]

diff --git a/Makefile.in b/Makefile.in
index 3d208e7..d984872 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -147,11 +147,9 @@ include $(srcdir)/conf/$(target_cpu)-$(platform).mk
 ### General targets.
 
 CLEANFILES += $(pkglib_DATA) $(pkgdata_DATA)
-pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst
-moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep.awk
-	cat $(DEFSYMFILES) /dev/null \
-	  | $(AWK) -f $(srcdir)/genmoddep.awk $(UNDSYMFILES) > $@ \
-	  || (rm -f $@; exit 1)
+pkglib_DATA += modsym.lst command.lst fs.lst partmap.lst parttool.lst handler.lst
+modsym.lst: $(MODFILES) grub-symdb
+	./grub-symdb -d . update $?
 
 command.lst: $(COMMANDFILES)
 	cat $^ /dev/null | sort > $@
diff --git a/conf/common.rmk b/conf/common.rmk
index 07ff04e..ddb368e 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -125,6 +125,23 @@ endif
 grub_pe2elf_SOURCES = util/grub-pe2elf.c util/misc.c
 CLEANFILES += grub-pe2elf
 
+# grub-mkmod
+bin_UTILITIES += grub-mkmod
+grub_mkmod_SOURCES = util/grub-mkmod.c util/misc.c util/obj.c \
+	util/obj_pe.c util/obj_elf.c kern/list.c
+CLEANFILES += grub-mkmod
+
+# grub-symdb
+bin_UTILITIES += grub-symdb
+grub_symdb_SOURCES = util/grub-symdb.c util/obj.c util/misc.c kern/list.c
+CLEANFILES += grub-symdb
+
+# grub-objdump
+bin_UTILITIES += grub-objdump
+grub_objdump_SOURCES = util/grub-objdump.c util/obj.c util/misc.c \
+	util/obj_dump.c kern/list.c
+CLEANFILES += grub-objdump
+
 # grub_macho2img assumes a lot about source file.
 # So installing it definitively is useless
 # But adding to bin_UTILITIES is needed for
@@ -512,15 +529,15 @@ sh_mod_CFLAGS = $(COMMON_CFLAGS)
 sh_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For lua.mod.
-lua_mod_SOURCES = script/lua/lapi.c script/lua/lcode.c script/lua/ldebug.c \
+lua_mod_SOURCES = script/lua/grub_main.c script/lua/grub_lib.c \
+	script/lua/lapi.c script/lua/lcode.c script/lua/ldebug.c \
 	script/lua/ldo.c script/lua/ldump.c script/lua/lfunc.c \
 	script/lua/lgc.c script/lua/llex.c script/lua/lmem.c \
 	script/lua/lobject.c script/lua/lopcodes.c script/lua/lparser.c \
 	script/lua/lstate.c script/lua/lstring.c script/lua/ltable.c \
 	script/lua/ltm.c script/lua/lundump.c script/lua/lvm.c \
 	script/lua/lzio.c script/lua/lauxlib.c script/lua/lbaselib.c \
-	script/lua/linit.c script/lua/ltablib.c script/lua/lstrlib.c \
-	script/lua/grub_main.c script/lua/grub_lib.c
+	script/lua/linit.c script/lua/ltablib.c script/lua/lstrlib.c
 lua_mod_CFLAGS = $(COMMON_CFLAGS)
 lua_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index f1915b6..185ffd4 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -53,7 +53,7 @@ kernel_img_SOURCES = kern/i386/pc/startup.S \
 	kern/misc.c kern/mm.c kern/reader.c kern/term.c \
 	kern/rescue_parser.c kern/rescue_reader.c \
 	kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \
-	kern/$(target_cpu)/dl.c kern/i386/pc/init.c kern/i386/pc/mmap.c \
+	kern/i386/pc/init.c kern/i386/pc/mmap.c \
 	kern/parser.c kern/partition.c \
 	kern/i386/tsc.c kern/i386/pit.c \
 	kern/generic/rtc_get_time_ms.c \
diff --git a/genmk.rb b/genmk.rb
index e3866c1..39141f4 100644
--- a/genmk.rb
+++ b/genmk.rb
@@ -106,60 +106,16 @@ class PModule
     objs_str = objs.join(' ')
     deps = objs.collect {|obj| obj.suffix('d')}
     deps_str = deps.join(' ')
-    pre_obj = 'pre-' + @name.suffix('o')
-    mod_src = 'mod-' + @name.suffix('c')
-    mod_obj = mod_src.suffix('o')
-    defsym = 'def-' + @name.suffix('lst')
-    undsym = 'und-' + @name.suffix('lst')
     mod_name = File.basename(@name, '.mod')
     symbolic_name = mod_name.sub(/\.[^\.]*$/, '')
 
-    "CLEANFILES += #{@name} #{mod_obj} #{mod_src} #{pre_obj} #{objs_str} #{undsym}
-ifneq ($(#{prefix}_EXPORTS),no)
-CLEANFILES += #{defsym}
-DEFSYMFILES += #{defsym}
-endif
+    "CLEANFILES += #{@name} #{objs_str}
 MOSTLYCLEANFILES += #{deps_str}
-UNDSYMFILES += #{undsym}
-
-ifneq ($(TARGET_APPLE_CC),1)
-#{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF)
-	-rm -f $@
-	$(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{pre_obj} #{mod_obj}
-	if test ! -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 $@
-else
-#{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF)
-	-rm -f $@
-	-rm -f $@.bin
-	$(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@.bin #{pre_obj} #{mod_obj}
-	$(OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -wd1106 -nu -nd $@.bin $@
-	-rm -f $@.bin
-endif
-
-#{pre_obj}: $(#{prefix}_DEPENDENCIES) #{objs_str}
-	-rm -f $@
-	$(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{objs_str}
 
-#{mod_obj}: #{mod_src}
-	$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(#{prefix}_CFLAGS) -c -o $@ $<
-
-#{mod_src}: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh
-	sh $(srcdir)/genmodsrc.sh '#{mod_name}' $< > $@ || (rm -f $@; exit 1)
-
-ifneq ($(#{prefix}_EXPORTS),no)
-ifneq ($(TARGET_APPLE_CC),1)
-#{defsym}: #{pre_obj}
-	$(NM) -g --defined-only -P -p $< | sed 's/^\\([^ ]*\\).*/\\1 #{mod_name}/' > $@
-else
-#{defsym}: #{pre_obj}
-	$(NM) -g -P -p $< | grep -E '^[a-zA-Z0-9_]* [TDS]'  | sed 's/^\\([^ ]*\\).*/\\1 #{mod_name}/' > $@
-endif
-endif
+#{@name}: $(#{prefix}_DEPENDENCIES) #{objs_str} grub-mkmod
+	./grub-mkmod -o $@ $(filter %.o, $^)
 
-#{undsym}: #{pre_obj}
-	echo '#{mod_name}' > $@
-	$(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+MODFILES += #{@name}
 
 " + objs.collect_with_index do |obj, i|
       src = sources[i]
diff --git a/include/grub/dl.h b/include/grub/dl.h
index e24b832..3f8b328 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -85,7 +85,6 @@ struct grub_dl
 };
 typedef struct grub_dl *grub_dl_t;
 
-grub_err_t EXPORT_FUNC(grub_dl_check_header) (void *ehdr, grub_size_t size);
 grub_dl_t EXPORT_FUNC(grub_dl_load_file) (const char *filename);
 grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
 grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
diff --git a/include/grub/elf.h b/include/grub/elf.h
index 1a1ec13..6d2f28d 100644
--- a/include/grub/elf.h
+++ b/include/grub/elf.h
@@ -454,8 +454,8 @@ typedef struct
    the end of a chain, meaning no further symbols are found in that bucket.  */
 
 #define STN_UNDEF	0		/* End of a chain.  */
-#define STN_ABS		65521
-
+#define STN_ABS		0xfff1
+#define STN_COMMON	0xfff2
 
 /* How to extract and insert information held in the st_other field.  */
 
diff --git a/include/grub/obj.h b/include/grub/obj.h
new file mode 100644
index 0000000..29ff8dc
--- /dev/null
+++ b/include/grub/obj.h
@@ -0,0 +1,90 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/>.
+ */
+
+#ifndef GRUB_OBJ_H
+#define GRUB_OBJ_H 1
+
+#include <grub/types.h>
+
+#define GRUB_OBJ_HEADER_MAGIC	0x4a424f47	/* GOBJ  */
+#define GRUB_OBJ_HEADER_VERSION	1
+
+#define GRUB_OBJ_SEG_END	0
+#define GRUB_OBJ_SEG_TEXT	1
+#define GRUB_OBJ_SEG_DATA	2
+#define GRUB_OBJ_SEG_RDATA	3
+#define GRUB_OBJ_SEG_BSS	4
+
+#define GRUB_OBJ_FUNC_NONE	0xffff
+
+struct grub_obj_segment
+{
+  grub_uint8_t type;
+  grub_uint32_t offset;
+  grub_uint8_t align;
+  grub_uint32_t size;
+} __attribute__((packed));
+
+struct grub_obj_header
+{
+  grub_uint32_t magic;
+  grub_uint16_t version;
+  grub_uint16_t symbol_table;
+  grub_uint16_t reloc_table;
+  grub_uint16_t string_table;
+  grub_uint16_t init_func;
+  grub_uint16_t fini_func;
+  grub_uint32_t mod_deps;
+  struct grub_obj_segment segments[1];
+} __attribute__((packed));
+
+#define GRUB_OBJ_SEGMENT_END	0xff
+
+#define GRUB_OBJ_REL_DIR32	0
+#define GRUB_OBJ_REL_REL32	1
+#define GRUB_OBJ_REL_DIR64	2
+#define GRUB_OBJ_REL_REL64	3
+
+#define GRUB_OBJ_REL_ISREL	1
+#define GRUB_OBJ_REL_64BIT	2
+
+struct grub_obj_symbol
+{
+  grub_uint8_t segment;
+  grub_uint16_t name;
+  grub_uint32_t offset;
+} __attribute__((packed));
+
+struct grub_obj_reloc
+{
+  grub_uint8_t segment;
+  grub_uint8_t type;
+  grub_uint32_t offset;
+  grub_uint8_t symbol_segment;
+} __attribute__((packed));
+
+struct grub_obj_reloc_extern
+{
+  grub_uint8_t segment;
+  grub_uint8_t type;
+  grub_uint32_t offset;
+  grub_uint8_t symbol_segment;
+  grub_uint16_t symbol_name;
+} __attribute__((packed));
+
+#endif /* ! GRUB_OBJ_H */
diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h
index 6a93ab0..841d702 100644
--- a/include/grub/util/misc.h
+++ b/include/grub/util/misc.h
@@ -26,6 +26,7 @@
 
 #include <config.h>
 #include <grub/types.h>
+#include <grub/list.h>
 
 #ifdef __NetBSD__
 /* NetBSD uses /boot for its boot block.  */
@@ -46,6 +47,9 @@ void grub_util_error (const char *fmt, ...) __attribute__ ((noreturn));
 void *xmalloc (size_t size);
 void *xrealloc (void *ptr, size_t size);
 char *xstrdup (const char *str);
+void *xmalloc_zero (size_t size);
+
+void * grub_list_reverse (grub_list_t head);
 
 char *grub_util_get_path (const char *dir, const char *file);
 size_t grub_util_get_fp_size (FILE *fp);
@@ -56,6 +60,8 @@ void grub_util_load_image (const char *path, char *buf);
 void grub_util_write_image (const char *img, size_t size, FILE *out);
 void grub_util_write_image_at (const void *img, size_t size, off_t offset,
 			       FILE *out);
+char * grub_util_get_module_name (const char *str);
+char * grub_util_get_module_path (const char *prefix, const char *str);
 
 #ifndef  HAVE_ASPRINTF
 
diff --git a/include/grub/util/obj.h b/include/grub/util/obj.h
new file mode 100644
index 0000000..f0cd610
--- /dev/null
+++ b/include/grub/util/obj.h
@@ -0,0 +1,85 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/>.
+ */
+
+#ifndef GRUB_UTIL_OBJ_HEADER
+#define GRUB_UTIL_OBJ_HEADER	1
+
+#include <config.h>
+#include <grub/types.h>
+#include <grub/obj.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+
+struct grub_util_obj_segment
+{
+  struct grub_util_obj_segment *next;
+  struct grub_obj_segment segment;
+  char *data;
+  grub_uint32_t raw_size;
+  grub_uint32_t file_off;
+  int index;
+};
+
+struct grub_util_obj_symbol
+{
+  struct grub_util_obj_symbol *next;
+  char *name;
+  struct grub_util_obj_segment *segment;
+  struct grub_obj_symbol symbol;
+};
+
+struct grub_util_obj_reloc
+{
+  struct grub_util_obj_reloc *next;
+  struct grub_util_obj_segment *segment;
+  struct grub_obj_reloc reloc;
+  struct grub_util_obj_segment *symbol_segment;
+  grub_uint32_t symbol_offset;
+  char *symbol_name;
+};
+
+struct grub_util_obj
+{
+  struct grub_util_obj_segment *segments;
+  struct grub_util_obj_symbol *symbols;
+  struct grub_util_obj_reloc *relocs;
+  grub_uint32_t mod_attr;
+  grub_uint32_t mod_deps;
+};
+
+#define GRUB_OBJ_MERGE_NONE	0
+#define GRUB_OBJ_MERGE_SAME	1
+#define GRUB_OBJ_MERGE_ALL	2
+
+void grub_obj_reverse (struct grub_util_obj *obj);
+void grub_obj_sort_segments (struct grub_util_obj *obj);
+void grub_obj_merge_segments (struct grub_util_obj *obj, int merge);
+void grub_obj_reloc_symbols (struct grub_util_obj *obj, int merge);
+void grub_obj_save (struct grub_util_obj *obj, char *mod_name, FILE *fp);
+struct grub_util_obj *grub_obj_load (char *image, int size, int load_data);
+void grub_obj_free (struct grub_util_obj *obj);
+
+void grub_obj_dump_segments (struct grub_util_obj *obj);
+void grub_obj_dump_symbols (struct grub_util_obj *obj);
+void grub_obj_dump_relocs (struct grub_util_obj *obj);
+
+int pe_add_file (struct grub_util_obj *obj, char *image, int size);
+int elf_add_file (struct grub_util_obj *obj, char *image, int size);
+
+#endif /* ! GRUB_UTIL_OBJ_HEADER */
diff --git a/kern/dl.c b/kern/dl.c
index 6c863be..fd81dd7 100644
--- a/kern/dl.c
+++ b/kern/dl.c
@@ -18,8 +18,8 @@
  */
 
 #include <config.h>
-#include <grub/elf.h>
 #include <grub/dl.h>
+#include <grub/obj.h>
 #include <grub/misc.h>
 #include <grub/mm.h>
 #include <grub/err.h>
@@ -219,78 +219,46 @@ grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
   return 0;
 }
 
-/* Check if EHDR is a valid ELF header.  */
-grub_err_t
-grub_dl_check_header (void *ehdr, grub_size_t size)
-{
-  Elf_Ehdr *e = ehdr;
-
-  /* Check the header size.  */
-  if (size < sizeof (Elf_Ehdr))
-    return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
-
-  /* Check the magic numbers.  */
-  if (grub_arch_dl_check_header (ehdr)
-      || e->e_ident[EI_MAG0] != ELFMAG0
-      || e->e_ident[EI_MAG1] != ELFMAG1
-      || e->e_ident[EI_MAG2] != ELFMAG2
-      || e->e_ident[EI_MAG3] != ELFMAG3
-      || e->e_ident[EI_VERSION] != EV_CURRENT
-      || e->e_version != EV_CURRENT)
-    return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
-
-  return GRUB_ERR_NONE;
-}
-
 /* Load all segments from memory specified by E.  */
 static grub_err_t
-grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+grub_dl_load_segments (grub_dl_t mod, struct grub_obj_header *e)
 {
   unsigned i;
-  Elf_Shdr *s;
+  struct grub_obj_segment *s;
 
-  for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
+  for (i = 0, s = &e->segments[0]; s->type != GRUB_OBJ_SEGMENT_END; i++, s++)
     {
-      if (s->sh_flags & SHF_ALLOC)
+      grub_dl_segment_t seg;
+      void *addr;
+
+      seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
+      if (! seg)
+	return grub_errno;
+
+      addr = grub_memalign (s->align, s->size);
+      if (! addr)
 	{
-	  grub_dl_segment_t seg;
+	  grub_free (seg);
+	  return grub_errno;
+	}
 
-	  seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
-	  if (! seg)
-	    return grub_errno;
+      grub_memset (addr, 0, s->size);
+      grub_memcpy (addr, (char *) e + s->offset,
+		   (s + 1)->offset - s->offset);
+      seg->addr = addr;
 
-	  if (s->sh_size)
-	    {
-	      void *addr;
-
-	      addr = grub_memalign (s->sh_addralign, s->sh_size);
-	      if (! addr)
-		{
-		  grub_free (seg);
-		  return grub_errno;
-		}
-
-	      switch (s->sh_type)
-		{
-		case SHT_PROGBITS:
-		  grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
-		  break;
-		case SHT_NOBITS:
-		  grub_memset (addr, 0, s->sh_size);
-		  break;
-		}
-
-	      seg->addr = addr;
-	    }
-	  else
-	    seg->addr = 0;
+      seg->size = s->size;
+      seg->section = i;
+      seg->next = mod->segment;
+      mod->segment = seg;
 
-	  seg->size = s->sh_size;
-	  seg->section = i;
-	  seg->next = mod->segment;
-	  mod->segment = seg;
+      if (! i)
+	{
+	  if (e->init_func != GRUB_OBJ_FUNC_NONE)
+	    mod->init = (void (*) (grub_dl_t)) ((char *) addr + e->init_func);
+
+	  if (e->fini_func != GRUB_OBJ_FUNC_NONE)
+	    mod->fini = (void (*) (void)) ((char *) addr + e->fini_func);
 	}
     }
 
@@ -298,92 +266,67 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
 }
 
 static grub_err_t
-grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
+grub_dl_resolve_symbols (grub_dl_t mod, struct grub_obj_header *e)
 {
-  unsigned i;
-  Elf_Shdr *s;
-  Elf_Sym *sym;
-  const char *str;
-  Elf_Word size, entsize;
-
-  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
-    if (s->sh_type == SHT_SYMTAB)
-      break;
+  char *strtab;
+  struct grub_obj_symbol *sym;
+  struct grub_obj_reloc_extern *rel;
 
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
+  strtab = (char *) e + e->string_table;
 
-#ifdef GRUB_MODULES_MACHINE_READONLY
-  mod->symtab = grub_malloc (s->sh_size);
-  memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size);
-#else
-  mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
-#endif
-  sym = mod->symtab;
-  size = s->sh_size;
-  entsize = s->sh_entsize;
+  for (sym = (struct grub_obj_symbol *) ((char *) e + e->symbol_table);
+       sym->segment != GRUB_OBJ_SEGMENT_END; sym++)
+    {
+      char *addr;
 
-  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
-  str = (char *) e + s->sh_offset;
+      addr = grub_dl_get_section_addr (mod, sym->segment);
+      addr += sym->offset;
 
-  for (i = 0;
-       i < size / entsize;
-       i++, sym = (Elf_Sym *) ((char *) sym + entsize))
-    {
-      unsigned char type = ELF_ST_TYPE (sym->st_info);
-      unsigned char bind = ELF_ST_BIND (sym->st_info);
-      const char *name = str + sym->st_name;
+      if (grub_dl_register_symbol (strtab + sym->name, addr, mod))
+	return grub_errno;
+    }
 
-      switch (type)
-	{
-	case STT_NOTYPE:
-	  /* Resolve a global symbol.  */
-	  if (sym->st_name != 0 && sym->st_shndx == 0)
-	    {
-	      sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
-	      if (! sym->st_value)
-		return grub_error (GRUB_ERR_BAD_MODULE,
-				   "the symbol `%s' not found", name);
-	    }
-	  else
-	    sym->st_value = 0;
-	  break;
+  for (rel = (struct grub_obj_reloc_extern *) ((char *) e + e->reloc_table);
+       rel->segment != GRUB_OBJ_SEGMENT_END;)
+    {
+      char *addr, *symbol_addr;
+      int type;
 
-	case STT_OBJECT:
-	  sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
-								sym->st_shndx);
-	  if (bind != STB_LOCAL)
-	    if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
-	      return grub_errno;
-	  break;
+      addr = grub_dl_get_section_addr (mod, rel->segment);
+      addr += rel->offset;
+      type = rel->type;
 
-	case STT_FUNC:
-	  sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
-								sym->st_shndx);
-	  if (bind != STB_LOCAL)
-	    if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
-	      return grub_errno;
-
-	  if (grub_strcmp (name, "grub_mod_init") == 0)
-	    mod->init = (void (*) (grub_dl_t)) sym->st_value;
-	  else if (grub_strcmp (name, "grub_mod_fini") == 0)
-	    mod->fini = (void (*) (void)) sym->st_value;
-	  break;
+      if (rel->symbol_segment == GRUB_OBJ_SEGMENT_END)
+	{
+	  char *name;
+
+	  name = strtab + rel->symbol_name;
+	  symbol_addr = grub_dl_resolve_symbol (name);
+	  if (! symbol_addr)
+	    return grub_error (GRUB_ERR_BAD_MODULE,
+			       "the symbol `%s' not found", name);
+	  rel++;
+	}
+      else
+	{
+	  symbol_addr = grub_dl_get_section_addr (mod, rel->symbol_segment);
+	  rel = (struct grub_obj_reloc_extern *)
+	    ((char *) rel + sizeof (struct grub_obj_reloc));
+	}
 
-	case STT_SECTION:
-	  sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
-							       sym->st_shndx);
+      switch (type)
+	{
+	case GRUB_OBJ_REL_DIR32:
+	  *((grub_uint32_t *) addr) += (grub_uint32_t) symbol_addr;
 	  break;
 
-	case STT_FILE:
-	  sym->st_value = 0;
+	case GRUB_OBJ_REL_REL32:
+	  *((grub_uint32_t *) addr) += (grub_uint32_t) (symbol_addr - addr);
 	  break;
 
 	default:
 	  return grub_error (GRUB_ERR_BAD_MODULE,
-			     "unknown symbol type `%d'", (int) type);
+			     "unknown reloc type %d", type);
 	}
     }
 
@@ -398,74 +341,31 @@ grub_dl_call_init (grub_dl_t mod)
 }
 
 static grub_err_t
-grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
+grub_dl_resolve_dependencies (grub_dl_t mod, char *name)
 {
-  Elf_Shdr *s;
-  const char *str;
-  unsigned i;
-
-  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
-  str = (char *) e + s->sh_offset;
-
-  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
-    if (grub_strcmp (str + s->sh_name, ".modname") == 0)
-      {
-	mod->name = grub_strdup ((char *) e + s->sh_offset);
-	if (! mod->name)
-	  return grub_errno;
-	break;
-      }
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
-
-  return GRUB_ERR_NONE;
-}
-
-static grub_err_t
-grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
-{
-  Elf_Shdr *s;
-  const char *str;
-  unsigned i;
-
-  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
-  str = (char *) e + s->sh_offset;
-
-  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
-    if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
-      {
-	const char *name = (char *) e + s->sh_offset;
-	const char *max = name + s->sh_size;
-
-	while ((name < max) && (*name))
-	  {
-	    grub_dl_t m;
-	    grub_dl_dep_t dep;
-
-	    m = grub_dl_load (name);
-	    if (! m)
-	      return grub_errno;
+  while (1)
+    {
+      grub_dl_t m;
+      grub_dl_dep_t dep;
 
-	    grub_dl_ref (m);
+      name += grub_strlen (name) + 1;
+      if (! *name)
+	return GRUB_ERR_NONE;
 
-	    dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
-	    if (! dep)
-	      return grub_errno;
+      m = grub_dl_load (name);
+      if (! m)
+	return grub_errno;
 
-	    dep->mod = m;
-	    dep->next = mod->dep;
-	    mod->dep = dep;
+      grub_dl_ref (m);
 
-	    name += grub_strlen (name) + 1;
-	  }
-      }
+      dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
+      if (! dep)
+	return grub_errno;
 
-  return GRUB_ERR_NONE;
+      dep->mod = m;
+      dep->next = mod->dep;
+      mod->dep = dep;
+    }
 }
 
 #ifndef GRUB_UTIL
@@ -510,25 +410,18 @@ grub_dl_flush_cache (grub_dl_t mod)
 grub_dl_t
 grub_dl_load_core (void *addr, grub_size_t size)
 {
-  Elf_Ehdr *e;
+  struct grub_obj_header *e;
   grub_dl_t mod;
+  char *name;
 
   grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr,
 		(unsigned long) size);
-  e = addr;
-  if (grub_dl_check_header (e, size))
-    return 0;
-
-  if (e->e_type != ET_REL)
-    {
-      grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
-      return 0;
-    }
 
-  /* Make sure that every section is within the core.  */
-  if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
+  e = addr;
+  if ((e->magic != GRUB_OBJ_HEADER_MAGIC) ||
+      (e->version != GRUB_OBJ_HEADER_VERSION))
     {
-      grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
+      grub_error (GRUB_ERR_BAD_OS, "invalid object file");
       return 0;
     }
 
@@ -536,7 +429,9 @@ grub_dl_load_core (void *addr, grub_size_t size)
   if (! mod)
     return 0;
 
-  mod->name = 0;
+  name = (char *) addr + e->mod_deps;
+
+  mod->name = grub_strdup (name);
   mod->ref_count = 1;
   mod->dep = 0;
   mod->segment = 0;
@@ -544,11 +439,9 @@ grub_dl_load_core (void *addr, grub_size_t size)
   mod->fini = 0;
 
   grub_dprintf ("modules", "relocating to %p\n", mod);
-  if (grub_dl_resolve_name (mod, e)
-      || grub_dl_resolve_dependencies (mod, e)
+  if (grub_dl_resolve_dependencies (mod, name)
       || grub_dl_load_segments (mod, e)
-      || grub_dl_resolve_symbols (mod, e)
-      || grub_arch_dl_relocate_symbols (mod, e))
+      || grub_dl_resolve_symbols (mod, e))
     {
       mod->fini = 0;
       grub_dl_unload (mod);
diff --git a/util/grub-mkmod.c b/util/grub-mkmod.c
new file mode 100644
index 0000000..1ed1b55
--- /dev/null
+++ b/util/grub-mkmod.c
@@ -0,0 +1,176 @@
+/* grub-mkmod.c - tool to generate mod file from object files.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+int
+grub_strcmp (const char *s1, const char *s2)
+{
+  return strcmp (s1, s2);
+}
+
+static struct option options[] = {
+  {"help", no_argument, 0, 'h'},
+  {"version", no_argument, 0, 'V'},
+  {"verbose", no_argument, 0, 'v'},
+  {"output", required_argument, 0, 'o'},
+  {"name", required_argument, 0, 'n'},
+  {0, 0, 0, 0}
+};
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr, "Try ``grub-mkmod --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-mkmod [OPTIONS] [OBJECT_FILES].\n\
+\n\
+Tool to generate mod file from object files.\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\
+  -o, --output=FILE       output a generated image to FILE [default=stdout]\n\
+  -n, --name=NAME         set module name\n\
+\n\
+Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+static void
+add_file (struct grub_util_obj *obj, char *image, int size)
+{
+  if ((! elf_add_file (obj, image, size)) &&
+      (! pe_add_file (obj, image, size)))
+    grub_util_error ("Invalid object format");
+}
+
+static void
+mkmod (char *objs[], char *name, FILE *fp)
+{
+  struct grub_util_obj *obj;
+  int merge = GRUB_OBJ_MERGE_ALL;
+
+  obj = xmalloc_zero (sizeof (*obj));
+  while (*objs)
+    {
+      char *image;
+      int size;
+
+      image = grub_util_read_image (*objs);
+      size = grub_util_get_image_size (*objs);
+      add_file (obj, image, size);
+      free (image);
+      objs++;
+    }
+
+  grub_obj_reverse (obj);
+  grub_obj_sort_segments (obj);
+  grub_obj_merge_segments (obj, merge);
+  grub_obj_reloc_symbols (obj, merge);
+  grub_obj_save (obj, name, fp);
+
+  grub_obj_free (obj);
+}
+
+int
+main (int argc, char *argv[])
+{
+  FILE *fp = stdout;
+  char *output = NULL;
+  char *name = NULL;
+
+  progname = "grub-pe2mod";
+
+  /* Check for options.  */
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "hVvo:n:", 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;
+
+	  case 'o':
+	    if (output)
+	      free (output);
+
+	    output = xstrdup (optarg);
+	    break;
+
+	  case 'n':
+	    if (name)
+	      free (name);
+
+	    name = xstrdup (optarg);
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  if (! name)
+    {
+      if (! output)
+	grub_util_error ("no module name");
+      name = grub_util_get_module_name (output);
+    }
+
+  if (output)
+    {
+      fp = fopen (output, "wb");
+      if (! fp)
+	grub_util_error ("cannot open %s", output);
+      free (output);
+    }
+
+  mkmod (argv + optind, name, fp);
+
+  fclose (fp);
+  free (name);
+
+  return 0;
+}
diff --git a/util/grub-objdump.c b/util/grub-objdump.c
new file mode 100644
index 0000000..9a30362
--- /dev/null
+++ b/util/grub-objdump.c
@@ -0,0 +1,172 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+int
+grub_strcmp (const char *s1, const char *s2)
+{
+  return strcmp (s1, s2);
+}
+
+static struct option options[] = {
+  {"help", no_argument, 0, 'h'},
+  {"version", no_argument, 0, 'V'},
+  {"verbose", no_argument, 0, 'v'},
+  {"all-headers", no_argument, 0, 'x'},
+  {"segment", no_argument, 0, 's'},
+  {"syms", no_argument, 0, 't'},
+  {"reloc", no_argument, 0, 'r'},
+  {0, 0, 0, 0}
+};
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr, "Try ``grub-mkmod --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-objdump [OPTIONS] [MODULE_FILES].\n\
+\n\
+Tool to generate mod file from object files.\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\
+  -x, --all               display all information\n\
+  -s, --segment           display segment information\n\
+  -t, --syms              display symbol table\n\
+  -r, --reloc             display reloc table\n\
+\n\
+Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+#define FLAG_DUMP_SEGMENT	1
+#define FLAG_DUMP_SYMBOL	2
+#define FLAG_DUMP_RELOC		4
+#define FLAG_DUMP_ALL		7
+
+static void
+objdump (char *objs[], int flag)
+{
+  while (*objs)
+    {
+      struct grub_util_obj *obj;
+      char *image;
+      int size;
+
+      image = grub_util_read_image (*objs);
+      size = grub_util_get_image_size (*objs);
+      obj = grub_obj_load (image, size, 1);
+
+      printf ("filename: %s\n", *objs);
+      printf ("mod name: %s\n", image + obj->mod_deps);
+      printf ("mod attr: 0x%x\n", obj->mod_attr);
+      printf ("mod deps: 0x%x\n", obj->mod_deps);
+
+      if (flag & FLAG_DUMP_SEGMENT)
+	{
+	  printf ("\n");
+	  grub_obj_dump_segments (obj);
+	}
+
+      if (flag & FLAG_DUMP_SYMBOL)
+	{
+	  printf ("\n");
+	  grub_obj_dump_symbols (obj);
+	}
+
+      if (flag & FLAG_DUMP_RELOC)
+	{
+	  printf ("\n");
+	  grub_obj_dump_relocs (obj);
+	}
+
+      grub_obj_free (obj);
+      free (image);
+      objs++;
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  int flag = 0;
+
+  progname = "grub-objdump";
+
+  /* Check for options.  */
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "hVvxstr", 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;
+
+	  case 'x':
+	    flag |= FLAG_DUMP_ALL;
+	    break;
+
+	  case 's':
+	    flag |= FLAG_DUMP_SEGMENT;
+	    break;
+
+	  case 't':
+	    flag |= FLAG_DUMP_SYMBOL;
+	    break;
+
+	  case 'r':
+	    flag |= FLAG_DUMP_RELOC;
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  objdump (argv + optind, flag);
+
+  return 0;
+}
diff --git a/util/grub-symdb.c b/util/grub-symdb.c
new file mode 100644
index 0000000..94a0904
--- /dev/null
+++ b/util/grub-symdb.c
@@ -0,0 +1,811 @@
+/* grub-symdb.c - manage symbol database  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#define _GNU_SOURCE	1
+#include <getopt.h>
+
+int
+grub_strcmp (const char *s1, const char *s2)
+{
+  return strcmp (s1, s2);
+}
+
+struct grub_symbol_list
+{
+  struct grub_symbol_list *next;
+  char *name;
+  struct grub_named_list *defs;
+  struct grub_named_list *unds;
+};
+
+static struct grub_symbol_list *symbol_list;
+
+struct grub_update_list
+{
+  struct grub_update_list *next;
+  char *name;
+  struct grub_named_list *add_mods;
+  struct grub_named_list *del_mods;
+  struct grub_named_list *cur_mods;
+};
+
+static struct grub_update_list *update_list;
+
+struct grub_mod_syms
+{
+  struct grub_named_list *defs;
+  struct grub_named_list *unds;
+};
+
+
+void *
+grub_sort_list_find (grub_named_list_t head, const char *name)
+{
+  grub_named_list_t result = 0;
+
+  auto int list_find (grub_named_list_t item);
+  int list_find (grub_named_list_t item)
+    {
+      int r;
+
+      r = strcmp (name, item->name);
+      if (! r)
+	{
+	  result = item;
+	  return 1;
+	}
+
+      return (r < 0);
+    }
+
+  grub_list_iterate (GRUB_AS_LIST (head), (grub_list_hook_t) list_find);
+  return result;
+}
+
+static void
+grub_sort_list_insert (grub_named_list_t *head, grub_named_list_t item)
+{
+  auto int test (grub_named_list_t new_item, grub_named_list_t item);
+  int test (grub_named_list_t new_item, grub_named_list_t item)
+    {
+      return (strcmp (new_item->name, item->name) < 0);
+    }
+
+  grub_list_insert (GRUB_AS_LIST_P (head), GRUB_AS_LIST (item),
+		    (grub_list_test_t) test);
+}
+
+static void
+free_named_list (grub_named_list_t *head)
+{
+  grub_named_list_t cur = *head;
+
+  while (cur)
+    {
+      grub_named_list_t tmp;
+
+      tmp = cur;
+      cur = cur->next;
+      free ((char *) tmp->name);
+      free (tmp);
+    }
+
+  *head = 0;
+}
+
+static int
+remove_string (grub_named_list_t *head, char *name)
+{
+  grub_named_list_t p;
+
+  p = grub_sort_list_find (*head, name);
+  if (p)
+    {
+      grub_list_remove (GRUB_AS_LIST_P (head), GRUB_AS_LIST (p));
+      free ((char *) p->name);
+      free (p);
+
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+insert_string (grub_named_list_t *head, char *name)
+{
+  struct grub_named_list *n;
+
+  n = grub_sort_list_find (*head, name);
+  if (n)
+    return 0;
+
+  n = xmalloc (sizeof (struct grub_named_list));
+  n->name = xstrdup (name);
+  grub_sort_list_insert (head, n);
+
+  return 1;
+}
+
+static struct grub_symbol_list *
+insert_symbol (char *name)
+{
+  struct grub_symbol_list *n;
+
+  n = grub_sort_list_find (GRUB_AS_NAMED_LIST (symbol_list), name);
+  if (! n)
+    {
+      n = xmalloc (sizeof (struct grub_symbol_list));
+      n->name = xstrdup (name);
+      n->defs = 0;
+      n->unds = 0;
+
+      grub_sort_list_insert (GRUB_AS_NAMED_LIST_P (&symbol_list),
+			     GRUB_AS_NAMED_LIST (n));
+    }
+
+  return n;
+}
+
+static struct grub_update_list *
+insert_update (char *name)
+{
+  struct grub_update_list *n;
+
+  n = grub_sort_list_find (GRUB_AS_NAMED_LIST (update_list), name);
+  if (! n)
+    {
+      n = xmalloc (sizeof (struct grub_update_list));
+      n->name = xstrdup (name);
+      n->add_mods = 0;
+      n->del_mods = 0;
+      n->cur_mods = 0;
+
+      grub_sort_list_insert (GRUB_AS_NAMED_LIST_P (&update_list),
+			     GRUB_AS_NAMED_LIST (n));
+    }
+
+  return n;
+}
+
+static void
+add_update (char *m1, char *m2, int is_add)
+{
+  struct grub_update_list *u;
+
+  u = insert_update (m2);
+  if (is_add)
+    {
+      remove_string (&u->del_mods, m1);
+      insert_string (&u->add_mods, m1);
+    }
+  else
+    insert_string (&u->del_mods, m1);
+}
+
+static void
+read_symdb (char *path)
+{
+  FILE *fp;
+  char line[512];
+  struct grub_symbol_list *sym = 0;
+
+  fp = fopen (path, "r");
+  if (! fp)
+    return;
+
+  while (fgets (line, sizeof (line), fp))
+    {
+      char *p;
+
+      p = line + strlen (line) - 1;
+      while ((p >= line) && ((*p == '\r') || (*p == '\n') || (*p == ' ')))
+	p--;
+
+      if (p < line)
+	continue;
+
+      *(p + 1) = 0;
+
+      p = line;
+      while (*p == ' ')
+	p++;
+
+      if (*p == '#')
+	continue;
+
+      if ((*p == '+') || (*p == '-'))
+	{
+	  if (! sym)
+	    grub_util_error ("No current symbol.");
+
+	  insert_string ((*p == '+') ? &sym->defs : &sym->unds, p + 1);
+	}
+      else
+	sym = insert_symbol (p);
+    }
+
+  fclose (fp);
+}
+
+static void
+write_symdb (char *path)
+{
+  FILE *fp;
+  struct grub_symbol_list *sym;
+
+  fp = fopen (path, "w");
+  if (! fp)
+    grub_util_error ("Can\'t write to ", path);
+
+  sym = symbol_list;
+  while (sym)
+    {
+      struct grub_named_list *mod;
+
+      fprintf (fp, "%s\n", sym->name);
+      mod = sym->defs;
+      while (mod)
+	{
+	  fprintf (fp, "+%s\n", mod->name);
+	  mod = mod->next;
+	}
+      mod = sym->unds;
+      while (mod)
+	{
+	  fprintf (fp, "-%s\n", mod->name);
+	  mod = mod->next;
+	}
+
+      sym = sym->next;
+    }
+
+  fclose (fp);
+}
+
+static void
+check_symdb (void)
+{
+  struct grub_symbol_list *sym;
+
+  sym = symbol_list;
+  while (sym)
+    {
+      if (! sym->defs)
+	printf ("undefined: %s\n", sym->name);
+      else if (sym->defs->next)
+	printf ("duplicate: %s\n", sym->name);
+
+      sym = sym->next;
+    }
+}
+
+static void
+read_mod_syms (struct grub_mod_syms *mod_syms, char *path)
+{
+  struct grub_util_obj *obj;
+  char *image;
+  size_t size;
+  struct grub_util_obj_symbol *sym;
+  struct grub_util_obj_reloc *rel;
+
+  mod_syms->defs = 0;
+  mod_syms->unds = 0;
+
+  image = grub_util_read_image (path);
+  size = grub_util_get_image_size (path);
+  obj = grub_obj_load (image, size, 0);
+
+  sym = obj->symbols;
+  while (sym)
+    {
+      insert_string (&mod_syms->defs, sym->name);
+      sym = sym->next;
+    }
+
+  rel = obj->relocs;
+  while (rel)
+    {
+      if (rel->symbol_name)
+	insert_string (&mod_syms->unds, rel->symbol_name);
+      rel = rel->next;
+    }
+
+  grub_obj_free (obj);
+  free (image);
+}
+
+static void
+update_mods (char *mods[], const char *dir)
+{
+  for (; mods[0]; mods++)
+    {
+      char *mod_name, *mod_path;
+      struct grub_mod_syms mod_syms;
+      struct grub_named_list *m;
+
+      mod_name = grub_util_get_module_name (mods[0]);
+      mod_path = grub_util_get_module_path (dir, mod_name);
+
+      if (! strcmp (mod_name, "grub-symdb"))
+	{
+	  free (mod_name);
+	  free (mod_path);
+	  continue;
+	}
+
+      read_mod_syms (&mod_syms, mod_path);
+
+      m = mod_syms.defs;
+      while (m)
+	{
+	  struct grub_symbol_list *sym;
+	  struct grub_named_list *n;
+
+	  sym = insert_symbol ((char *) m->name);
+	  insert_string (&sym->defs, mod_name);
+
+	  n = sym->unds;
+	  while (n)
+	    {
+	      add_update ((char *) mod_name, (char *) n->name, 1);
+	      n = n->next;
+	    }
+
+	  m = m->next;
+	}
+
+      m = mod_syms.unds;
+      while (m)
+	{
+	  struct grub_symbol_list *sym;
+	  struct grub_named_list *n;
+
+	  sym = insert_symbol ((char *) m->name);
+	  insert_string (&sym->unds, mod_name);
+
+	  n = sym->defs;
+	  while (n)
+	    {
+	      add_update ((char *) n->name, (char *) mod_name, 1);
+	      n = n->next;
+	    }
+
+	  m = m->next;
+	}
+
+      free (mod_name);
+      free (mod_path);
+    }
+}
+
+static void
+remove_mods (char *mods[])
+{
+  for (; mods[0]; mods++)
+    {
+      char *mod_name;
+      struct grub_symbol_list *sym;
+
+      mod_name = grub_util_get_module_name (mods[0]);
+
+      sym = symbol_list;
+      while (sym)
+	{
+	  struct grub_named_list *m, *n;
+
+	  m = sym->defs;
+	  while (m)
+	    {
+	      int r;
+
+	      r = strcmp (mod_name, m->name);
+	      if (! r)
+		break;
+
+	      if (r < 0)
+		{
+		  m = 0;
+		  break;
+		}
+
+	      m = m->next;
+	    }
+
+	  n = sym->unds;
+	  while (n)
+	    {
+	      if (m)
+		{
+		  add_update ((char *) m->name, (char *) n->name, 0);
+		}
+	      else
+		{
+		  int r;
+
+		  r = strcmp (mod_name, n->name);
+		  if (! r)
+		    {
+		      m = sym->defs;
+		      while (m)
+			{
+			  add_update ((char *) m->name, (char *) n->name, 0);
+			  m = m->next;
+			}
+
+		      break;
+		    }
+
+		  if (r < 0)
+		    break;
+		}
+
+	      n = n->next;
+	    }
+
+	  sym = sym->next;
+	}
+
+      free (mod_name);
+    }
+}
+
+static void
+dump_update (void)
+{
+  struct grub_update_list *u;
+
+  u = update_list;
+  while (u)
+    {
+      struct grub_named_list *n;
+
+      printf ("%s:" , u->name);
+      n = u->add_mods;
+      while (n)
+	{
+	  printf (" +%s", n->name);
+	  n = n->next;
+	}
+
+      n = u->del_mods;
+      while (n)
+	{
+	  printf (" -%s", n->name);
+	  n = n->next;
+	}
+
+      printf ("\n");
+      u = u->next;
+    }
+}
+
+static void
+update_deps (struct grub_update_list *u, char *path)
+{
+  struct grub_named_list *n;
+  int modified;
+
+  modified = 0;
+  n = u->del_mods;
+  while (n)
+    {
+      modified |= remove_string (&u->cur_mods, (char *) n->name);
+      n = n->next;
+    }
+  n = u->add_mods;
+  while (n)
+    {
+      modified |= insert_string (&u->cur_mods, (char *) n->name);
+      n = n->next;
+    }
+
+  if (modified)
+    {
+      char *image, *p;
+      struct grub_obj_header *hdr;
+      int size;
+      FILE *fp;
+
+      image = grub_util_read_image (path);
+      hdr = (struct grub_obj_header *) image;
+
+      size = hdr->mod_deps;
+      size += strlen (image + size) + 1;
+
+      fp = fopen (path, "wb");
+      if (! fp)
+	grub_util_error ("Can\'t write to %s", path);
+
+      grub_util_write_image (image, size, fp);
+
+      p = image;
+      n = u->cur_mods;
+      while (n)
+	{
+	  strcpy (p, n->name);
+	  p += strlen (p) + 1;
+	  n = n->next;
+	}
+      *(p++) = 0;
+
+      grub_util_write_image (image, p - image, fp);
+
+      fclose (fp);
+      free (image);
+    }
+}
+
+static void
+write_moddep (struct grub_update_list *u, FILE *fp)
+{
+  struct grub_named_list *n;
+
+  if (! u->cur_mods)
+    return;
+
+  fprintf (fp, "%s:", u->name);
+  n = u->cur_mods;
+  while (n)
+    {
+      fprintf (fp, " %s", n->name);
+      n = n->next;
+    }
+
+  fprintf (fp, "\n");
+  free_named_list (&u->cur_mods);
+}
+
+static void
+update_moddep (char *dir)
+{
+  FILE *fp;
+  struct stat st;
+  char *path, *image;
+  struct grub_update_list *u;
+
+  path = grub_util_get_path (dir, "moddep.lst");
+  image = (stat (path, &st) == 0) ? grub_util_read_image (path) : 0;
+
+  fp = fopen (path, "w");
+  if (! fp)
+    grub_util_error ("Can\'t write to ", path);
+
+  if (image)
+    {
+      char *line;
+
+      line = image;
+      while (*line)
+	{
+	  char *p, *c;
+	  int n;
+
+	  n = strcspn (line, "\r\n");
+	  p = line;
+
+	  line += n;
+	  while ((*line == '\r') || (*line == '\n'))
+	    line++;
+
+	  *(p + n) = 0;
+
+	  c = strchr (p, ':');
+	  if (! c)
+	    continue;
+
+	  *c = 0;
+	  u = update_list;
+	  while (u)
+	    {
+	      int r;
+
+	      r = strcmp (p, u->name);
+	      if (! r)
+		break;
+
+	      if (r < 0)
+		{
+		  u = 0;
+		  break;
+		}
+
+	      u = u->next;
+	    }
+	  *c = ':';
+
+	  if (u)
+	    write_moddep (u, fp);
+	  else
+	    fprintf (fp, "%s\n", p);
+	}
+    }
+
+  u = update_list;
+  while (u)
+    {
+      write_moddep (u, fp);
+      u = u->next;
+    }
+
+  fclose (fp);
+  free (path);
+  free (image);
+}
+
+static void
+apply_update (char *dir)
+{
+  struct grub_update_list *u;
+
+  u = update_list;
+  while (u)
+    {
+      char *mod_path;
+
+      mod_path = grub_util_get_module_path (dir, u->name);
+      update_deps (u, mod_path);
+      free (mod_path);
+      u = u->next;
+    }
+
+  update_moddep (dir);
+}
+
+static struct option options[] =
+  {
+    {"directory", required_argument, 0, 'd'},
+    {"test", no_argument, 0, 't'},
+    {"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-symdb --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-symdb [OPTION]... COMMAND\n\
+\n\
+Manage the symbol database of GRUB.\n\
+\nCommands:\n\
+  update MODULES          add/update modules to the symbol database\n\
+  remove MODULES          remove modules from the symbol databsae\n\
+  check                   check for duplicate and unresolved symbols\n\
+\n\
+  -d, --directory=DIR     use images and modules under DIR [default=%s]\n\
+  -t, --test              test mode\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\
+", GRUB_LIBDIR, PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+int
+main (int argc, char *argv[])
+{
+  char *dir = NULL;
+  char *path;
+  int test_mode = 0;
+
+  progname = "grub-symdb";
+
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "d:thVv", options, 0);
+
+      if (c == -1)
+	break;
+      else
+	switch (c)
+	  {
+	  case 'd':
+	    if (dir)
+	      free (dir);
+
+	    dir = xstrdup (optarg);
+	    break;
+
+	  case 't':
+	    test_mode++;
+	    break;
+
+	  case 'h':
+	    usage (0);
+	    break;
+
+	  case 'V':
+	    printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+	    return 0;
+
+	  case 'v':
+	    verbosity++;
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  if (! dir)
+    dir = xstrdup (GRUB_LIBDIR);
+
+  path = grub_util_get_path (dir, "modsym.lst");
+
+  argv += optind;
+  argc -= optind;
+  if (argc == 0)
+    grub_util_error ("No command specified.");
+
+  read_symdb (path);
+  if (! strcmp (argv[0], "update"))
+    {
+      remove_mods (argv + 1);
+      update_mods (argv + 1, dir);
+      if (test_mode)
+	dump_update ();
+      else
+	{
+	  apply_update (dir);
+	  write_symdb (path);
+	}
+    }
+  else if (! strcmp (argv[0], "remove"))
+    {
+      remove_mods (argv + 1);
+      if (test_mode)
+	dump_update ();
+      else
+	{
+	  apply_update (dir);
+	  write_symdb (path);
+	}
+    }
+  else if (! strcmp (argv[0], "check"))
+    {
+      check_symdb ();
+    }
+  else
+    grub_util_error ("Unkown command %s.", argv[0]);
+
+  free (path);
+  free (dir);
+
+  return 0;
+}
diff --git a/util/misc.c b/util/misc.c
index f615a42..955a184 100644
--- a/util/misc.c
+++ b/util/misc.c
@@ -143,6 +143,36 @@ xstrdup (const char *str)
   return dup;
 }
 
+void *
+xmalloc_zero (size_t size)
+{
+  void *p;
+
+  p = xmalloc (size);
+  memset (p, 0, size);
+
+  return p;
+}
+
+void *
+grub_list_reverse (grub_list_t head)
+{
+  grub_list_t prev;
+
+  prev = 0;
+  while (head)
+    {
+      grub_list_t temp;
+
+      temp = head->next;
+      head->next = prev;
+      prev = head;
+      head = temp;
+    }
+
+  return prev;
+}
+
 char *
 grub_util_get_path (const char *dir, const char *file)
 {
@@ -251,6 +281,58 @@ grub_util_write_image (const char *img, size_t size, FILE *out)
     grub_util_error ("write failed");
 }
 
+char *
+grub_util_get_module_name (const char *str)
+{
+  char *base;
+  char *ext;
+
+  base = strrchr (str, '/');
+  if (! base)
+    base = (char *) str;
+  else
+    base++;
+
+  ext = strrchr (base, '.');
+  if (ext && strcmp (ext, ".mod") == 0)
+    {
+      char *name;
+
+      name = xmalloc (ext - base + 1);
+      memcpy (name, base, ext - base);
+      name[ext - base] = '\0';
+      return name;
+    }
+
+  return xstrdup (base);
+}
+
+char *
+grub_util_get_module_path (const char *prefix, const char *str)
+{
+  char *dir;
+  char *base;
+  char *ext;
+  char *ret;
+
+  ext = strrchr (str, '.');
+  if (ext && strcmp (ext, ".mod") == 0)
+    base = xstrdup (str);
+  else
+    {
+      base = xmalloc (strlen (str) + 4 + 1);
+      sprintf (base, "%s.mod", str);
+    }
+
+  dir = strchr (str, '/');
+  if (dir)
+    return base;
+
+  ret = grub_util_get_path (prefix, base);
+  free (base);
+  return ret;
+}
+
 void *
 grub_malloc (grub_size_t size)
 {
diff --git a/util/obj.c b/util/obj.c
new file mode 100644
index 0000000..c83edac
--- /dev/null
+++ b/util/obj.c
@@ -0,0 +1,634 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void
+grub_obj_reverse (struct grub_util_obj *obj)
+{
+  obj->segments = grub_list_reverse (GRUB_AS_LIST (obj->segments));
+  obj->symbols = grub_list_reverse (GRUB_AS_LIST (obj->symbols));
+  obj->relocs = grub_list_reverse (GRUB_AS_LIST (obj->relocs));
+}
+
+void
+grub_obj_sort_segments (struct grub_util_obj *obj)
+{
+  grub_list_t n;
+  int i;
+
+  n = 0;
+  for (i = GRUB_OBJ_SEG_TEXT; i <= GRUB_OBJ_SEG_BSS; i++)
+    {
+      struct grub_util_obj_segment **p, *q;
+
+      for (p = &obj->segments, q = *p; q; q = *p)
+	if (q->segment.type == i)
+	  {
+	    *p = q->next;
+	    grub_list_push (&n, GRUB_AS_LIST (q));
+	  }
+	else
+	  p = &(q->next);
+    }
+
+  obj->segments = grub_list_reverse (n);
+}
+
+static int
+check_merge (struct grub_util_obj_segment *s1,
+	     struct grub_util_obj_segment *s2,
+	     int merge)
+{
+  if (! s2)
+    return 0;
+
+  switch (merge)
+    {
+    case GRUB_OBJ_MERGE_NONE:
+      return (s1 == s2);
+
+    case GRUB_OBJ_MERGE_SAME:
+      return (s1->segment.type == s2->segment.type);
+
+    case GRUB_OBJ_MERGE_ALL:
+      return 1;
+    }
+
+  return 0;
+}
+
+void
+grub_obj_merge_segments (struct grub_util_obj *obj, int merge)
+{
+  struct grub_util_obj_segment *p, *first;
+  grub_uint32_t offset;
+
+  if (merge == GRUB_OBJ_MERGE_NONE)
+    return;
+
+  first = 0;
+  offset = 0;
+  p = obj->segments;
+  while (p)
+    {
+      if (check_merge (p, first, merge))
+	{
+	  grub_uint32_t mask;
+
+	  if (p->segment.align > first->segment.align)
+	    first->segment.align = p->segment.align;
+
+	  mask = p->segment.align - 1;
+	  offset = (offset + mask) & ~mask;
+	  p->segment.offset = offset;
+	  offset += p->segment.size;
+	}
+      else
+	{
+	  first = p;
+	  offset = p->segment.size;
+	}
+
+      p = p->next;
+    }
+}
+
+void
+grub_obj_reloc_symbols (struct grub_util_obj *obj, int merge)
+{
+  struct grub_util_obj_reloc *rel;
+
+  for (rel = obj->relocs; rel; rel = rel->next)
+    {
+      char *addr;
+
+      if (! rel->segment)
+	continue;
+
+      if (! rel->segment->data)
+	grub_util_error ("can\'t relocate in .bss segment");
+
+      addr = rel->segment->data + rel->reloc.offset;
+
+      if (! rel->symbol_segment)
+	{
+	  struct grub_util_obj_symbol *sym;
+
+	  sym = grub_named_list_find (GRUB_AS_NAMED_LIST (obj->symbols),
+				      rel->symbol_name);
+	  if (sym)
+	    {
+	      if (rel->reloc.type & GRUB_OBJ_REL_64BIT)
+		*((grub_uint64_t *) addr) += sym->symbol.offset;
+	      else
+		*((grub_uint32_t *) addr) += sym->symbol.offset;
+
+	      rel->symbol_segment = sym->segment;
+	    }
+	}
+
+      if (rel->symbol_segment)
+	{
+	  grub_uint64_t delta;
+
+	  delta = rel->symbol_segment->segment.offset;
+	  if ((check_merge (rel->segment, rel->symbol_segment, merge)) &&
+	      (rel->reloc.type & GRUB_OBJ_REL_ISREL))
+	    {
+	      delta -= rel->segment->segment.offset + rel->reloc.offset;
+	      rel->segment = 0;
+	    }
+
+	  if (rel->reloc.type & GRUB_OBJ_REL_64BIT)
+	    *((grub_uint64_t *) addr) += delta;
+	  else
+	    *((grub_uint32_t *) addr) += delta;
+	}
+    }
+}
+
+struct grub_strtab
+{
+  struct grub_strtab *next;
+  char *name;
+  int len;
+};
+typedef struct grub_strtab *grub_strtab_t;
+
+static int
+grub_strtab_find (grub_strtab_t head, char *name)
+{
+  int index = 1;
+  int len = strlen (name);
+
+  auto int scan_str (grub_strtab_t item);
+  int scan_str (grub_strtab_t item)
+    {
+      if (item->len >= len)
+	{
+	  int ofs;
+
+	  ofs = item->len - len;
+	  if (! strcmp (item->name + ofs, name))
+	    {
+	      index += ofs;
+	      return 1;
+	    }
+	}
+
+      index += item->len + 1;
+      return 0;
+    }
+
+  if (! grub_list_iterate (GRUB_AS_LIST (head), (grub_list_hook_t) scan_str))
+    index = -index;
+
+  return index;
+}
+
+static void
+grub_strtab_insert (grub_strtab_t *head, char *name)
+{
+  auto int test (grub_strtab_t new_item, grub_strtab_t item);
+  int test (grub_strtab_t new_item, grub_strtab_t item)
+    {
+      return (strcmp (new_item->name, item->name) < 0);
+    }
+
+  grub_strtab_t nitem;
+
+  if (grub_strtab_find (*head, name) > 0)
+    return;
+
+  nitem = xmalloc (sizeof (*nitem));
+  nitem->name = name;
+  nitem->len = strlen (name);
+
+  grub_list_insert (GRUB_AS_LIST_P (head), GRUB_AS_LIST (nitem),
+		    (grub_list_test_t) test);
+}
+
+#define GRUB_OBJ_HEADER_MAX	0xffff
+
+void
+grub_obj_save (struct grub_util_obj *obj, char *mod_name, FILE *fp)
+{
+  char *buf, *p;
+  struct grub_obj_header *hdr;
+  struct grub_util_obj_segment *seg;
+  struct grub_util_obj_symbol *sym;
+  struct grub_util_obj_reloc *rel;
+  int idx;
+  grub_uint32_t offset, raw_size;
+  grub_strtab_t strtab;
+  int strtab_size;
+  int mod_name_len = strlen (mod_name);
+  char mod_init[mod_name_len + 11];
+  char mod_fini[mod_name_len + 11];
+
+  if ((! obj->segments) || (obj->segments->segment.offset))
+    grub_util_error ("invalid segment");
+
+  buf = xmalloc (GRUB_OBJ_HEADER_MAX);
+  hdr = (struct grub_obj_header *) buf;
+
+  hdr->magic = GRUB_OBJ_HEADER_MAGIC;
+  hdr->version = GRUB_OBJ_HEADER_VERSION;
+  hdr->init_func = GRUB_OBJ_FUNC_NONE;
+  hdr->fini_func = GRUB_OBJ_FUNC_NONE;
+
+  idx = 0;
+  offset = 0;
+  raw_size = 0;
+  hdr->segments[0].offset = 0;
+  seg = obj->segments;
+  while (seg)
+    {
+      struct grub_util_obj_segment *cur;
+      grub_uint32_t mask;
+      int is_last;
+
+      cur = seg;
+      seg = seg->next;
+
+      if (! cur->segment.offset)
+	{
+	  if (idx >= GRUB_OBJ_SEGMENT_END)
+	    grub_util_error ("too many segments");
+
+	  hdr->segments[idx].type = cur->segment.type;
+	  hdr->segments[idx].align = cur->segment.align;
+	  hdr->segments[idx].size = 0;
+	  raw_size = 0;
+	}
+
+      cur->index = idx;
+      mask = cur->segment.align - 1;
+      hdr->segments[idx].size = (hdr->segments[idx].size + mask) & ~mask;
+      hdr->segments[idx].size += cur->segment.size;
+
+      is_last = ((! seg) || (! seg->segment.offset));
+
+      if (cur->segment.type != GRUB_OBJ_SEG_BSS)
+	{
+	  raw_size = (raw_size + mask) & ~mask;
+	  raw_size += (is_last) ? cur->raw_size : cur->segment.size;
+	}
+
+      if (is_last)
+	{
+	  offset += raw_size;
+	  idx++;
+	  hdr->segments[idx].offset = offset;
+	}
+    }
+
+  hdr->segments[idx].type = GRUB_OBJ_SEGMENT_END;
+  p = ((char *) &hdr->segments[idx]) + 5;
+
+  sprintf (mod_init, "grub_%s_init", mod_name);
+  sprintf (mod_fini, "grub_%s_fini", mod_name);
+
+  strtab = 0;
+  sym = obj->symbols;
+  while (sym)
+    {
+      if (sym->segment)
+	{
+	  if (! strcmp (sym->name, "grub_mod_init"))
+	    {
+	      if ((sym->symbol.offset >= GRUB_OBJ_HEADER_MAX) ||
+		  (sym->segment->index))
+		grub_util_error ("init function too far");
+
+	      hdr->init_func = sym->symbol.offset;
+	      sym->segment = 0;
+	    }
+	  else if (! strcmp (sym->name, "grub_mod_fini"))
+	    {
+	      if ((sym->symbol.offset >= GRUB_OBJ_HEADER_MAX) ||
+		  (sym->segment->index))
+		grub_util_error ("fini function too far");
+
+	      hdr->fini_func = sym->symbol.offset;
+	      sym->segment = 0;
+	    }
+	  else if ((! strcmp (sym->name, mod_init)) ||
+		   (! strcmp (sym->name, mod_fini)))
+	    {
+	      sym->segment = 0;
+	    }
+	  else
+	    grub_strtab_insert (&strtab, sym->name);
+	}
+      sym = sym->next;
+    }
+
+  rel = obj->relocs;
+  while (rel)
+    {
+      if ((rel->segment) && (! rel->symbol_segment))
+	grub_strtab_insert (&strtab, rel->symbol_name);
+      rel = rel->next;
+    }
+
+  strtab_size = - grub_strtab_find (strtab, "?");
+  if (strtab_size >= GRUB_OBJ_HEADER_MAX)
+    grub_util_error ("string table too large");
+
+  hdr->symbol_table = (p - buf);
+  sym = obj->symbols;
+  while (sym)
+    {
+      if (sym->segment)
+	{
+	  struct grub_obj_symbol *s;
+
+	  s = (struct grub_obj_symbol *) p;
+	  p += sizeof (struct grub_obj_symbol);
+	  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+	    grub_util_error ("symbol table too large");
+
+	  s->segment = sym->segment->index;
+	  s->name = grub_strtab_find (strtab, sym->name);
+	  s->offset = sym->symbol.offset + sym->segment->segment.offset;
+	}
+      sym = sym->next;
+    }
+  *(p++) = GRUB_OBJ_SEGMENT_END;
+  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+    grub_util_error ("symbol table too large");
+
+  hdr->reloc_table = (p - buf);
+  rel = obj->relocs;
+  while (rel)
+    {
+      if (rel->segment)
+	{
+	  struct grub_obj_reloc_extern *r;
+
+	  r = (struct grub_obj_reloc_extern *) p;
+	  p += ((rel->symbol_segment) ? sizeof (struct grub_obj_reloc) :
+		sizeof (struct grub_obj_reloc_extern));
+	  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+	    grub_util_error ("symbol table too large");
+
+	  r->segment = rel->segment->index;
+	  r->type = rel->reloc.type;
+	  r->offset = rel->reloc.offset + rel->segment->segment.offset;
+	  if (rel->symbol_segment)
+	    {
+	      r->symbol_segment = rel->symbol_segment->index;
+	    }
+	  else
+	    {
+	      r->symbol_segment = GRUB_OBJ_SEGMENT_END;
+	      r->symbol_name = grub_strtab_find (strtab, rel->symbol_name);
+	    }
+	}
+      rel = rel->next;
+    }
+  *(p++) = GRUB_OBJ_SEGMENT_END;
+  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+    grub_util_error ("symbol table too large");
+
+  hdr->string_table = (p - buf);
+  offset = strtab_size + hdr->string_table;
+  idx = 0;
+  while (1)
+    {
+      hdr->segments[idx].offset += offset;
+      if (hdr->segments[idx].type == GRUB_OBJ_SEGMENT_END)
+	break;
+      idx++;
+    }
+  hdr->mod_deps = hdr->segments[idx].offset;
+
+  grub_util_write_image (buf, hdr->string_table, fp);
+  free (buf);
+
+  buf = xmalloc (strtab_size);
+  p = buf;
+  *(p++) = 0;
+
+  while (strtab)
+    {
+      grub_strtab_t cur;
+
+      cur = strtab;
+      strtab = strtab->next;
+
+      strcpy (p, cur->name);
+      p += cur->len + 1;
+      free (cur);
+    }
+
+  grub_util_write_image (buf, strtab_size, fp);
+  free (buf);
+
+  buf = xmalloc_zero (256);
+
+  seg = obj->segments;
+  raw_size = 0;
+  while (seg)
+    {
+      struct grub_util_obj_segment *cur;
+
+      cur = seg;
+      seg = seg->next;
+
+      if (! cur->segment.offset)
+	raw_size = 0;
+
+      if (cur->segment.type != GRUB_OBJ_SEG_BSS)
+	{
+	  grub_uint32_t mask, size;
+	  int is_last;
+
+	  mask = cur->segment.align - 1;
+	  size = (raw_size + mask) & ~mask;
+	  if (size != raw_size)
+	    {
+	      if (size - raw_size > 256)
+		grub_util_error ("alignment too large");
+
+	      grub_util_write_image (buf, size - raw_size, fp);
+	    }
+
+	  raw_size = size;
+	  is_last = ((! seg) || (! seg->segment.offset));
+	  size = (is_last) ? cur->raw_size : cur->segment.size;
+	  grub_util_write_image (cur->data, size, fp);
+	  raw_size += size;
+	}
+      else
+	break;
+    }
+
+  strcpy (buf, mod_name);
+  grub_util_write_image (buf, mod_name_len + 2, fp);
+  free (buf);
+}
+
+struct grub_util_obj *
+grub_obj_load (char *image, int size, int load_data)
+{
+  struct grub_util_obj *obj;
+  struct grub_obj_header *hdr;
+  struct grub_obj_symbol *sym;
+  struct grub_obj_reloc_extern *rel;
+  char *strtab;
+  struct grub_util_obj_segment **segments;
+  int i;
+
+  hdr = (struct grub_obj_header *) image;
+
+  if ((size <= (int) sizeof (*hdr)) || (hdr->magic != GRUB_OBJ_HEADER_MAGIC))
+    grub_util_error ("invalid module file");
+
+  if (hdr->version != GRUB_OBJ_HEADER_VERSION)
+    grub_util_error ("version number not match");
+
+  obj = xmalloc_zero (sizeof (*obj));
+  segments = xmalloc_zero (256 * sizeof (segments[0]));
+
+  for (i = 0; hdr->segments[i].type != GRUB_OBJ_SEGMENT_END; i++)
+    {
+      struct grub_util_obj_segment *p;
+
+      p = xmalloc_zero (sizeof (*p));
+      p->segment.type = hdr->segments[i].type;
+      p->segment.align = hdr->segments[i].align;
+      p->segment.size = hdr->segments[i].size;
+      p->file_off = hdr->segments[i].offset;
+      p->raw_size = hdr->segments[i + 1].offset - p->file_off;
+      p->index = i;
+
+      if ((p->raw_size) && (load_data))
+	{
+	  p->data = xmalloc_zero (p->segment.size);
+	  memcpy (p->data, image + p->file_off, p->raw_size);
+	}
+
+      segments[i] = p;
+      grub_list_push (GRUB_AS_LIST_P (&obj->segments), GRUB_AS_LIST (p));
+    }
+
+  obj->mod_attr = hdr->segments[i].offset;
+  obj->mod_deps = hdr->mod_deps;
+
+  strtab = image + hdr->string_table;
+  for (sym = (struct grub_obj_symbol *) (image + hdr->symbol_table);
+       sym->segment != GRUB_OBJ_SEGMENT_END; sym++)
+    {
+      struct grub_util_obj_symbol *p;
+
+      p = xmalloc_zero (sizeof (*p));
+      p->name = xstrdup (strtab + sym->name);
+      p->segment = segments[sym->segment];
+      p->symbol.offset = sym->offset;
+
+      grub_list_push (GRUB_AS_LIST_P (&obj->symbols), GRUB_AS_LIST (p));
+    }
+
+  for (rel = (struct grub_obj_reloc_extern *) (image + hdr->reloc_table);
+       rel->segment != GRUB_OBJ_SEGMENT_END;)
+    {
+      struct grub_util_obj_reloc *p;
+
+      p = xmalloc_zero (sizeof (*p));
+      p->segment = segments[rel->segment];
+      p->reloc.type = rel->type;
+      p->reloc.offset = rel->offset;
+      if (rel->symbol_segment == GRUB_OBJ_SEGMENT_END)
+	{
+	  p->symbol_name = xstrdup (strtab + rel->symbol_name);
+	  rel++;
+	}
+      else
+	{
+	  p->symbol_segment = segments[rel->symbol_segment];
+	  rel = (struct grub_obj_reloc_extern *)
+	    ((char *) rel + sizeof (struct grub_obj_reloc));
+	}
+
+      grub_list_push (GRUB_AS_LIST_P (&obj->relocs), GRUB_AS_LIST (p));
+    }
+
+  free (segments);
+  grub_obj_reverse (obj);
+  return obj;
+}
+
+void
+grub_obj_free (struct grub_util_obj *obj)
+{
+  struct grub_util_obj_segment *seg;
+  struct grub_util_obj_symbol *sym;
+  struct grub_util_obj_reloc *rel;
+
+  seg = obj->segments;
+  while (seg)
+    {
+      struct grub_util_obj_segment *p;
+
+      p = seg;
+      seg = seg->next;
+
+      if (p->data)
+	free (p->data);
+
+      free (p);
+    }
+
+  sym = obj->symbols;
+  while (sym)
+    {
+      struct grub_util_obj_symbol *p;
+
+      p = sym;
+      sym = sym->next;
+
+      if (p->name)
+	free (p->name);
+
+      free (p);
+    }
+
+  rel = obj->relocs;
+  while (sym)
+    {
+      struct grub_util_obj_reloc *p;
+
+      p = rel;
+      rel = rel->next;
+
+      if (p->symbol_name)
+	free (p->symbol_name);
+
+      free (p);
+    }
+}
diff --git a/util/obj_dump.c b/util/obj_dump.c
new file mode 100644
index 0000000..4bfab89
--- /dev/null
+++ b/util/obj_dump.c
@@ -0,0 +1,140 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *
+get_segment_name (int type)
+{
+  switch (type)
+    {
+    case GRUB_OBJ_SEG_TEXT:
+      return ".text";
+
+    case GRUB_OBJ_SEG_DATA:
+      return ".data";
+
+    case GRUB_OBJ_SEG_RDATA:
+      return ".rdata";
+
+    case GRUB_OBJ_SEG_BSS:
+      return ".bss";
+    }
+
+  return "unknown";
+}
+
+static char *
+get_reloc_type (int type)
+{
+  switch (type)
+    {
+    case GRUB_OBJ_REL_DIR32:
+      return "dir32";
+
+    case GRUB_OBJ_REL_REL32:
+      return "rel32";
+
+    case GRUB_OBJ_REL_DIR64:
+      return "dir64";
+
+    case GRUB_OBJ_REL_REL64:
+      return "rel64";
+    }
+
+  return "unknown";
+}
+
+static int
+dump_segments_hook (struct grub_util_obj_segment *obj)
+{
+  printf ("%-10s%08x  %08x  %08x  %08x  %d\n",
+	  get_segment_name (obj->segment.type),
+	  obj->segment.offset, obj->segment.size, obj->raw_size,
+	  obj->file_off, obj->segment.align);
+
+  return 0;
+}
+
+void
+grub_obj_dump_segments (struct grub_util_obj *obj)
+{
+  printf ("Segments:\n"
+	  "Segment   Offset    Size      Raw Size  File Off  Align\n");
+  grub_list_iterate (GRUB_AS_LIST (obj->segments),
+		     (grub_list_hook_t) dump_segments_hook);
+}
+
+static int
+dump_symbols_hook (struct grub_util_obj_symbol *obj)
+{
+  if (obj->segment)
+    printf ("%-10s%08x  %s\n",
+	    get_segment_name (obj->segment->segment.type),
+	    obj->symbol.offset + obj->segment->segment.offset, obj->name);
+
+  return 0;
+}
+
+void
+grub_obj_dump_symbols (struct grub_util_obj *obj)
+{
+  printf ("Symbols:\n"
+	  "Segment   Offset    Name\n");
+  grub_list_iterate (GRUB_AS_LIST (obj->symbols),
+		     (grub_list_hook_t) dump_symbols_hook);
+}
+
+static int
+dump_reloc_hook (struct grub_util_obj_reloc *obj)
+{
+  if (obj->segment)
+    {
+      grub_uint32_t value;
+
+      if (obj->segment->data)
+	value = *((grub_uint32_t *) (obj->segment->data + obj->reloc.offset));
+      else
+	value = 0;
+
+      printf ("%-10s%08x  %08x  %-10s%s\n",
+	      get_segment_name (obj->segment->segment.type),
+	      obj->reloc.offset + obj->segment->segment.offset, value,
+	      get_reloc_type (obj->reloc.type),
+	      ((! obj->symbol_segment) ? obj->symbol_name :
+	       get_segment_name (obj->symbol_segment->segment.type)));
+    }
+
+  return 0;
+}
+
+void
+grub_obj_dump_relocs (struct grub_util_obj *obj)
+{
+  printf ("Relocs:\n"
+	  "Segment   Offset    Value     Type      Name\n");
+  grub_list_iterate (GRUB_AS_LIST (obj->relocs),
+		     (grub_list_hook_t) dump_reloc_hook);
+}
diff --git a/util/obj_elf.c b/util/obj_elf.c
new file mode 100644
index 0000000..3695079
--- /dev/null
+++ b/util/obj_elf.c
@@ -0,0 +1,360 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+#include <grub/elf.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if GRUB_TARGET_SIZEOF_VOID_P == 4
+
+#define grub_target_to_host	grub_target_to_host32
+#define grub_host_to_target	grub_host_to_target32
+
+#elif GRUB_TARGET_SIZEOF_VOID_P == 8
+
+#define grub_target_to_host	grub_target_to_host64
+#define grub_host_to_target	grub_host_to_target64
+
+#endif
+
+static int
+check_elf_header (Elf_Ehdr *e, size_t size)
+{
+  if (size < sizeof (*e)
+      || e->e_ident[EI_MAG0] != ELFMAG0
+      || e->e_ident[EI_MAG1] != ELFMAG1
+      || e->e_ident[EI_MAG2] != ELFMAG2
+      || e->e_ident[EI_MAG3] != ELFMAG3
+      || e->e_ident[EI_VERSION] != EV_CURRENT
+      || e->e_version != EV_CURRENT)
+     return 0;
+
+  return 1;
+}
+
+/* Return the symbol table section, if any.  */
+static Elf_Shdr *
+find_symtab_section (Elf_Shdr *sections,
+		     Elf_Half section_entsize, Elf_Half num_sections)
+{
+  int i;
+  Elf_Shdr *s;
+
+  for (i = 0, s = sections;
+       i < num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
+    if (s->sh_type == grub_cpu_to_le32 (SHT_SYMTAB))
+      return s;
+
+  return 0;
+}
+
+static void
+add_segments (struct grub_util_obj *obj,
+	      struct grub_util_obj_segment **segments,
+	      char *image,
+	      Elf_Shdr *sections, int section_entsize, int num_sections)
+{
+  Elf_Shdr *s;
+  int i;
+
+  for (i = 0, s = sections;
+       i < num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
+    {
+      int type;
+
+      if ((s->sh_flags & grub_cpu_to_le32 (SHF_EXECINSTR | SHF_ALLOC))
+	  == grub_cpu_to_le32 (SHF_EXECINSTR | SHF_ALLOC))
+	type = GRUB_OBJ_SEG_TEXT;
+      else if (s->sh_flags & grub_cpu_to_le32 (SHF_ALLOC)
+	       && ! (s->sh_flags & grub_cpu_to_le32 (SHF_EXECINSTR)))
+	{
+	  if (! (s->sh_flags & grub_cpu_to_le32 (SHF_WRITE)))
+	    type = GRUB_OBJ_SEG_RDATA;
+	  else if (s->sh_type == grub_cpu_to_le32 (SHT_NOBITS))
+	    type = GRUB_OBJ_SEG_BSS;
+	  else
+	    type = GRUB_OBJ_SEG_DATA;
+	}
+      else
+	type = 0;
+
+
+      if ((type) && ((type == GRUB_OBJ_SEG_BSS) || (s->sh_size)))
+	{
+	  struct grub_util_obj_segment *p;
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->segment.type = type;
+	  p->segment.align = grub_le_to_cpu32 (s->sh_addralign);
+	  p->segment.size = grub_le_to_cpu32 (s->sh_size);
+	  segments[i] = p;
+
+	  if (type == GRUB_OBJ_SEG_BSS)
+	    {
+	      p->raw_size = p->segment.size;
+	      if (segments[0])
+		grub_util_error ("mutiple .bss segment");
+	      segments[0] = p;
+	    }
+	  else
+	    {
+	      p->raw_size = p->segment.size;
+	      p->data = xmalloc (p->raw_size);
+	      memcpy (p->data, image + grub_le_to_cpu32 (s->sh_offset),
+		      p->raw_size);
+	      segments[i] = p;
+	      grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+			      GRUB_AS_LIST (p));
+
+	    }
+	}
+    }
+
+  if (! segments[0])
+    grub_util_error ("no .bss segment");
+}
+
+static void
+add_symbols (struct grub_util_obj *obj,
+	     struct grub_util_obj_segment **segments,
+	     char *image,
+	     Elf_Shdr *sections, int section_entsize, int num_sections)
+{
+  int i;
+  Elf_Shdr *symtab_section, *str_sec;
+  Elf_Sym *sym;
+  int num_syms, sym_size;
+  char *strtab;
+
+  symtab_section = find_symtab_section (sections,
+					section_entsize, num_sections);
+  sym = (Elf_Sym *) (image + grub_target_to_host (symtab_section->sh_offset));
+  sym_size = grub_target_to_host32 (symtab_section->sh_entsize);
+  num_syms = grub_target_to_host32 (symtab_section->sh_size) / sym_size;
+  str_sec = (Elf_Shdr *) ((char *) sections
+			  + (grub_target_to_host32 (symtab_section->sh_link)
+			     * section_entsize));
+  strtab = image + grub_target_to_host32 (str_sec->sh_offset);
+
+  for (i = 0; i < num_syms;
+       i++, sym = (Elf_Sym *) ((char *) sym + sym_size))
+    {
+      Elf_Section index;
+      char *name;
+
+      name = strtab + grub_target_to_host32 (sym->st_name);
+
+      if ((ELF_ST_BIND (sym->st_info) == STB_LOCAL) &&
+	  (strcmp (name, "grub_mod_init")) &&
+	  (strcmp (name, "grub_mod_fini")))
+	continue;
+
+      index = grub_target_to_host16 (sym->st_shndx);
+
+      if (index == STN_UNDEF)
+	continue;
+
+      if (index == STN_COMMON)
+	{
+	  if (! sym->st_value)
+	    continue;
+	  index = 0;
+	}
+
+      if ((index < num_sections) && (segments[index]))
+	{
+	  struct grub_util_obj_symbol *p;
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->name = xstrdup (name);
+	  p->segment = segments[index];
+	  if (! index)
+	    {
+	      p->symbol.offset = p->segment->segment.size;
+	      p->segment->segment.size += grub_target_to_host (sym->st_value);
+	    }
+	  else
+	    p->symbol.offset = grub_target_to_host (sym->st_value);
+
+	  grub_list_push (GRUB_AS_LIST_P (&obj->symbols),
+			  GRUB_AS_LIST (p));
+	}
+    }
+}
+
+static void
+add_relocs (struct grub_util_obj *obj,
+	    struct grub_util_obj_segment **segments,
+	    char *image,
+	    Elf_Shdr *sections, int section_entsize, int num_sections)
+{
+  int i;
+  Elf_Shdr *s;
+
+  for (i = 0, s = sections;
+       i < num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
+    if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) ||
+	(s->sh_type == grub_cpu_to_le32 (SHT_RELA)))
+      {
+	Elf_Rela *r;
+	Elf_Shdr *sym_sec, *str_sec;
+	int sym_size;
+	char *strtab;
+	Elf_Word r_size, num_rs, j;
+	Elf_Word target_index;
+
+	sym_sec = (Elf_Shdr *) ((char *) sections
+				+ (grub_target_to_host32 (s->sh_link)
+				   * section_entsize));
+	sym_size = grub_target_to_host32 (sym_sec->sh_entsize);
+
+	str_sec = (Elf_Shdr *) ((char *) sections
+				+ (grub_target_to_host32 (sym_sec->sh_link)
+				   * section_entsize));
+	strtab = image + grub_target_to_host32 (str_sec->sh_offset);
+
+	target_index = grub_target_to_host32 (s->sh_info);
+	if (! segments[target_index])
+	  continue;
+
+	r_size = grub_target_to_host32 (s->sh_entsize);
+	num_rs = grub_target_to_host32 (s->sh_size) / r_size;
+
+	r = (Elf_Rela *) (image + grub_target_to_host32 (s->sh_offset));
+	for (j = 0;
+	     j < num_rs;
+	     j++, r = (Elf_Rela *) ((char *) r + r_size))
+	  {
+	    struct grub_util_obj_reloc *p;
+	    Elf_Addr info, offset;
+	    Elf_Sym *sym;
+	    int sym_idx;
+	    char *addr;
+	    int type;
+
+	    offset = grub_target_to_host (r->r_offset);
+	    if (! segments[target_index]->data)
+	      grub_util_error ("can\'t relocate in .bss segment");
+
+	    addr = segments[target_index]->data + offset;
+	    info = grub_target_to_host (r->r_info);
+
+	    type = -1;
+	    switch (ELF_R_TYPE (info))
+	      {
+	      case R_386_NONE:
+		break;
+
+	      case R_386_32:
+		type = GRUB_OBJ_REL_DIR32;
+		break;
+
+	      case R_386_PC32:
+		type = GRUB_OBJ_REL_REL32;
+		break;
+
+	      default:
+		grub_util_error ("unknown relocation type %d",
+				 ELF_R_TYPE (info));
+	      }
+
+	    if (type < 0)
+	      continue;
+
+	    if ((grub_target_to_host32 (s->sh_type) == SHT_RELA) &&
+		(r->r_addend))
+	      {
+		if (type & GRUB_OBJ_REL_64BIT)
+		  *((grub_uint64_t *) addr) += r->r_addend;
+		else
+		  *((grub_uint32_t *) addr) += r->r_addend;
+	      }
+
+	    p = xmalloc_zero (sizeof (*p));
+	    p->segment = segments[target_index];
+	    p->reloc.type = type;
+	    p->reloc.offset = offset;
+
+	    sym = (Elf_Sym *) (image
+			       + grub_target_to_host32 (sym_sec->sh_offset)
+			       + (ELF_R_SYM (info) * sym_size));
+	    sym_idx = grub_target_to_host16 (sym->st_shndx);
+	    if (sym_idx == STN_ABS)
+	      grub_util_error ("can\'t relocate absolute symbol");
+
+	    if ((sym_idx != STN_UNDEF) && (sym_idx != STN_COMMON))
+	      {
+		if (! segments[sym_idx])
+		  grub_util_error ("no symbol segment");
+
+		p->symbol_segment = segments[sym_idx];
+	      }
+	    p->symbol_name = xstrdup (strtab +
+				      grub_target_to_host32 (sym->st_name));
+
+	    grub_list_push (GRUB_AS_LIST_P (&obj->relocs),
+			    GRUB_AS_LIST (p));
+	  }
+      }
+}
+
+int
+elf_add_file (struct grub_util_obj *obj, char *image, int size)
+{
+  Elf_Ehdr *e;
+  Elf_Shdr *sections;
+  Elf_Off section_offset;
+  Elf_Half section_entsize;
+  Elf_Half num_sections;
+  struct grub_util_obj_segment **segments;
+
+  e = (Elf_Ehdr *) image;
+  if (! check_elf_header (e, size))
+    return 0;
+
+  section_offset = grub_target_to_host (e->e_shoff);
+  section_entsize = grub_target_to_host16 (e->e_shentsize);
+  num_sections = grub_target_to_host16 (e->e_shnum);
+
+  if (size < (int) (section_offset + section_entsize * num_sections))
+    grub_util_error ("invalid ELF format");
+
+  sections = (Elf_Shdr *) (image + section_offset);
+  segments = xmalloc_zero (num_sections * sizeof (segments[0]));
+  add_segments (obj, segments, image, sections, section_entsize, num_sections);
+  add_symbols (obj, segments, image, sections, section_entsize, num_sections);
+  add_relocs (obj, segments, image, sections, section_entsize, num_sections);
+
+  if (segments[0]->segment.size)
+    grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+		    GRUB_AS_LIST (segments[0]));
+  else
+    free (segments[0]);
+
+  free (segments);
+  return 1;
+}
diff --git a/util/obj_pe.c b/util/obj_pe.c
new file mode 100644
index 0000000..e168fb1
--- /dev/null
+++ b/util/obj_pe.c
@@ -0,0 +1,272 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+#include <grub/efi/pe32.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+add_segments (struct grub_util_obj *obj,
+	      struct grub_util_obj_segment **segments,
+	      char *image,
+	      struct grub_pe32_section_table *pe_shdr, int num_secs)
+{
+  int i;
+
+  for (i = 0; i < num_secs; i++, pe_shdr++)
+    {
+      int type;
+
+      if (! strcmp (pe_shdr->name, ".text"))
+	type = GRUB_OBJ_SEG_TEXT;
+      else if (! strcmp (pe_shdr->name, ".data"))
+	type = GRUB_OBJ_SEG_DATA;
+      else if (! strcmp (pe_shdr->name, ".rdata"))
+	type = GRUB_OBJ_SEG_RDATA;
+      else if (! strcmp (pe_shdr->name, ".bss"))
+	type = GRUB_OBJ_SEG_BSS;
+      else
+	type = 0;
+
+      if ((type) && ((type == GRUB_OBJ_SEG_BSS) || (pe_shdr->raw_data_size)))
+	{
+	  struct grub_util_obj_segment *p;
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->segment.type = type;
+	  p->segment.align = 1 << (((pe_shdr->characteristics >>
+				     GRUB_PE32_SCN_ALIGN_SHIFT) &
+				    GRUB_PE32_SCN_ALIGN_MASK) - 1);
+	  p->segment.size = pe_shdr->raw_data_size;
+	  segments[i + 1] = p;
+
+	  if (type == GRUB_OBJ_SEG_BSS)
+	    {
+	      p->raw_size = p->segment.size;
+	      if (segments[0])
+		grub_util_error ("mutiple .bss segment");
+	      segments[0] = p;
+	    }
+	  else
+	    {
+	      p->raw_size = p->segment.size;
+	      p->data = xmalloc (pe_shdr->raw_data_size);
+	      memcpy (p->data, image + pe_shdr->raw_data_offset,
+		      pe_shdr->raw_data_size);
+
+	      grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+			      GRUB_AS_LIST (p));
+	    }
+	}
+    }
+
+  if (! segments[0])
+    grub_util_error ("no .bss segment");
+}
+
+static char *
+get_symbol_name (struct grub_pe32_symbol *pe_sym, char *pe_strtab)
+{
+  char short_name[9];
+  char *name;
+
+  if (pe_sym->long_name[0])
+    {
+      strncpy (short_name, pe_sym->short_name, 8);
+      short_name[8] = 0;
+      name = short_name;
+    }
+  else
+    name = pe_strtab + pe_sym->long_name[1];
+
+  if (*name == '_')
+    name++;
+
+  return xstrdup (name);
+}
+
+static void
+add_symbols (struct grub_util_obj *obj,
+	     struct grub_util_obj_segment **segments,
+	     struct grub_pe32_symbol *pe_symtab, int num_syms,
+	     char *pe_strtab)
+{
+  int i;
+
+  for (i = 0; i < num_syms;
+       i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
+    {
+      struct grub_util_obj_symbol *p;
+      char *name;
+
+      name = get_symbol_name (pe_symtab, pe_strtab);
+
+      if ((pe_symtab->section > num_syms) ||
+	  (! segments[pe_symtab->section]) ||
+	  ((pe_symtab->storage_class != GRUB_PE32_SYM_CLASS_EXTERNAL) &&
+	   (strcmp (name, "grub_mod_init")) &&
+	   (strcmp (name, "grub_mod_fini"))))
+	{
+	  free (name);
+	  continue;
+	}
+
+      if ((! pe_symtab->section) && (! pe_symtab->value))
+	{
+	  free (name);
+	  continue;
+	}
+
+      p = xmalloc_zero (sizeof (*p));
+      p->name = name;
+      p->segment = segments[pe_symtab->section];
+
+      if (! pe_symtab->section)
+	{
+	  p->symbol.offset = p->segment->segment.size;
+	  p->segment->segment.size += pe_symtab->value;
+	}
+      else
+	p->symbol.offset = pe_symtab->value;
+
+      grub_list_push (GRUB_AS_LIST_P (&obj->symbols), GRUB_AS_LIST (p));
+    }
+}
+
+static void
+add_relocs (struct grub_util_obj *obj,
+	    struct grub_util_obj_segment **segments,
+	    char *image,
+	    struct grub_pe32_section_table *pe_sec, int num_secs,
+	    struct grub_pe32_symbol *pe_symtab, int num_syms,
+	    char *pe_strtab)
+{
+  int i;
+
+  for (i = 0; i < num_secs; i++, pe_sec++)
+    {
+      struct grub_pe32_reloc *pe_rel;
+      int j;
+
+      if (! segments[i + 1])
+	continue;
+
+      pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
+      for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
+	{
+	  struct grub_util_obj_reloc *p;
+	  struct grub_pe32_symbol *pe_sym;
+	  int type;
+
+	  pe_sym = pe_symtab + pe_rel->symtab_index;
+
+	  if (((int) pe_rel->symtab_index >= num_syms) ||
+	      ((pe_sym->section) && (! segments[pe_sym->section])))
+	    grub_util_error ("invalid symbol index");
+
+	  if (pe_rel->type == GRUB_PE32_REL_I386_DIR32)
+	    type = GRUB_OBJ_REL_DIR32;
+	  else if (pe_rel->type == GRUB_PE32_REL_I386_REL32)
+	    type = GRUB_OBJ_REL_REL32;
+	  else
+	    grub_util_error ("unknown pe relocation type %d\n", pe_rel->type);
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->segment = segments[i + 1];
+	  p->reloc.type = type;
+	  p->reloc.offset = pe_rel->offset - pe_sec->virtual_address;
+	  if (pe_sym->section)
+	    p->symbol_segment = segments[pe_sym->section];
+	  p->symbol_name = get_symbol_name (pe_sym, pe_strtab);
+
+	  if (! p->segment->data)
+	    grub_util_error ("can\'t relocate in .bss segment");
+
+	  if (type & GRUB_OBJ_REL_ISREL)
+	    {
+	      grub_uint8_t code;
+	      grub_uint32_t *addr;
+
+	      addr = (grub_uint32_t *) (p->segment->data + p->reloc.offset);
+	      code = p->segment->data[p->reloc.offset - 1];
+	      if (((code != 0xe8) && (code != 0xe9)) || (*addr))
+		grub_util_error ("invalid relocation (%x %x)", code, *addr);
+
+	      if (p->reloc.type & GRUB_OBJ_REL_64BIT)
+		*((grub_uint64_t *) addr) = -4;
+	      else
+		*((grub_uint32_t *) addr) = -4;
+	    }
+
+	  grub_list_push (GRUB_AS_LIST_P (&obj->relocs), GRUB_AS_LIST (p));
+	}
+    }
+}
+
+static int
+check_pe_header (struct grub_pe32_coff_header *c, size_t size)
+{
+  if ((size < sizeof (*c) ||
+       (grub_le_to_cpu16 (c->machine) != GRUB_PE32_MACHINE_I386)))
+    return 0;
+
+  return 1;
+}
+
+int
+pe_add_file (struct grub_util_obj *obj, char *image, int size)
+{
+  struct grub_pe32_coff_header *pe_chdr;
+  struct grub_pe32_section_table *pe_shdr;
+  struct grub_pe32_symbol *pe_symtab;
+  int num_secs, num_syms;
+  char *pe_strtab;
+  struct grub_util_obj_segment **segments;
+
+  pe_chdr = (struct grub_pe32_coff_header *) image;
+  if (! check_pe_header (pe_chdr, size))
+    return 0;
+
+  pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1);
+  num_secs = pe_chdr->num_sections;
+  segments = xmalloc_zero ((num_secs + 1) * sizeof (segments[0]));
+  add_segments (obj, segments, image, pe_shdr, num_secs);
+
+  pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
+  num_syms = pe_chdr->num_symbols;
+  pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
+
+  add_symbols (obj, segments, pe_symtab, num_syms, pe_strtab);
+  add_relocs (obj, segments, image, pe_shdr, num_secs,
+	      pe_symtab, num_syms, pe_strtab);
+
+  if (segments[0]->segment.size)
+    grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+		    GRUB_AS_LIST (segments[0]));
+  else
+    free (segments[0]);
+
+  free (segments);
+  return 1;
+}
diff --git a/util/resolve.c b/util/resolve.c
index 8b33beb..32c9e82 100644
--- a/util/resolve.c
+++ b/util/resolve.c
@@ -126,58 +126,6 @@ read_dep_list (FILE *fp)
   return dep_list;
 }
 
-static char *
-get_module_name (const char *str)
-{
-  char *base;
-  char *ext;
-
-  base = strrchr (str, '/');
-  if (! base)
-    base = (char *) str;
-  else
-    base++;
-
-  ext = strrchr (base, '.');
-  if (ext && strcmp (ext, ".mod") == 0)
-    {
-      char *name;
-
-      name = xmalloc (ext - base + 1);
-      memcpy (name, base, ext - base);
-      name[ext - base] = '\0';
-      return name;
-    }
-
-  return xstrdup (base);
-}
-
-static char *
-get_module_path (const char *prefix, const char *str)
-{
-  char *dir;
-  char *base;
-  char *ext;
-  char *ret;
-
-  ext = strrchr (str, '.');
-  if (ext && strcmp (ext, ".mod") == 0)
-    base = xstrdup (str);
-  else
-    {
-      base = xmalloc (strlen (str) + 4 + 1);
-      sprintf (base, "%s.mod", str);
-    }
-
-  dir = strchr (str, '/');
-  if (dir)
-    return base;
-
-  ret = grub_util_get_path (prefix, base);
-  free (base);
-  return ret;
-}
-
 static void
 add_module (const char *dir,
 	    struct dep_list *dep_list,
@@ -190,7 +138,7 @@ add_module (const char *dir,
   struct mod_list *mod;
   struct dep_list *dep;
 
-  mod_name = get_module_name (name);
+  mod_name = grub_util_get_module_name (name);
 
   /* Check if the module has already been added.  */
   for (mod = *mod_head; mod; mod = mod->next)
@@ -218,7 +166,7 @@ add_module (const char *dir,
 
   /* Add this path.  */
   path = (struct grub_util_path_list *) xmalloc (sizeof (*path));
-  path->name = get_module_path (dir, name);
+  path->name = grub_util_get_module_path (dir, name);
   path->next = *path_head;
   *path_head = path;
 }

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* please stop this
  2009-07-14 15:49 [PATCH] New object format Bean
@ 2009-07-14 17:57 ` Robert Millan
  2009-07-14 18:09   ` Pavel Roskin
  2009-07-14 21:02 ` [PATCH] New object format Bean
  2009-07-19  1:52 ` Isaac Dupree
  2 siblings, 1 reply; 25+ messages in thread
From: Robert Millan @ 2009-07-14 17:57 UTC (permalink / raw)
  To: The development of GRUB 2; +Cc: Bean, Yoshinori K. Okuji

On Tue, Jul 14, 2009 at 11:49:37PM +0800, Bean wrote:
> Hi,
> 
> This patch implement a new object format, the advantages are:

Hi Bean,

Last time this was discussed [1], there was no consensus that we should switch
to a new object format.  Furthermore, Marco had some objections:

  http://lists.gnu.org/archive/html/grub-devel/2009-07/msg00101.html

and I'm growing increasingly worried about this tendency.  Just about every
week we have to discuss a proposal that significantly affects core
functionality, which may later be implemented and sent as a patch no matter
what we have discussed, and if left unattended, merged in SVN, and afterwards
we and our users have and additional burden of finding new bugs and fixing
them.

I agree that we have a problem due to lack of leadership, but this is not
acceptable.  Marco is busy right now (traveling), so please put this on hold
untill he's back, then we can discuss it.

Don't take me wrong, Bean I really appreciate your contribution to GRUB, but
you see it as an experimentation ground, and GRUB is not a research project.
PUPA was, but GRUB aims to be a stable bootloader.

[1] http://lists.gnu.org/archive/html/grub-devel/2009-07/msg00098.html

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: please stop this
  2009-07-14 17:57 ` please stop this Robert Millan
@ 2009-07-14 18:09   ` Pavel Roskin
  2009-07-14 18:17     ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Pavel Roskin @ 2009-07-14 18:09 UTC (permalink / raw)
  To: The development of GRUB 2

On Tue, 2009-07-14 at 19:57 +0200, Robert Millan wrote:

> I agree that we have a problem due to lack of leadership, but this is not
> acceptable.  Marco is busy right now (traveling), so please put this on hold
> untill he's back, then we can discuss it.

I agree that we should not rush with such changes.  However, publishing
patches for discussion and testing is a good thing and should not be
discouraged.

-- 
Regards,
Pavel Roskin



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: please stop this
  2009-07-14 18:09   ` Pavel Roskin
@ 2009-07-14 18:17     ` Bean
  2009-07-16 15:23       ` Robert Millan
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-14 18:17 UTC (permalink / raw)
  To: The development of GRUB 2

On Wed, Jul 15, 2009 at 2:09 AM, Pavel Roskin<proski@gnu.org> wrote:
> On Tue, 2009-07-14 at 19:57 +0200, Robert Millan wrote:
>
>> I agree that we have a problem due to lack of leadership, but this is not
>> acceptable.  Marco is busy right now (traveling), so please put this on hold
>> untill he's back, then we can discuss it.
>
> I agree that we should not rush with such changes.  However, publishing
> patches for discussion and testing is a good thing and should not be
> discouraged.

Hi,

Yeah, I don't mean to push it. In fact, I've created a git repository
for my temporary work

http://repo.or.cz/w/grub2/bean.git

The new object format and library support would be committed there in
case someone is interested.

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-14 15:49 [PATCH] New object format Bean
  2009-07-14 17:57 ` please stop this Robert Millan
@ 2009-07-14 21:02 ` Bean
  2009-07-19  1:52 ` Isaac Dupree
  2 siblings, 0 replies; 25+ messages in thread
From: Bean @ 2009-07-14 21:02 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 1231 bytes --]

Hi,

Ok, find the bug for elf converter. elf and pe are both ok now.

On Tue, Jul 14, 2009 at 11:49 PM, Bean<bean123ch@gmail.com> wrote:
> Hi,
>
> This patch implement a new object format, the advantages are:
>
> Reduce size dramatically, some result:
>
> Size of all modules:
>
> original: 645575
> new: 519093
>
> Dynamic loader:
>
> original
>   text    data     bss     dec     hex filename
>    412       0       0     412     19c kernel_img-kern___target_cpu__dl.o
>   3052       0    2064    5116    13fc kernel_img-kern_dl.o
>
> new:
>   text    data     bss     dec     hex filename
>   2272       0    2064    4336    10f0 kernel_img-kern_dl.o
>
> Greatly simplify the build process:
>
> 1, compile *.c, *.S to *.o
> 2, use grub-mkmod to merge *.o file to mod
> 3, use grub-symdb to maintain module dependence
>
> so there is no pre-* mod-* def-* und-* anymore.
>
> grub-mkmod support both pe and elf format, no need to convert with grub-pe2elf.
>
> BTW, this patch breaks other platform. And it seems there is some bug
> in elf->mod converter. But the pe->mod works quite nice, I can load
> graphic menu and even run osdetect script with lua.
>
> --
> Bean
>



-- 
Bean

[-- Attachment #2: obj.diff --]
[-- Type: text/x-patch, Size: 88142 bytes --]

diff --git a/Makefile.in b/Makefile.in
index 3d208e7..d984872 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -147,11 +147,9 @@ include $(srcdir)/conf/$(target_cpu)-$(platform).mk
 ### General targets.
 
 CLEANFILES += $(pkglib_DATA) $(pkgdata_DATA)
-pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst
-moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep.awk
-	cat $(DEFSYMFILES) /dev/null \
-	  | $(AWK) -f $(srcdir)/genmoddep.awk $(UNDSYMFILES) > $@ \
-	  || (rm -f $@; exit 1)
+pkglib_DATA += modsym.lst command.lst fs.lst partmap.lst parttool.lst handler.lst
+modsym.lst: $(MODFILES) grub-symdb
+	./grub-symdb -d . update $?
 
 command.lst: $(COMMANDFILES)
 	cat $^ /dev/null | sort > $@
diff --git a/conf/common.rmk b/conf/common.rmk
index 07ff04e..ddb368e 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -125,6 +125,23 @@ endif
 grub_pe2elf_SOURCES = util/grub-pe2elf.c util/misc.c
 CLEANFILES += grub-pe2elf
 
+# grub-mkmod
+bin_UTILITIES += grub-mkmod
+grub_mkmod_SOURCES = util/grub-mkmod.c util/misc.c util/obj.c \
+	util/obj_pe.c util/obj_elf.c kern/list.c
+CLEANFILES += grub-mkmod
+
+# grub-symdb
+bin_UTILITIES += grub-symdb
+grub_symdb_SOURCES = util/grub-symdb.c util/obj.c util/misc.c kern/list.c
+CLEANFILES += grub-symdb
+
+# grub-objdump
+bin_UTILITIES += grub-objdump
+grub_objdump_SOURCES = util/grub-objdump.c util/obj.c util/misc.c \
+	util/obj_dump.c kern/list.c
+CLEANFILES += grub-objdump
+
 # grub_macho2img assumes a lot about source file.
 # So installing it definitively is useless
 # But adding to bin_UTILITIES is needed for
@@ -512,15 +529,15 @@ sh_mod_CFLAGS = $(COMMON_CFLAGS)
 sh_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For lua.mod.
-lua_mod_SOURCES = script/lua/lapi.c script/lua/lcode.c script/lua/ldebug.c \
+lua_mod_SOURCES = script/lua/grub_main.c script/lua/grub_lib.c \
+	script/lua/lapi.c script/lua/lcode.c script/lua/ldebug.c \
 	script/lua/ldo.c script/lua/ldump.c script/lua/lfunc.c \
 	script/lua/lgc.c script/lua/llex.c script/lua/lmem.c \
 	script/lua/lobject.c script/lua/lopcodes.c script/lua/lparser.c \
 	script/lua/lstate.c script/lua/lstring.c script/lua/ltable.c \
 	script/lua/ltm.c script/lua/lundump.c script/lua/lvm.c \
 	script/lua/lzio.c script/lua/lauxlib.c script/lua/lbaselib.c \
-	script/lua/linit.c script/lua/ltablib.c script/lua/lstrlib.c \
-	script/lua/grub_main.c script/lua/grub_lib.c
+	script/lua/linit.c script/lua/ltablib.c script/lua/lstrlib.c
 lua_mod_CFLAGS = $(COMMON_CFLAGS)
 lua_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk
index f1915b6..185ffd4 100644
--- a/conf/i386-pc.rmk
+++ b/conf/i386-pc.rmk
@@ -53,7 +53,7 @@ kernel_img_SOURCES = kern/i386/pc/startup.S \
 	kern/misc.c kern/mm.c kern/reader.c kern/term.c \
 	kern/rescue_parser.c kern/rescue_reader.c \
 	kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \
-	kern/$(target_cpu)/dl.c kern/i386/pc/init.c kern/i386/pc/mmap.c \
+	kern/i386/pc/init.c kern/i386/pc/mmap.c \
 	kern/parser.c kern/partition.c \
 	kern/i386/tsc.c kern/i386/pit.c \
 	kern/generic/rtc_get_time_ms.c \
diff --git a/genmk.rb b/genmk.rb
index e3866c1..39141f4 100644
--- a/genmk.rb
+++ b/genmk.rb
@@ -106,60 +106,16 @@ class PModule
     objs_str = objs.join(' ')
     deps = objs.collect {|obj| obj.suffix('d')}
     deps_str = deps.join(' ')
-    pre_obj = 'pre-' + @name.suffix('o')
-    mod_src = 'mod-' + @name.suffix('c')
-    mod_obj = mod_src.suffix('o')
-    defsym = 'def-' + @name.suffix('lst')
-    undsym = 'und-' + @name.suffix('lst')
     mod_name = File.basename(@name, '.mod')
     symbolic_name = mod_name.sub(/\.[^\.]*$/, '')
 
-    "CLEANFILES += #{@name} #{mod_obj} #{mod_src} #{pre_obj} #{objs_str} #{undsym}
-ifneq ($(#{prefix}_EXPORTS),no)
-CLEANFILES += #{defsym}
-DEFSYMFILES += #{defsym}
-endif
+    "CLEANFILES += #{@name} #{objs_str}
 MOSTLYCLEANFILES += #{deps_str}
-UNDSYMFILES += #{undsym}
-
-ifneq ($(TARGET_APPLE_CC),1)
-#{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF)
-	-rm -f $@
-	$(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{pre_obj} #{mod_obj}
-	if test ! -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 $@
-else
-#{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF)
-	-rm -f $@
-	-rm -f $@.bin
-	$(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@.bin #{pre_obj} #{mod_obj}
-	$(OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -wd1106 -nu -nd $@.bin $@
-	-rm -f $@.bin
-endif
-
-#{pre_obj}: $(#{prefix}_DEPENDENCIES) #{objs_str}
-	-rm -f $@
-	$(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{objs_str}
 
-#{mod_obj}: #{mod_src}
-	$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(#{prefix}_CFLAGS) -c -o $@ $<
-
-#{mod_src}: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh
-	sh $(srcdir)/genmodsrc.sh '#{mod_name}' $< > $@ || (rm -f $@; exit 1)
-
-ifneq ($(#{prefix}_EXPORTS),no)
-ifneq ($(TARGET_APPLE_CC),1)
-#{defsym}: #{pre_obj}
-	$(NM) -g --defined-only -P -p $< | sed 's/^\\([^ ]*\\).*/\\1 #{mod_name}/' > $@
-else
-#{defsym}: #{pre_obj}
-	$(NM) -g -P -p $< | grep -E '^[a-zA-Z0-9_]* [TDS]'  | sed 's/^\\([^ ]*\\).*/\\1 #{mod_name}/' > $@
-endif
-endif
+#{@name}: $(#{prefix}_DEPENDENCIES) #{objs_str} grub-mkmod
+	./grub-mkmod -o $@ $(filter %.o, $^)
 
-#{undsym}: #{pre_obj}
-	echo '#{mod_name}' > $@
-	$(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+MODFILES += #{@name}
 
 " + objs.collect_with_index do |obj, i|
       src = sources[i]
diff --git a/include/grub/dl.h b/include/grub/dl.h
index e24b832..3f8b328 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -85,7 +85,6 @@ struct grub_dl
 };
 typedef struct grub_dl *grub_dl_t;
 
-grub_err_t EXPORT_FUNC(grub_dl_check_header) (void *ehdr, grub_size_t size);
 grub_dl_t EXPORT_FUNC(grub_dl_load_file) (const char *filename);
 grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
 grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
diff --git a/include/grub/elf.h b/include/grub/elf.h
index 1a1ec13..6d2f28d 100644
--- a/include/grub/elf.h
+++ b/include/grub/elf.h
@@ -454,8 +454,8 @@ typedef struct
    the end of a chain, meaning no further symbols are found in that bucket.  */
 
 #define STN_UNDEF	0		/* End of a chain.  */
-#define STN_ABS		65521
-
+#define STN_ABS		0xfff1
+#define STN_COMMON	0xfff2
 
 /* How to extract and insert information held in the st_other field.  */
 
diff --git a/include/grub/obj.h b/include/grub/obj.h
new file mode 100644
index 0000000..29ff8dc
--- /dev/null
+++ b/include/grub/obj.h
@@ -0,0 +1,90 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/>.
+ */
+
+#ifndef GRUB_OBJ_H
+#define GRUB_OBJ_H 1
+
+#include <grub/types.h>
+
+#define GRUB_OBJ_HEADER_MAGIC	0x4a424f47	/* GOBJ  */
+#define GRUB_OBJ_HEADER_VERSION	1
+
+#define GRUB_OBJ_SEG_END	0
+#define GRUB_OBJ_SEG_TEXT	1
+#define GRUB_OBJ_SEG_DATA	2
+#define GRUB_OBJ_SEG_RDATA	3
+#define GRUB_OBJ_SEG_BSS	4
+
+#define GRUB_OBJ_FUNC_NONE	0xffff
+
+struct grub_obj_segment
+{
+  grub_uint8_t type;
+  grub_uint32_t offset;
+  grub_uint8_t align;
+  grub_uint32_t size;
+} __attribute__((packed));
+
+struct grub_obj_header
+{
+  grub_uint32_t magic;
+  grub_uint16_t version;
+  grub_uint16_t symbol_table;
+  grub_uint16_t reloc_table;
+  grub_uint16_t string_table;
+  grub_uint16_t init_func;
+  grub_uint16_t fini_func;
+  grub_uint32_t mod_deps;
+  struct grub_obj_segment segments[1];
+} __attribute__((packed));
+
+#define GRUB_OBJ_SEGMENT_END	0xff
+
+#define GRUB_OBJ_REL_DIR32	0
+#define GRUB_OBJ_REL_REL32	1
+#define GRUB_OBJ_REL_DIR64	2
+#define GRUB_OBJ_REL_REL64	3
+
+#define GRUB_OBJ_REL_ISREL	1
+#define GRUB_OBJ_REL_64BIT	2
+
+struct grub_obj_symbol
+{
+  grub_uint8_t segment;
+  grub_uint16_t name;
+  grub_uint32_t offset;
+} __attribute__((packed));
+
+struct grub_obj_reloc
+{
+  grub_uint8_t segment;
+  grub_uint8_t type;
+  grub_uint32_t offset;
+  grub_uint8_t symbol_segment;
+} __attribute__((packed));
+
+struct grub_obj_reloc_extern
+{
+  grub_uint8_t segment;
+  grub_uint8_t type;
+  grub_uint32_t offset;
+  grub_uint8_t symbol_segment;
+  grub_uint16_t symbol_name;
+} __attribute__((packed));
+
+#endif /* ! GRUB_OBJ_H */
diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h
index 6a93ab0..841d702 100644
--- a/include/grub/util/misc.h
+++ b/include/grub/util/misc.h
@@ -26,6 +26,7 @@
 
 #include <config.h>
 #include <grub/types.h>
+#include <grub/list.h>
 
 #ifdef __NetBSD__
 /* NetBSD uses /boot for its boot block.  */
@@ -46,6 +47,9 @@ void grub_util_error (const char *fmt, ...) __attribute__ ((noreturn));
 void *xmalloc (size_t size);
 void *xrealloc (void *ptr, size_t size);
 char *xstrdup (const char *str);
+void *xmalloc_zero (size_t size);
+
+void * grub_list_reverse (grub_list_t head);
 
 char *grub_util_get_path (const char *dir, const char *file);
 size_t grub_util_get_fp_size (FILE *fp);
@@ -56,6 +60,8 @@ void grub_util_load_image (const char *path, char *buf);
 void grub_util_write_image (const char *img, size_t size, FILE *out);
 void grub_util_write_image_at (const void *img, size_t size, off_t offset,
 			       FILE *out);
+char * grub_util_get_module_name (const char *str);
+char * grub_util_get_module_path (const char *prefix, const char *str);
 
 #ifndef  HAVE_ASPRINTF
 
diff --git a/include/grub/util/obj.h b/include/grub/util/obj.h
new file mode 100644
index 0000000..2c35bc9
--- /dev/null
+++ b/include/grub/util/obj.h
@@ -0,0 +1,84 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/>.
+ */
+
+#ifndef GRUB_UTIL_OBJ_HEADER
+#define GRUB_UTIL_OBJ_HEADER	1
+
+#include <config.h>
+#include <grub/types.h>
+#include <grub/obj.h>
+#include <grub/list.h>
+
+#include <stdio.h>
+
+struct grub_util_obj_segment
+{
+  struct grub_util_obj_segment *next;
+  struct grub_obj_segment segment;
+  char *data;
+  grub_uint32_t raw_size;
+  grub_uint32_t file_off;
+  int index;
+};
+
+struct grub_util_obj_symbol
+{
+  struct grub_util_obj_symbol *next;
+  char *name;
+  struct grub_util_obj_segment *segment;
+  struct grub_obj_symbol symbol;
+};
+
+struct grub_util_obj_reloc
+{
+  struct grub_util_obj_reloc *next;
+  struct grub_util_obj_segment *segment;
+  struct grub_obj_reloc reloc;
+  struct grub_util_obj_segment *symbol_segment;
+  grub_uint32_t symbol_offset;
+  char *symbol_name;
+};
+
+struct grub_util_obj
+{
+  struct grub_util_obj_segment *segments;
+  struct grub_util_obj_symbol *symbols;
+  struct grub_util_obj_reloc *relocs;
+  grub_uint32_t mod_attr;
+};
+
+#define GRUB_OBJ_MERGE_NONE	0
+#define GRUB_OBJ_MERGE_SAME	1
+#define GRUB_OBJ_MERGE_ALL	2
+
+void grub_obj_reverse (struct grub_util_obj *obj);
+void grub_obj_sort_segments (struct grub_util_obj *obj);
+void grub_obj_merge_segments (struct grub_util_obj *obj, int merge);
+void grub_obj_reloc_symbols (struct grub_util_obj *obj, int merge);
+void grub_obj_save (struct grub_util_obj *obj, char *mod_name, FILE *fp);
+struct grub_util_obj *grub_obj_load (char *image, int size, int load_data);
+void grub_obj_free (struct grub_util_obj *obj);
+
+void grub_obj_dump_segments (struct grub_util_obj *obj);
+void grub_obj_dump_symbols (struct grub_util_obj *obj);
+void grub_obj_dump_relocs (struct grub_util_obj *obj);
+
+int pe_add_file (struct grub_util_obj *obj, char *image, int size);
+int elf_add_file (struct grub_util_obj *obj, char *image, int size);
+
+#endif /* ! GRUB_UTIL_OBJ_HEADER */
diff --git a/kern/dl.c b/kern/dl.c
index 6c863be..fd81dd7 100644
--- a/kern/dl.c
+++ b/kern/dl.c
@@ -18,8 +18,8 @@
  */
 
 #include <config.h>
-#include <grub/elf.h>
 #include <grub/dl.h>
+#include <grub/obj.h>
 #include <grub/misc.h>
 #include <grub/mm.h>
 #include <grub/err.h>
@@ -219,78 +219,46 @@ grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
   return 0;
 }
 
-/* Check if EHDR is a valid ELF header.  */
-grub_err_t
-grub_dl_check_header (void *ehdr, grub_size_t size)
-{
-  Elf_Ehdr *e = ehdr;
-
-  /* Check the header size.  */
-  if (size < sizeof (Elf_Ehdr))
-    return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected");
-
-  /* Check the magic numbers.  */
-  if (grub_arch_dl_check_header (ehdr)
-      || e->e_ident[EI_MAG0] != ELFMAG0
-      || e->e_ident[EI_MAG1] != ELFMAG1
-      || e->e_ident[EI_MAG2] != ELFMAG2
-      || e->e_ident[EI_MAG3] != ELFMAG3
-      || e->e_ident[EI_VERSION] != EV_CURRENT
-      || e->e_version != EV_CURRENT)
-    return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
-
-  return GRUB_ERR_NONE;
-}
-
 /* Load all segments from memory specified by E.  */
 static grub_err_t
-grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
+grub_dl_load_segments (grub_dl_t mod, struct grub_obj_header *e)
 {
   unsigned i;
-  Elf_Shdr *s;
+  struct grub_obj_segment *s;
 
-  for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
+  for (i = 0, s = &e->segments[0]; s->type != GRUB_OBJ_SEGMENT_END; i++, s++)
     {
-      if (s->sh_flags & SHF_ALLOC)
+      grub_dl_segment_t seg;
+      void *addr;
+
+      seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
+      if (! seg)
+	return grub_errno;
+
+      addr = grub_memalign (s->align, s->size);
+      if (! addr)
 	{
-	  grub_dl_segment_t seg;
+	  grub_free (seg);
+	  return grub_errno;
+	}
 
-	  seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
-	  if (! seg)
-	    return grub_errno;
+      grub_memset (addr, 0, s->size);
+      grub_memcpy (addr, (char *) e + s->offset,
+		   (s + 1)->offset - s->offset);
+      seg->addr = addr;
 
-	  if (s->sh_size)
-	    {
-	      void *addr;
-
-	      addr = grub_memalign (s->sh_addralign, s->sh_size);
-	      if (! addr)
-		{
-		  grub_free (seg);
-		  return grub_errno;
-		}
-
-	      switch (s->sh_type)
-		{
-		case SHT_PROGBITS:
-		  grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
-		  break;
-		case SHT_NOBITS:
-		  grub_memset (addr, 0, s->sh_size);
-		  break;
-		}
-
-	      seg->addr = addr;
-	    }
-	  else
-	    seg->addr = 0;
+      seg->size = s->size;
+      seg->section = i;
+      seg->next = mod->segment;
+      mod->segment = seg;
 
-	  seg->size = s->sh_size;
-	  seg->section = i;
-	  seg->next = mod->segment;
-	  mod->segment = seg;
+      if (! i)
+	{
+	  if (e->init_func != GRUB_OBJ_FUNC_NONE)
+	    mod->init = (void (*) (grub_dl_t)) ((char *) addr + e->init_func);
+
+	  if (e->fini_func != GRUB_OBJ_FUNC_NONE)
+	    mod->fini = (void (*) (void)) ((char *) addr + e->fini_func);
 	}
     }
 
@@ -298,92 +266,67 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
 }
 
 static grub_err_t
-grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
+grub_dl_resolve_symbols (grub_dl_t mod, struct grub_obj_header *e)
 {
-  unsigned i;
-  Elf_Shdr *s;
-  Elf_Sym *sym;
-  const char *str;
-  Elf_Word size, entsize;
-
-  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
-    if (s->sh_type == SHT_SYMTAB)
-      break;
+  char *strtab;
+  struct grub_obj_symbol *sym;
+  struct grub_obj_reloc_extern *rel;
 
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table");
+  strtab = (char *) e + e->string_table;
 
-#ifdef GRUB_MODULES_MACHINE_READONLY
-  mod->symtab = grub_malloc (s->sh_size);
-  memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size);
-#else
-  mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset);
-#endif
-  sym = mod->symtab;
-  size = s->sh_size;
-  entsize = s->sh_entsize;
+  for (sym = (struct grub_obj_symbol *) ((char *) e + e->symbol_table);
+       sym->segment != GRUB_OBJ_SEGMENT_END; sym++)
+    {
+      char *addr;
 
-  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * s->sh_link);
-  str = (char *) e + s->sh_offset;
+      addr = grub_dl_get_section_addr (mod, sym->segment);
+      addr += sym->offset;
 
-  for (i = 0;
-       i < size / entsize;
-       i++, sym = (Elf_Sym *) ((char *) sym + entsize))
-    {
-      unsigned char type = ELF_ST_TYPE (sym->st_info);
-      unsigned char bind = ELF_ST_BIND (sym->st_info);
-      const char *name = str + sym->st_name;
+      if (grub_dl_register_symbol (strtab + sym->name, addr, mod))
+	return grub_errno;
+    }
 
-      switch (type)
-	{
-	case STT_NOTYPE:
-	  /* Resolve a global symbol.  */
-	  if (sym->st_name != 0 && sym->st_shndx == 0)
-	    {
-	      sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name);
-	      if (! sym->st_value)
-		return grub_error (GRUB_ERR_BAD_MODULE,
-				   "the symbol `%s' not found", name);
-	    }
-	  else
-	    sym->st_value = 0;
-	  break;
+  for (rel = (struct grub_obj_reloc_extern *) ((char *) e + e->reloc_table);
+       rel->segment != GRUB_OBJ_SEGMENT_END;)
+    {
+      char *addr, *symbol_addr;
+      int type;
 
-	case STT_OBJECT:
-	  sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
-								sym->st_shndx);
-	  if (bind != STB_LOCAL)
-	    if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
-	      return grub_errno;
-	  break;
+      addr = grub_dl_get_section_addr (mod, rel->segment);
+      addr += rel->offset;
+      type = rel->type;
 
-	case STT_FUNC:
-	  sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
-								sym->st_shndx);
-	  if (bind != STB_LOCAL)
-	    if (grub_dl_register_symbol (name, (void *) sym->st_value, mod))
-	      return grub_errno;
-
-	  if (grub_strcmp (name, "grub_mod_init") == 0)
-	    mod->init = (void (*) (grub_dl_t)) sym->st_value;
-	  else if (grub_strcmp (name, "grub_mod_fini") == 0)
-	    mod->fini = (void (*) (void)) sym->st_value;
-	  break;
+      if (rel->symbol_segment == GRUB_OBJ_SEGMENT_END)
+	{
+	  char *name;
+
+	  name = strtab + rel->symbol_name;
+	  symbol_addr = grub_dl_resolve_symbol (name);
+	  if (! symbol_addr)
+	    return grub_error (GRUB_ERR_BAD_MODULE,
+			       "the symbol `%s' not found", name);
+	  rel++;
+	}
+      else
+	{
+	  symbol_addr = grub_dl_get_section_addr (mod, rel->symbol_segment);
+	  rel = (struct grub_obj_reloc_extern *)
+	    ((char *) rel + sizeof (struct grub_obj_reloc));
+	}
 
-	case STT_SECTION:
-	  sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
-							       sym->st_shndx);
+      switch (type)
+	{
+	case GRUB_OBJ_REL_DIR32:
+	  *((grub_uint32_t *) addr) += (grub_uint32_t) symbol_addr;
 	  break;
 
-	case STT_FILE:
-	  sym->st_value = 0;
+	case GRUB_OBJ_REL_REL32:
+	  *((grub_uint32_t *) addr) += (grub_uint32_t) (symbol_addr - addr);
 	  break;
 
 	default:
 	  return grub_error (GRUB_ERR_BAD_MODULE,
-			     "unknown symbol type `%d'", (int) type);
+			     "unknown reloc type %d", type);
 	}
     }
 
@@ -398,74 +341,31 @@ grub_dl_call_init (grub_dl_t mod)
 }
 
 static grub_err_t
-grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
+grub_dl_resolve_dependencies (grub_dl_t mod, char *name)
 {
-  Elf_Shdr *s;
-  const char *str;
-  unsigned i;
-
-  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
-  str = (char *) e + s->sh_offset;
-
-  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
-    if (grub_strcmp (str + s->sh_name, ".modname") == 0)
-      {
-	mod->name = grub_strdup ((char *) e + s->sh_offset);
-	if (! mod->name)
-	  return grub_errno;
-	break;
-      }
-
-  if (i == e->e_shnum)
-    return grub_error (GRUB_ERR_BAD_MODULE, "no module name found");
-
-  return GRUB_ERR_NONE;
-}
-
-static grub_err_t
-grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e)
-{
-  Elf_Shdr *s;
-  const char *str;
-  unsigned i;
-
-  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
-  str = (char *) e + s->sh_offset;
-
-  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
-    if (grub_strcmp (str + s->sh_name, ".moddeps") == 0)
-      {
-	const char *name = (char *) e + s->sh_offset;
-	const char *max = name + s->sh_size;
-
-	while ((name < max) && (*name))
-	  {
-	    grub_dl_t m;
-	    grub_dl_dep_t dep;
-
-	    m = grub_dl_load (name);
-	    if (! m)
-	      return grub_errno;
+  while (1)
+    {
+      grub_dl_t m;
+      grub_dl_dep_t dep;
 
-	    grub_dl_ref (m);
+      name += grub_strlen (name) + 1;
+      if (! *name)
+	return GRUB_ERR_NONE;
 
-	    dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
-	    if (! dep)
-	      return grub_errno;
+      m = grub_dl_load (name);
+      if (! m)
+	return grub_errno;
 
-	    dep->mod = m;
-	    dep->next = mod->dep;
-	    mod->dep = dep;
+      grub_dl_ref (m);
 
-	    name += grub_strlen (name) + 1;
-	  }
-      }
+      dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep));
+      if (! dep)
+	return grub_errno;
 
-  return GRUB_ERR_NONE;
+      dep->mod = m;
+      dep->next = mod->dep;
+      mod->dep = dep;
+    }
 }
 
 #ifndef GRUB_UTIL
@@ -510,25 +410,18 @@ grub_dl_flush_cache (grub_dl_t mod)
 grub_dl_t
 grub_dl_load_core (void *addr, grub_size_t size)
 {
-  Elf_Ehdr *e;
+  struct grub_obj_header *e;
   grub_dl_t mod;
+  char *name;
 
   grub_dprintf ("modules", "module at %p, size 0x%lx\n", addr,
 		(unsigned long) size);
-  e = addr;
-  if (grub_dl_check_header (e, size))
-    return 0;
-
-  if (e->e_type != ET_REL)
-    {
-      grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type");
-      return 0;
-    }
 
-  /* Make sure that every section is within the core.  */
-  if (size < e->e_shoff + e->e_shentsize * e->e_shnum)
+  e = addr;
+  if ((e->magic != GRUB_OBJ_HEADER_MAGIC) ||
+      (e->version != GRUB_OBJ_HEADER_VERSION))
     {
-      grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
+      grub_error (GRUB_ERR_BAD_OS, "invalid object file");
       return 0;
     }
 
@@ -536,7 +429,9 @@ grub_dl_load_core (void *addr, grub_size_t size)
   if (! mod)
     return 0;
 
-  mod->name = 0;
+  name = (char *) addr + e->mod_deps;
+
+  mod->name = grub_strdup (name);
   mod->ref_count = 1;
   mod->dep = 0;
   mod->segment = 0;
@@ -544,11 +439,9 @@ grub_dl_load_core (void *addr, grub_size_t size)
   mod->fini = 0;
 
   grub_dprintf ("modules", "relocating to %p\n", mod);
-  if (grub_dl_resolve_name (mod, e)
-      || grub_dl_resolve_dependencies (mod, e)
+  if (grub_dl_resolve_dependencies (mod, name)
       || grub_dl_load_segments (mod, e)
-      || grub_dl_resolve_symbols (mod, e)
-      || grub_arch_dl_relocate_symbols (mod, e))
+      || grub_dl_resolve_symbols (mod, e))
     {
       mod->fini = 0;
       grub_dl_unload (mod);
diff --git a/util/grub-mkmod.c b/util/grub-mkmod.c
new file mode 100644
index 0000000..1ed1b55
--- /dev/null
+++ b/util/grub-mkmod.c
@@ -0,0 +1,176 @@
+/* grub-mkmod.c - tool to generate mod file from object files.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+int
+grub_strcmp (const char *s1, const char *s2)
+{
+  return strcmp (s1, s2);
+}
+
+static struct option options[] = {
+  {"help", no_argument, 0, 'h'},
+  {"version", no_argument, 0, 'V'},
+  {"verbose", no_argument, 0, 'v'},
+  {"output", required_argument, 0, 'o'},
+  {"name", required_argument, 0, 'n'},
+  {0, 0, 0, 0}
+};
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr, "Try ``grub-mkmod --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-mkmod [OPTIONS] [OBJECT_FILES].\n\
+\n\
+Tool to generate mod file from object files.\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\
+  -o, --output=FILE       output a generated image to FILE [default=stdout]\n\
+  -n, --name=NAME         set module name\n\
+\n\
+Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+static void
+add_file (struct grub_util_obj *obj, char *image, int size)
+{
+  if ((! elf_add_file (obj, image, size)) &&
+      (! pe_add_file (obj, image, size)))
+    grub_util_error ("Invalid object format");
+}
+
+static void
+mkmod (char *objs[], char *name, FILE *fp)
+{
+  struct grub_util_obj *obj;
+  int merge = GRUB_OBJ_MERGE_ALL;
+
+  obj = xmalloc_zero (sizeof (*obj));
+  while (*objs)
+    {
+      char *image;
+      int size;
+
+      image = grub_util_read_image (*objs);
+      size = grub_util_get_image_size (*objs);
+      add_file (obj, image, size);
+      free (image);
+      objs++;
+    }
+
+  grub_obj_reverse (obj);
+  grub_obj_sort_segments (obj);
+  grub_obj_merge_segments (obj, merge);
+  grub_obj_reloc_symbols (obj, merge);
+  grub_obj_save (obj, name, fp);
+
+  grub_obj_free (obj);
+}
+
+int
+main (int argc, char *argv[])
+{
+  FILE *fp = stdout;
+  char *output = NULL;
+  char *name = NULL;
+
+  progname = "grub-pe2mod";
+
+  /* Check for options.  */
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "hVvo:n:", 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;
+
+	  case 'o':
+	    if (output)
+	      free (output);
+
+	    output = xstrdup (optarg);
+	    break;
+
+	  case 'n':
+	    if (name)
+	      free (name);
+
+	    name = xstrdup (optarg);
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  if (! name)
+    {
+      if (! output)
+	grub_util_error ("no module name");
+      name = grub_util_get_module_name (output);
+    }
+
+  if (output)
+    {
+      fp = fopen (output, "wb");
+      if (! fp)
+	grub_util_error ("cannot open %s", output);
+      free (output);
+    }
+
+  mkmod (argv + optind, name, fp);
+
+  fclose (fp);
+  free (name);
+
+  return 0;
+}
diff --git a/util/grub-objdump.c b/util/grub-objdump.c
new file mode 100644
index 0000000..05a5b3e
--- /dev/null
+++ b/util/grub-objdump.c
@@ -0,0 +1,176 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+int
+grub_strcmp (const char *s1, const char *s2)
+{
+  return strcmp (s1, s2);
+}
+
+static struct option options[] = {
+  {"help", no_argument, 0, 'h'},
+  {"version", no_argument, 0, 'V'},
+  {"verbose", no_argument, 0, 'v'},
+  {"all-headers", no_argument, 0, 'x'},
+  {"segment", no_argument, 0, 's'},
+  {"syms", no_argument, 0, 't'},
+  {"reloc", no_argument, 0, 'r'},
+  {0, 0, 0, 0}
+};
+
+static void
+usage (int status)
+{
+  if (status)
+    fprintf (stderr, "Try ``grub-mkmod --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-objdump [OPTIONS] [MODULE_FILES].\n\
+\n\
+Tool to generate mod file from object files.\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\
+  -x, --all               display all information\n\
+  -s, --segment           display segment information\n\
+  -t, --syms              display symbol table\n\
+  -r, --reloc             display reloc table\n\
+\n\
+Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+#define FLAG_DUMP_SEGMENT	1
+#define FLAG_DUMP_SYMBOL	2
+#define FLAG_DUMP_RELOC		4
+#define FLAG_DUMP_ALL		7
+
+static void
+objdump (char *objs[], int flag)
+{
+  while (*objs)
+    {
+      struct grub_util_obj *obj;
+      char *image;
+      struct grub_obj_header *e;
+      int size;
+
+      image = grub_util_read_image (*objs);
+      size = grub_util_get_image_size (*objs);
+      obj = grub_obj_load (image, size, 1);
+
+      e = (struct grub_obj_header *) image;
+      printf ("filename: %s\n", *objs);
+      printf ("mod name: %s\n", image + e->mod_deps);
+      printf ("mod attr: 0x%x\n", obj->mod_attr);
+      printf ("mod deps: 0x%x\n", e->mod_deps);
+      printf ("init func: 0x%x\n", e->init_func);
+      printf ("fini func: 0x%x\n", e->fini_func);
+
+      if (flag & FLAG_DUMP_SEGMENT)
+	{
+	  printf ("\n");
+	  grub_obj_dump_segments (obj);
+	}
+
+      if (flag & FLAG_DUMP_SYMBOL)
+	{
+	  printf ("\n");
+	  grub_obj_dump_symbols (obj);
+	}
+
+      if (flag & FLAG_DUMP_RELOC)
+	{
+	  printf ("\n");
+	  grub_obj_dump_relocs (obj);
+	}
+
+      grub_obj_free (obj);
+      free (image);
+      objs++;
+    }
+}
+
+int
+main (int argc, char *argv[])
+{
+  int flag = 0;
+
+  progname = "grub-objdump";
+
+  /* Check for options.  */
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "hVvxstr", 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;
+
+	  case 'x':
+	    flag |= FLAG_DUMP_ALL;
+	    break;
+
+	  case 's':
+	    flag |= FLAG_DUMP_SEGMENT;
+	    break;
+
+	  case 't':
+	    flag |= FLAG_DUMP_SYMBOL;
+	    break;
+
+	  case 'r':
+	    flag |= FLAG_DUMP_RELOC;
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  objdump (argv + optind, flag);
+
+  return 0;
+}
diff --git a/util/grub-symdb.c b/util/grub-symdb.c
new file mode 100644
index 0000000..94a0904
--- /dev/null
+++ b/util/grub-symdb.c
@@ -0,0 +1,811 @@
+/* grub-symdb.c - manage symbol database  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#define _GNU_SOURCE	1
+#include <getopt.h>
+
+int
+grub_strcmp (const char *s1, const char *s2)
+{
+  return strcmp (s1, s2);
+}
+
+struct grub_symbol_list
+{
+  struct grub_symbol_list *next;
+  char *name;
+  struct grub_named_list *defs;
+  struct grub_named_list *unds;
+};
+
+static struct grub_symbol_list *symbol_list;
+
+struct grub_update_list
+{
+  struct grub_update_list *next;
+  char *name;
+  struct grub_named_list *add_mods;
+  struct grub_named_list *del_mods;
+  struct grub_named_list *cur_mods;
+};
+
+static struct grub_update_list *update_list;
+
+struct grub_mod_syms
+{
+  struct grub_named_list *defs;
+  struct grub_named_list *unds;
+};
+
+
+void *
+grub_sort_list_find (grub_named_list_t head, const char *name)
+{
+  grub_named_list_t result = 0;
+
+  auto int list_find (grub_named_list_t item);
+  int list_find (grub_named_list_t item)
+    {
+      int r;
+
+      r = strcmp (name, item->name);
+      if (! r)
+	{
+	  result = item;
+	  return 1;
+	}
+
+      return (r < 0);
+    }
+
+  grub_list_iterate (GRUB_AS_LIST (head), (grub_list_hook_t) list_find);
+  return result;
+}
+
+static void
+grub_sort_list_insert (grub_named_list_t *head, grub_named_list_t item)
+{
+  auto int test (grub_named_list_t new_item, grub_named_list_t item);
+  int test (grub_named_list_t new_item, grub_named_list_t item)
+    {
+      return (strcmp (new_item->name, item->name) < 0);
+    }
+
+  grub_list_insert (GRUB_AS_LIST_P (head), GRUB_AS_LIST (item),
+		    (grub_list_test_t) test);
+}
+
+static void
+free_named_list (grub_named_list_t *head)
+{
+  grub_named_list_t cur = *head;
+
+  while (cur)
+    {
+      grub_named_list_t tmp;
+
+      tmp = cur;
+      cur = cur->next;
+      free ((char *) tmp->name);
+      free (tmp);
+    }
+
+  *head = 0;
+}
+
+static int
+remove_string (grub_named_list_t *head, char *name)
+{
+  grub_named_list_t p;
+
+  p = grub_sort_list_find (*head, name);
+  if (p)
+    {
+      grub_list_remove (GRUB_AS_LIST_P (head), GRUB_AS_LIST (p));
+      free ((char *) p->name);
+      free (p);
+
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+insert_string (grub_named_list_t *head, char *name)
+{
+  struct grub_named_list *n;
+
+  n = grub_sort_list_find (*head, name);
+  if (n)
+    return 0;
+
+  n = xmalloc (sizeof (struct grub_named_list));
+  n->name = xstrdup (name);
+  grub_sort_list_insert (head, n);
+
+  return 1;
+}
+
+static struct grub_symbol_list *
+insert_symbol (char *name)
+{
+  struct grub_symbol_list *n;
+
+  n = grub_sort_list_find (GRUB_AS_NAMED_LIST (symbol_list), name);
+  if (! n)
+    {
+      n = xmalloc (sizeof (struct grub_symbol_list));
+      n->name = xstrdup (name);
+      n->defs = 0;
+      n->unds = 0;
+
+      grub_sort_list_insert (GRUB_AS_NAMED_LIST_P (&symbol_list),
+			     GRUB_AS_NAMED_LIST (n));
+    }
+
+  return n;
+}
+
+static struct grub_update_list *
+insert_update (char *name)
+{
+  struct grub_update_list *n;
+
+  n = grub_sort_list_find (GRUB_AS_NAMED_LIST (update_list), name);
+  if (! n)
+    {
+      n = xmalloc (sizeof (struct grub_update_list));
+      n->name = xstrdup (name);
+      n->add_mods = 0;
+      n->del_mods = 0;
+      n->cur_mods = 0;
+
+      grub_sort_list_insert (GRUB_AS_NAMED_LIST_P (&update_list),
+			     GRUB_AS_NAMED_LIST (n));
+    }
+
+  return n;
+}
+
+static void
+add_update (char *m1, char *m2, int is_add)
+{
+  struct grub_update_list *u;
+
+  u = insert_update (m2);
+  if (is_add)
+    {
+      remove_string (&u->del_mods, m1);
+      insert_string (&u->add_mods, m1);
+    }
+  else
+    insert_string (&u->del_mods, m1);
+}
+
+static void
+read_symdb (char *path)
+{
+  FILE *fp;
+  char line[512];
+  struct grub_symbol_list *sym = 0;
+
+  fp = fopen (path, "r");
+  if (! fp)
+    return;
+
+  while (fgets (line, sizeof (line), fp))
+    {
+      char *p;
+
+      p = line + strlen (line) - 1;
+      while ((p >= line) && ((*p == '\r') || (*p == '\n') || (*p == ' ')))
+	p--;
+
+      if (p < line)
+	continue;
+
+      *(p + 1) = 0;
+
+      p = line;
+      while (*p == ' ')
+	p++;
+
+      if (*p == '#')
+	continue;
+
+      if ((*p == '+') || (*p == '-'))
+	{
+	  if (! sym)
+	    grub_util_error ("No current symbol.");
+
+	  insert_string ((*p == '+') ? &sym->defs : &sym->unds, p + 1);
+	}
+      else
+	sym = insert_symbol (p);
+    }
+
+  fclose (fp);
+}
+
+static void
+write_symdb (char *path)
+{
+  FILE *fp;
+  struct grub_symbol_list *sym;
+
+  fp = fopen (path, "w");
+  if (! fp)
+    grub_util_error ("Can\'t write to ", path);
+
+  sym = symbol_list;
+  while (sym)
+    {
+      struct grub_named_list *mod;
+
+      fprintf (fp, "%s\n", sym->name);
+      mod = sym->defs;
+      while (mod)
+	{
+	  fprintf (fp, "+%s\n", mod->name);
+	  mod = mod->next;
+	}
+      mod = sym->unds;
+      while (mod)
+	{
+	  fprintf (fp, "-%s\n", mod->name);
+	  mod = mod->next;
+	}
+
+      sym = sym->next;
+    }
+
+  fclose (fp);
+}
+
+static void
+check_symdb (void)
+{
+  struct grub_symbol_list *sym;
+
+  sym = symbol_list;
+  while (sym)
+    {
+      if (! sym->defs)
+	printf ("undefined: %s\n", sym->name);
+      else if (sym->defs->next)
+	printf ("duplicate: %s\n", sym->name);
+
+      sym = sym->next;
+    }
+}
+
+static void
+read_mod_syms (struct grub_mod_syms *mod_syms, char *path)
+{
+  struct grub_util_obj *obj;
+  char *image;
+  size_t size;
+  struct grub_util_obj_symbol *sym;
+  struct grub_util_obj_reloc *rel;
+
+  mod_syms->defs = 0;
+  mod_syms->unds = 0;
+
+  image = grub_util_read_image (path);
+  size = grub_util_get_image_size (path);
+  obj = grub_obj_load (image, size, 0);
+
+  sym = obj->symbols;
+  while (sym)
+    {
+      insert_string (&mod_syms->defs, sym->name);
+      sym = sym->next;
+    }
+
+  rel = obj->relocs;
+  while (rel)
+    {
+      if (rel->symbol_name)
+	insert_string (&mod_syms->unds, rel->symbol_name);
+      rel = rel->next;
+    }
+
+  grub_obj_free (obj);
+  free (image);
+}
+
+static void
+update_mods (char *mods[], const char *dir)
+{
+  for (; mods[0]; mods++)
+    {
+      char *mod_name, *mod_path;
+      struct grub_mod_syms mod_syms;
+      struct grub_named_list *m;
+
+      mod_name = grub_util_get_module_name (mods[0]);
+      mod_path = grub_util_get_module_path (dir, mod_name);
+
+      if (! strcmp (mod_name, "grub-symdb"))
+	{
+	  free (mod_name);
+	  free (mod_path);
+	  continue;
+	}
+
+      read_mod_syms (&mod_syms, mod_path);
+
+      m = mod_syms.defs;
+      while (m)
+	{
+	  struct grub_symbol_list *sym;
+	  struct grub_named_list *n;
+
+	  sym = insert_symbol ((char *) m->name);
+	  insert_string (&sym->defs, mod_name);
+
+	  n = sym->unds;
+	  while (n)
+	    {
+	      add_update ((char *) mod_name, (char *) n->name, 1);
+	      n = n->next;
+	    }
+
+	  m = m->next;
+	}
+
+      m = mod_syms.unds;
+      while (m)
+	{
+	  struct grub_symbol_list *sym;
+	  struct grub_named_list *n;
+
+	  sym = insert_symbol ((char *) m->name);
+	  insert_string (&sym->unds, mod_name);
+
+	  n = sym->defs;
+	  while (n)
+	    {
+	      add_update ((char *) n->name, (char *) mod_name, 1);
+	      n = n->next;
+	    }
+
+	  m = m->next;
+	}
+
+      free (mod_name);
+      free (mod_path);
+    }
+}
+
+static void
+remove_mods (char *mods[])
+{
+  for (; mods[0]; mods++)
+    {
+      char *mod_name;
+      struct grub_symbol_list *sym;
+
+      mod_name = grub_util_get_module_name (mods[0]);
+
+      sym = symbol_list;
+      while (sym)
+	{
+	  struct grub_named_list *m, *n;
+
+	  m = sym->defs;
+	  while (m)
+	    {
+	      int r;
+
+	      r = strcmp (mod_name, m->name);
+	      if (! r)
+		break;
+
+	      if (r < 0)
+		{
+		  m = 0;
+		  break;
+		}
+
+	      m = m->next;
+	    }
+
+	  n = sym->unds;
+	  while (n)
+	    {
+	      if (m)
+		{
+		  add_update ((char *) m->name, (char *) n->name, 0);
+		}
+	      else
+		{
+		  int r;
+
+		  r = strcmp (mod_name, n->name);
+		  if (! r)
+		    {
+		      m = sym->defs;
+		      while (m)
+			{
+			  add_update ((char *) m->name, (char *) n->name, 0);
+			  m = m->next;
+			}
+
+		      break;
+		    }
+
+		  if (r < 0)
+		    break;
+		}
+
+	      n = n->next;
+	    }
+
+	  sym = sym->next;
+	}
+
+      free (mod_name);
+    }
+}
+
+static void
+dump_update (void)
+{
+  struct grub_update_list *u;
+
+  u = update_list;
+  while (u)
+    {
+      struct grub_named_list *n;
+
+      printf ("%s:" , u->name);
+      n = u->add_mods;
+      while (n)
+	{
+	  printf (" +%s", n->name);
+	  n = n->next;
+	}
+
+      n = u->del_mods;
+      while (n)
+	{
+	  printf (" -%s", n->name);
+	  n = n->next;
+	}
+
+      printf ("\n");
+      u = u->next;
+    }
+}
+
+static void
+update_deps (struct grub_update_list *u, char *path)
+{
+  struct grub_named_list *n;
+  int modified;
+
+  modified = 0;
+  n = u->del_mods;
+  while (n)
+    {
+      modified |= remove_string (&u->cur_mods, (char *) n->name);
+      n = n->next;
+    }
+  n = u->add_mods;
+  while (n)
+    {
+      modified |= insert_string (&u->cur_mods, (char *) n->name);
+      n = n->next;
+    }
+
+  if (modified)
+    {
+      char *image, *p;
+      struct grub_obj_header *hdr;
+      int size;
+      FILE *fp;
+
+      image = grub_util_read_image (path);
+      hdr = (struct grub_obj_header *) image;
+
+      size = hdr->mod_deps;
+      size += strlen (image + size) + 1;
+
+      fp = fopen (path, "wb");
+      if (! fp)
+	grub_util_error ("Can\'t write to %s", path);
+
+      grub_util_write_image (image, size, fp);
+
+      p = image;
+      n = u->cur_mods;
+      while (n)
+	{
+	  strcpy (p, n->name);
+	  p += strlen (p) + 1;
+	  n = n->next;
+	}
+      *(p++) = 0;
+
+      grub_util_write_image (image, p - image, fp);
+
+      fclose (fp);
+      free (image);
+    }
+}
+
+static void
+write_moddep (struct grub_update_list *u, FILE *fp)
+{
+  struct grub_named_list *n;
+
+  if (! u->cur_mods)
+    return;
+
+  fprintf (fp, "%s:", u->name);
+  n = u->cur_mods;
+  while (n)
+    {
+      fprintf (fp, " %s", n->name);
+      n = n->next;
+    }
+
+  fprintf (fp, "\n");
+  free_named_list (&u->cur_mods);
+}
+
+static void
+update_moddep (char *dir)
+{
+  FILE *fp;
+  struct stat st;
+  char *path, *image;
+  struct grub_update_list *u;
+
+  path = grub_util_get_path (dir, "moddep.lst");
+  image = (stat (path, &st) == 0) ? grub_util_read_image (path) : 0;
+
+  fp = fopen (path, "w");
+  if (! fp)
+    grub_util_error ("Can\'t write to ", path);
+
+  if (image)
+    {
+      char *line;
+
+      line = image;
+      while (*line)
+	{
+	  char *p, *c;
+	  int n;
+
+	  n = strcspn (line, "\r\n");
+	  p = line;
+
+	  line += n;
+	  while ((*line == '\r') || (*line == '\n'))
+	    line++;
+
+	  *(p + n) = 0;
+
+	  c = strchr (p, ':');
+	  if (! c)
+	    continue;
+
+	  *c = 0;
+	  u = update_list;
+	  while (u)
+	    {
+	      int r;
+
+	      r = strcmp (p, u->name);
+	      if (! r)
+		break;
+
+	      if (r < 0)
+		{
+		  u = 0;
+		  break;
+		}
+
+	      u = u->next;
+	    }
+	  *c = ':';
+
+	  if (u)
+	    write_moddep (u, fp);
+	  else
+	    fprintf (fp, "%s\n", p);
+	}
+    }
+
+  u = update_list;
+  while (u)
+    {
+      write_moddep (u, fp);
+      u = u->next;
+    }
+
+  fclose (fp);
+  free (path);
+  free (image);
+}
+
+static void
+apply_update (char *dir)
+{
+  struct grub_update_list *u;
+
+  u = update_list;
+  while (u)
+    {
+      char *mod_path;
+
+      mod_path = grub_util_get_module_path (dir, u->name);
+      update_deps (u, mod_path);
+      free (mod_path);
+      u = u->next;
+    }
+
+  update_moddep (dir);
+}
+
+static struct option options[] =
+  {
+    {"directory", required_argument, 0, 'd'},
+    {"test", no_argument, 0, 't'},
+    {"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-symdb --help'' for more information.\n");
+  else
+    printf ("\
+Usage: grub-symdb [OPTION]... COMMAND\n\
+\n\
+Manage the symbol database of GRUB.\n\
+\nCommands:\n\
+  update MODULES          add/update modules to the symbol database\n\
+  remove MODULES          remove modules from the symbol databsae\n\
+  check                   check for duplicate and unresolved symbols\n\
+\n\
+  -d, --directory=DIR     use images and modules under DIR [default=%s]\n\
+  -t, --test              test mode\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\
+", GRUB_LIBDIR, PACKAGE_BUGREPORT);
+
+  exit (status);
+}
+
+int
+main (int argc, char *argv[])
+{
+  char *dir = NULL;
+  char *path;
+  int test_mode = 0;
+
+  progname = "grub-symdb";
+
+  while (1)
+    {
+      int c = getopt_long (argc, argv, "d:thVv", options, 0);
+
+      if (c == -1)
+	break;
+      else
+	switch (c)
+	  {
+	  case 'd':
+	    if (dir)
+	      free (dir);
+
+	    dir = xstrdup (optarg);
+	    break;
+
+	  case 't':
+	    test_mode++;
+	    break;
+
+	  case 'h':
+	    usage (0);
+	    break;
+
+	  case 'V':
+	    printf ("grub-mkimage (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+	    return 0;
+
+	  case 'v':
+	    verbosity++;
+	    break;
+
+	  default:
+	    usage (1);
+	    break;
+	  }
+    }
+
+  if (! dir)
+    dir = xstrdup (GRUB_LIBDIR);
+
+  path = grub_util_get_path (dir, "modsym.lst");
+
+  argv += optind;
+  argc -= optind;
+  if (argc == 0)
+    grub_util_error ("No command specified.");
+
+  read_symdb (path);
+  if (! strcmp (argv[0], "update"))
+    {
+      remove_mods (argv + 1);
+      update_mods (argv + 1, dir);
+      if (test_mode)
+	dump_update ();
+      else
+	{
+	  apply_update (dir);
+	  write_symdb (path);
+	}
+    }
+  else if (! strcmp (argv[0], "remove"))
+    {
+      remove_mods (argv + 1);
+      if (test_mode)
+	dump_update ();
+      else
+	{
+	  apply_update (dir);
+	  write_symdb (path);
+	}
+    }
+  else if (! strcmp (argv[0], "check"))
+    {
+      check_symdb ();
+    }
+  else
+    grub_util_error ("Unkown command %s.", argv[0]);
+
+  free (path);
+  free (dir);
+
+  return 0;
+}
diff --git a/util/misc.c b/util/misc.c
index f615a42..955a184 100644
--- a/util/misc.c
+++ b/util/misc.c
@@ -143,6 +143,36 @@ xstrdup (const char *str)
   return dup;
 }
 
+void *
+xmalloc_zero (size_t size)
+{
+  void *p;
+
+  p = xmalloc (size);
+  memset (p, 0, size);
+
+  return p;
+}
+
+void *
+grub_list_reverse (grub_list_t head)
+{
+  grub_list_t prev;
+
+  prev = 0;
+  while (head)
+    {
+      grub_list_t temp;
+
+      temp = head->next;
+      head->next = prev;
+      prev = head;
+      head = temp;
+    }
+
+  return prev;
+}
+
 char *
 grub_util_get_path (const char *dir, const char *file)
 {
@@ -251,6 +281,58 @@ grub_util_write_image (const char *img, size_t size, FILE *out)
     grub_util_error ("write failed");
 }
 
+char *
+grub_util_get_module_name (const char *str)
+{
+  char *base;
+  char *ext;
+
+  base = strrchr (str, '/');
+  if (! base)
+    base = (char *) str;
+  else
+    base++;
+
+  ext = strrchr (base, '.');
+  if (ext && strcmp (ext, ".mod") == 0)
+    {
+      char *name;
+
+      name = xmalloc (ext - base + 1);
+      memcpy (name, base, ext - base);
+      name[ext - base] = '\0';
+      return name;
+    }
+
+  return xstrdup (base);
+}
+
+char *
+grub_util_get_module_path (const char *prefix, const char *str)
+{
+  char *dir;
+  char *base;
+  char *ext;
+  char *ret;
+
+  ext = strrchr (str, '.');
+  if (ext && strcmp (ext, ".mod") == 0)
+    base = xstrdup (str);
+  else
+    {
+      base = xmalloc (strlen (str) + 4 + 1);
+      sprintf (base, "%s.mod", str);
+    }
+
+  dir = strchr (str, '/');
+  if (dir)
+    return base;
+
+  ret = grub_util_get_path (prefix, base);
+  free (base);
+  return ret;
+}
+
 void *
 grub_malloc (grub_size_t size)
 {
diff --git a/util/obj.c b/util/obj.c
new file mode 100644
index 0000000..2a76dd6
--- /dev/null
+++ b/util/obj.c
@@ -0,0 +1,634 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void
+grub_obj_reverse (struct grub_util_obj *obj)
+{
+  obj->segments = grub_list_reverse (GRUB_AS_LIST (obj->segments));
+  obj->symbols = grub_list_reverse (GRUB_AS_LIST (obj->symbols));
+  obj->relocs = grub_list_reverse (GRUB_AS_LIST (obj->relocs));
+}
+
+void
+grub_obj_sort_segments (struct grub_util_obj *obj)
+{
+  grub_list_t n;
+  int i;
+
+  n = 0;
+  for (i = GRUB_OBJ_SEG_TEXT; i <= GRUB_OBJ_SEG_BSS; i++)
+    {
+      struct grub_util_obj_segment **p, *q;
+
+      for (p = &obj->segments, q = *p; q; q = *p)
+	if (q->segment.type == i)
+	  {
+	    *p = q->next;
+	    grub_list_push (&n, GRUB_AS_LIST (q));
+	  }
+	else
+	  p = &(q->next);
+    }
+
+  obj->segments = grub_list_reverse (n);
+}
+
+static int
+check_merge (struct grub_util_obj_segment *s1,
+	     struct grub_util_obj_segment *s2,
+	     int merge)
+{
+  if (! s2)
+    return 0;
+
+  switch (merge)
+    {
+    case GRUB_OBJ_MERGE_NONE:
+      return (s1 == s2);
+
+    case GRUB_OBJ_MERGE_SAME:
+      return (s1->segment.type == s2->segment.type);
+
+    case GRUB_OBJ_MERGE_ALL:
+      return 1;
+    }
+
+  return 0;
+}
+
+void
+grub_obj_merge_segments (struct grub_util_obj *obj, int merge)
+{
+  struct grub_util_obj_segment *p, *first;
+  grub_uint32_t offset;
+
+  if (merge == GRUB_OBJ_MERGE_NONE)
+    return;
+
+  first = 0;
+  offset = 0;
+  p = obj->segments;
+  while (p)
+    {
+      if (check_merge (p, first, merge))
+	{
+	  grub_uint32_t mask;
+
+	  if (p->segment.align > first->segment.align)
+	    first->segment.align = p->segment.align;
+
+	  mask = p->segment.align - 1;
+	  offset = (offset + mask) & ~mask;
+	  p->segment.offset = offset;
+	  offset += p->segment.size;
+	}
+      else
+	{
+	  first = p;
+	  offset = p->segment.size;
+	}
+
+      p = p->next;
+    }
+}
+
+void
+grub_obj_reloc_symbols (struct grub_util_obj *obj, int merge)
+{
+  struct grub_util_obj_reloc *rel;
+
+  for (rel = obj->relocs; rel; rel = rel->next)
+    {
+      char *addr;
+
+      if (! rel->segment)
+	continue;
+
+      if (! rel->segment->data)
+	grub_util_error ("can\'t relocate in .bss segment");
+
+      addr = rel->segment->data + rel->reloc.offset;
+
+      if (! rel->symbol_segment)
+	{
+	  struct grub_util_obj_symbol *sym;
+
+	  sym = grub_named_list_find (GRUB_AS_NAMED_LIST (obj->symbols),
+				      rel->symbol_name);
+	  if (sym)
+	    {
+	      if (rel->reloc.type & GRUB_OBJ_REL_64BIT)
+		*((grub_uint64_t *) addr) += sym->symbol.offset;
+	      else
+		*((grub_uint32_t *) addr) += sym->symbol.offset;
+
+	      rel->symbol_segment = sym->segment;
+	    }
+	}
+
+      if (rel->symbol_segment)
+	{
+	  grub_uint64_t delta;
+
+	  delta = rel->symbol_segment->segment.offset;
+	  if ((check_merge (rel->segment, rel->symbol_segment, merge)) &&
+	      (rel->reloc.type & GRUB_OBJ_REL_ISREL))
+	    {
+	      delta -= rel->segment->segment.offset + rel->reloc.offset;
+	      rel->segment = 0;
+	    }
+
+	  if (rel->reloc.type & GRUB_OBJ_REL_64BIT)
+	    *((grub_uint64_t *) addr) += delta;
+	  else
+	    *((grub_uint32_t *) addr) += delta;
+	}
+    }
+}
+
+struct grub_strtab
+{
+  struct grub_strtab *next;
+  char *name;
+  int len;
+};
+typedef struct grub_strtab *grub_strtab_t;
+
+static int
+grub_strtab_find (grub_strtab_t head, char *name)
+{
+  int index = 1;
+  int len = strlen (name);
+
+  auto int scan_str (grub_strtab_t item);
+  int scan_str (grub_strtab_t item)
+    {
+      if (item->len >= len)
+	{
+	  int ofs;
+
+	  ofs = item->len - len;
+	  if (! strcmp (item->name + ofs, name))
+	    {
+	      index += ofs;
+	      return 1;
+	    }
+	}
+
+      index += item->len + 1;
+      return 0;
+    }
+
+  if (! grub_list_iterate (GRUB_AS_LIST (head), (grub_list_hook_t) scan_str))
+    index = -index;
+
+  return index;
+}
+
+static void
+grub_strtab_insert (grub_strtab_t *head, char *name)
+{
+  auto int test (grub_strtab_t new_item, grub_strtab_t item);
+  int test (grub_strtab_t new_item, grub_strtab_t item)
+    {
+      return (strcmp (new_item->name, item->name) < 0);
+    }
+
+  grub_strtab_t nitem;
+
+  if (grub_strtab_find (*head, name) > 0)
+    return;
+
+  nitem = xmalloc (sizeof (*nitem));
+  nitem->name = name;
+  nitem->len = strlen (name);
+
+  grub_list_insert (GRUB_AS_LIST_P (head), GRUB_AS_LIST (nitem),
+		    (grub_list_test_t) test);
+}
+
+#define GRUB_OBJ_HEADER_MAX	0xffff
+
+void
+grub_obj_save (struct grub_util_obj *obj, char *mod_name, FILE *fp)
+{
+  char *buf, *p;
+  struct grub_obj_header *hdr;
+  struct grub_util_obj_segment *seg;
+  struct grub_util_obj_symbol *sym;
+  struct grub_util_obj_reloc *rel;
+  int idx;
+  grub_uint32_t offset, raw_size;
+  grub_strtab_t strtab;
+  int strtab_size;
+  int mod_name_len = strlen (mod_name);
+  char mod_init[mod_name_len + 11];
+  char mod_fini[mod_name_len + 11];
+
+  if ((! obj->segments) || (obj->segments->segment.offset))
+    grub_util_error ("invalid segment");
+
+  buf = xmalloc (GRUB_OBJ_HEADER_MAX);
+  hdr = (struct grub_obj_header *) buf;
+
+  hdr->magic = GRUB_OBJ_HEADER_MAGIC;
+  hdr->version = GRUB_OBJ_HEADER_VERSION;
+  hdr->init_func = GRUB_OBJ_FUNC_NONE;
+  hdr->fini_func = GRUB_OBJ_FUNC_NONE;
+
+  idx = 0;
+  offset = 0;
+  raw_size = 0;
+  hdr->segments[0].offset = 0;
+  seg = obj->segments;
+  while (seg)
+    {
+      struct grub_util_obj_segment *cur;
+      grub_uint32_t mask;
+      int is_last;
+
+      cur = seg;
+      seg = seg->next;
+
+      if (! cur->segment.offset)
+	{
+	  if (idx >= GRUB_OBJ_SEGMENT_END)
+	    grub_util_error ("too many segments");
+
+	  hdr->segments[idx].type = cur->segment.type;
+	  hdr->segments[idx].align = cur->segment.align;
+	  hdr->segments[idx].size = 0;
+	  raw_size = 0;
+	}
+
+      cur->index = idx;
+      mask = cur->segment.align - 1;
+      hdr->segments[idx].size = (hdr->segments[idx].size + mask) & ~mask;
+      hdr->segments[idx].size += cur->segment.size;
+
+      is_last = ((! seg) || (! seg->segment.offset));
+
+      if (cur->segment.type != GRUB_OBJ_SEG_BSS)
+	{
+	  raw_size = (raw_size + mask) & ~mask;
+	  raw_size += (is_last) ? cur->raw_size : cur->segment.size;
+	}
+
+      if (is_last)
+	{
+	  offset += raw_size;
+	  idx++;
+	  hdr->segments[idx].offset = offset;
+	}
+    }
+
+  hdr->segments[idx].type = GRUB_OBJ_SEGMENT_END;
+  p = ((char *) &hdr->segments[idx]) + 5;
+
+  sprintf (mod_init, "grub_%s_init", mod_name);
+  sprintf (mod_fini, "grub_%s_fini", mod_name);
+
+  strtab = 0;
+  sym = obj->symbols;
+  while (sym)
+    {
+      if (sym->segment)
+	{
+	  grub_uint32_t ofs;
+
+	  ofs = sym->symbol.offset + sym->segment->segment.offset;
+	  if (! strcmp (sym->name, "grub_mod_init"))
+	    {
+	      if ((ofs >= GRUB_OBJ_HEADER_MAX) || (sym->segment->index))
+		grub_util_error ("init function too far");
+
+	      hdr->init_func = ofs;
+	      sym->segment = 0;
+	    }
+	  else if (! strcmp (sym->name, "grub_mod_fini"))
+	    {
+	      if ((ofs >= GRUB_OBJ_HEADER_MAX) || (sym->segment->index))
+		grub_util_error ("fini function too far");
+
+	      hdr->fini_func = ofs;
+	      sym->segment = 0;
+	    }
+	  else if ((! strcmp (sym->name, mod_init)) ||
+		   (! strcmp (sym->name, mod_fini)))
+	    {
+	      sym->segment = 0;
+	    }
+	  else
+	    grub_strtab_insert (&strtab, sym->name);
+	}
+      sym = sym->next;
+    }
+
+  rel = obj->relocs;
+  while (rel)
+    {
+      if ((rel->segment) && (! rel->symbol_segment))
+	grub_strtab_insert (&strtab, rel->symbol_name);
+      rel = rel->next;
+    }
+
+  strtab_size = - grub_strtab_find (strtab, "?");
+  if (strtab_size >= GRUB_OBJ_HEADER_MAX)
+    grub_util_error ("string table too large");
+
+  hdr->symbol_table = (p - buf);
+  sym = obj->symbols;
+  while (sym)
+    {
+      if (sym->segment)
+	{
+	  struct grub_obj_symbol *s;
+
+	  s = (struct grub_obj_symbol *) p;
+	  p += sizeof (struct grub_obj_symbol);
+	  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+	    grub_util_error ("symbol table too large");
+
+	  s->segment = sym->segment->index;
+	  s->name = grub_strtab_find (strtab, sym->name);
+	  s->offset = sym->symbol.offset + sym->segment->segment.offset;
+	}
+      sym = sym->next;
+    }
+  *(p++) = GRUB_OBJ_SEGMENT_END;
+  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+    grub_util_error ("symbol table too large");
+
+  hdr->reloc_table = (p - buf);
+  rel = obj->relocs;
+  while (rel)
+    {
+      if (rel->segment)
+	{
+	  struct grub_obj_reloc_extern *r;
+
+	  r = (struct grub_obj_reloc_extern *) p;
+	  p += ((rel->symbol_segment) ? sizeof (struct grub_obj_reloc) :
+		sizeof (struct grub_obj_reloc_extern));
+	  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+	    grub_util_error ("symbol table too large");
+
+	  r->segment = rel->segment->index;
+	  r->type = rel->reloc.type;
+	  r->offset = rel->reloc.offset + rel->segment->segment.offset;
+	  if (rel->symbol_segment)
+	    {
+	      r->symbol_segment = rel->symbol_segment->index;
+	    }
+	  else
+	    {
+	      r->symbol_segment = GRUB_OBJ_SEGMENT_END;
+	      r->symbol_name = grub_strtab_find (strtab, rel->symbol_name);
+	    }
+	}
+      rel = rel->next;
+    }
+  *(p++) = GRUB_OBJ_SEGMENT_END;
+  if (p - buf >= GRUB_OBJ_HEADER_MAX)
+    grub_util_error ("symbol table too large");
+
+  hdr->string_table = (p - buf);
+  offset = strtab_size + hdr->string_table;
+  idx = 0;
+  while (1)
+    {
+      hdr->segments[idx].offset += offset;
+      if (hdr->segments[idx].type == GRUB_OBJ_SEGMENT_END)
+	break;
+      idx++;
+    }
+  hdr->mod_deps = hdr->segments[idx].offset;
+
+  grub_util_write_image (buf, hdr->string_table, fp);
+  free (buf);
+
+  buf = xmalloc (strtab_size);
+  p = buf;
+  *(p++) = 0;
+
+  while (strtab)
+    {
+      grub_strtab_t cur;
+
+      cur = strtab;
+      strtab = strtab->next;
+
+      strcpy (p, cur->name);
+      p += cur->len + 1;
+      free (cur);
+    }
+
+  grub_util_write_image (buf, strtab_size, fp);
+  free (buf);
+
+  buf = xmalloc_zero (256);
+
+  seg = obj->segments;
+  raw_size = 0;
+  while (seg)
+    {
+      struct grub_util_obj_segment *cur;
+
+      cur = seg;
+      seg = seg->next;
+
+      if (! cur->segment.offset)
+	raw_size = 0;
+
+      if (cur->segment.type != GRUB_OBJ_SEG_BSS)
+	{
+	  grub_uint32_t mask, size;
+	  int is_last;
+
+	  mask = cur->segment.align - 1;
+	  size = (raw_size + mask) & ~mask;
+	  if (size != raw_size)
+	    {
+	      if (size - raw_size > 256)
+		grub_util_error ("alignment too large");
+
+	      grub_util_write_image (buf, size - raw_size, fp);
+	    }
+
+	  raw_size = size;
+	  is_last = ((! seg) || (! seg->segment.offset));
+	  size = (is_last) ? cur->raw_size : cur->segment.size;
+	  grub_util_write_image (cur->data, size, fp);
+	  raw_size += size;
+	}
+      else
+	break;
+    }
+
+  strcpy (buf, mod_name);
+  grub_util_write_image (buf, mod_name_len + 2, fp);
+  free (buf);
+}
+
+struct grub_util_obj *
+grub_obj_load (char *image, int size, int load_data)
+{
+  struct grub_util_obj *obj;
+  struct grub_obj_header *hdr;
+  struct grub_obj_symbol *sym;
+  struct grub_obj_reloc_extern *rel;
+  char *strtab;
+  struct grub_util_obj_segment **segments;
+  int i;
+
+  hdr = (struct grub_obj_header *) image;
+
+  if ((size <= (int) sizeof (*hdr)) || (hdr->magic != GRUB_OBJ_HEADER_MAGIC))
+    grub_util_error ("invalid module file");
+
+  if (hdr->version != GRUB_OBJ_HEADER_VERSION)
+    grub_util_error ("version number not match");
+
+  obj = xmalloc_zero (sizeof (*obj));
+  segments = xmalloc_zero (256 * sizeof (segments[0]));
+
+  for (i = 0; hdr->segments[i].type != GRUB_OBJ_SEGMENT_END; i++)
+    {
+      struct grub_util_obj_segment *p;
+
+      p = xmalloc_zero (sizeof (*p));
+      p->segment.type = hdr->segments[i].type;
+      p->segment.align = hdr->segments[i].align;
+      p->segment.size = hdr->segments[i].size;
+      p->file_off = hdr->segments[i].offset;
+      p->raw_size = hdr->segments[i + 1].offset - p->file_off;
+      p->index = i;
+
+      if ((p->raw_size) && (load_data))
+	{
+	  p->data = xmalloc_zero (p->segment.size);
+	  memcpy (p->data, image + p->file_off, p->raw_size);
+	}
+
+      segments[i] = p;
+      grub_list_push (GRUB_AS_LIST_P (&obj->segments), GRUB_AS_LIST (p));
+    }
+
+  obj->mod_attr = hdr->segments[i].offset;
+
+  strtab = image + hdr->string_table;
+  for (sym = (struct grub_obj_symbol *) (image + hdr->symbol_table);
+       sym->segment != GRUB_OBJ_SEGMENT_END; sym++)
+    {
+      struct grub_util_obj_symbol *p;
+
+      p = xmalloc_zero (sizeof (*p));
+      p->name = xstrdup (strtab + sym->name);
+      p->segment = segments[sym->segment];
+      p->symbol.offset = sym->offset;
+
+      grub_list_push (GRUB_AS_LIST_P (&obj->symbols), GRUB_AS_LIST (p));
+    }
+
+  for (rel = (struct grub_obj_reloc_extern *) (image + hdr->reloc_table);
+       rel->segment != GRUB_OBJ_SEGMENT_END;)
+    {
+      struct grub_util_obj_reloc *p;
+
+      p = xmalloc_zero (sizeof (*p));
+      p->segment = segments[rel->segment];
+      p->reloc.type = rel->type;
+      p->reloc.offset = rel->offset;
+      if (rel->symbol_segment == GRUB_OBJ_SEGMENT_END)
+	{
+	  p->symbol_name = xstrdup (strtab + rel->symbol_name);
+	  rel++;
+	}
+      else
+	{
+	  p->symbol_segment = segments[rel->symbol_segment];
+	  rel = (struct grub_obj_reloc_extern *)
+	    ((char *) rel + sizeof (struct grub_obj_reloc));
+	}
+
+      grub_list_push (GRUB_AS_LIST_P (&obj->relocs), GRUB_AS_LIST (p));
+    }
+
+  free (segments);
+  grub_obj_reverse (obj);
+  return obj;
+}
+
+void
+grub_obj_free (struct grub_util_obj *obj)
+{
+  struct grub_util_obj_segment *seg;
+  struct grub_util_obj_symbol *sym;
+  struct grub_util_obj_reloc *rel;
+
+  seg = obj->segments;
+  while (seg)
+    {
+      struct grub_util_obj_segment *p;
+
+      p = seg;
+      seg = seg->next;
+
+      if (p->data)
+	free (p->data);
+
+      free (p);
+    }
+
+  sym = obj->symbols;
+  while (sym)
+    {
+      struct grub_util_obj_symbol *p;
+
+      p = sym;
+      sym = sym->next;
+
+      if (p->name)
+	free (p->name);
+
+      free (p);
+    }
+
+  rel = obj->relocs;
+  while (sym)
+    {
+      struct grub_util_obj_reloc *p;
+
+      p = rel;
+      rel = rel->next;
+
+      if (p->symbol_name)
+	free (p->symbol_name);
+
+      free (p);
+    }
+}
diff --git a/util/obj_dump.c b/util/obj_dump.c
new file mode 100644
index 0000000..4bfab89
--- /dev/null
+++ b/util/obj_dump.c
@@ -0,0 +1,140 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *
+get_segment_name (int type)
+{
+  switch (type)
+    {
+    case GRUB_OBJ_SEG_TEXT:
+      return ".text";
+
+    case GRUB_OBJ_SEG_DATA:
+      return ".data";
+
+    case GRUB_OBJ_SEG_RDATA:
+      return ".rdata";
+
+    case GRUB_OBJ_SEG_BSS:
+      return ".bss";
+    }
+
+  return "unknown";
+}
+
+static char *
+get_reloc_type (int type)
+{
+  switch (type)
+    {
+    case GRUB_OBJ_REL_DIR32:
+      return "dir32";
+
+    case GRUB_OBJ_REL_REL32:
+      return "rel32";
+
+    case GRUB_OBJ_REL_DIR64:
+      return "dir64";
+
+    case GRUB_OBJ_REL_REL64:
+      return "rel64";
+    }
+
+  return "unknown";
+}
+
+static int
+dump_segments_hook (struct grub_util_obj_segment *obj)
+{
+  printf ("%-10s%08x  %08x  %08x  %08x  %d\n",
+	  get_segment_name (obj->segment.type),
+	  obj->segment.offset, obj->segment.size, obj->raw_size,
+	  obj->file_off, obj->segment.align);
+
+  return 0;
+}
+
+void
+grub_obj_dump_segments (struct grub_util_obj *obj)
+{
+  printf ("Segments:\n"
+	  "Segment   Offset    Size      Raw Size  File Off  Align\n");
+  grub_list_iterate (GRUB_AS_LIST (obj->segments),
+		     (grub_list_hook_t) dump_segments_hook);
+}
+
+static int
+dump_symbols_hook (struct grub_util_obj_symbol *obj)
+{
+  if (obj->segment)
+    printf ("%-10s%08x  %s\n",
+	    get_segment_name (obj->segment->segment.type),
+	    obj->symbol.offset + obj->segment->segment.offset, obj->name);
+
+  return 0;
+}
+
+void
+grub_obj_dump_symbols (struct grub_util_obj *obj)
+{
+  printf ("Symbols:\n"
+	  "Segment   Offset    Name\n");
+  grub_list_iterate (GRUB_AS_LIST (obj->symbols),
+		     (grub_list_hook_t) dump_symbols_hook);
+}
+
+static int
+dump_reloc_hook (struct grub_util_obj_reloc *obj)
+{
+  if (obj->segment)
+    {
+      grub_uint32_t value;
+
+      if (obj->segment->data)
+	value = *((grub_uint32_t *) (obj->segment->data + obj->reloc.offset));
+      else
+	value = 0;
+
+      printf ("%-10s%08x  %08x  %-10s%s\n",
+	      get_segment_name (obj->segment->segment.type),
+	      obj->reloc.offset + obj->segment->segment.offset, value,
+	      get_reloc_type (obj->reloc.type),
+	      ((! obj->symbol_segment) ? obj->symbol_name :
+	       get_segment_name (obj->symbol_segment->segment.type)));
+    }
+
+  return 0;
+}
+
+void
+grub_obj_dump_relocs (struct grub_util_obj *obj)
+{
+  printf ("Relocs:\n"
+	  "Segment   Offset    Value     Type      Name\n");
+  grub_list_iterate (GRUB_AS_LIST (obj->relocs),
+		     (grub_list_hook_t) dump_reloc_hook);
+}
diff --git a/util/obj_elf.c b/util/obj_elf.c
new file mode 100644
index 0000000..4056c06
--- /dev/null
+++ b/util/obj_elf.c
@@ -0,0 +1,365 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+#include <grub/elf.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if GRUB_TARGET_SIZEOF_VOID_P == 4
+
+#define grub_target_to_host	grub_target_to_host32
+#define grub_host_to_target	grub_host_to_target32
+
+#elif GRUB_TARGET_SIZEOF_VOID_P == 8
+
+#define grub_target_to_host	grub_target_to_host64
+#define grub_host_to_target	grub_host_to_target64
+
+#endif
+
+static int
+check_elf_header (Elf_Ehdr *e, size_t size)
+{
+  if (size < sizeof (*e)
+      || e->e_ident[EI_MAG0] != ELFMAG0
+      || e->e_ident[EI_MAG1] != ELFMAG1
+      || e->e_ident[EI_MAG2] != ELFMAG2
+      || e->e_ident[EI_MAG3] != ELFMAG3
+      || e->e_ident[EI_VERSION] != EV_CURRENT
+      || e->e_version != EV_CURRENT)
+     return 0;
+
+  return 1;
+}
+
+/* Return the symbol table section, if any.  */
+static Elf_Shdr *
+find_symtab_section (Elf_Shdr *sections,
+		     Elf_Half section_entsize, Elf_Half num_sections)
+{
+  int i;
+  Elf_Shdr *s;
+
+  for (i = 0, s = sections;
+       i < num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
+    if (s->sh_type == grub_cpu_to_le32 (SHT_SYMTAB))
+      return s;
+
+  return 0;
+}
+
+static void
+add_segments (struct grub_util_obj *obj,
+	      struct grub_util_obj_segment **segments,
+	      char *image,
+	      Elf_Shdr *sections, int section_entsize, int num_sections)
+{
+  Elf_Shdr *s;
+  int i;
+
+  for (i = 0, s = sections;
+       i < num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
+    {
+      int type;
+
+      if ((s->sh_flags & grub_cpu_to_le32 (SHF_EXECINSTR | SHF_ALLOC))
+	  == grub_cpu_to_le32 (SHF_EXECINSTR | SHF_ALLOC))
+	type = GRUB_OBJ_SEG_TEXT;
+      else if (s->sh_flags & grub_cpu_to_le32 (SHF_ALLOC)
+	       && ! (s->sh_flags & grub_cpu_to_le32 (SHF_EXECINSTR)))
+	{
+	  if (! (s->sh_flags & grub_cpu_to_le32 (SHF_WRITE)))
+	    type = GRUB_OBJ_SEG_RDATA;
+	  else if (s->sh_type == grub_cpu_to_le32 (SHT_NOBITS))
+	    type = GRUB_OBJ_SEG_BSS;
+	  else
+	    type = GRUB_OBJ_SEG_DATA;
+	}
+      else
+	type = 0;
+
+
+      if ((type) && ((type == GRUB_OBJ_SEG_BSS) || (s->sh_size)))
+	{
+	  struct grub_util_obj_segment *p;
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->segment.type = type;
+	  p->segment.align = grub_le_to_cpu32 (s->sh_addralign);
+	  p->segment.size = grub_le_to_cpu32 (s->sh_size);
+	  segments[i] = p;
+
+	  if (type == GRUB_OBJ_SEG_BSS)
+	    {
+	      p->raw_size = p->segment.size;
+	      if (segments[0])
+		grub_util_error ("mutiple .bss segment");
+	      segments[0] = p;
+	    }
+	  else
+	    {
+	      p->raw_size = p->segment.size;
+	      p->data = xmalloc (p->raw_size);
+	      memcpy (p->data, image + grub_le_to_cpu32 (s->sh_offset),
+		      p->raw_size);
+	      segments[i] = p;
+	      grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+			      GRUB_AS_LIST (p));
+
+	    }
+	}
+    }
+
+  if (! segments[0])
+    grub_util_error ("no .bss segment");
+}
+
+static void
+add_symbols (struct grub_util_obj *obj,
+	     struct grub_util_obj_segment **segments,
+	     char *image,
+	     Elf_Shdr *sections, int section_entsize, int num_sections)
+{
+  int i;
+  Elf_Shdr *symtab_section, *str_sec;
+  Elf_Sym *sym;
+  int num_syms, sym_size;
+  char *strtab;
+
+  symtab_section = find_symtab_section (sections,
+					section_entsize, num_sections);
+  sym = (Elf_Sym *) (image + grub_target_to_host (symtab_section->sh_offset));
+  sym_size = grub_target_to_host32 (symtab_section->sh_entsize);
+  num_syms = grub_target_to_host32 (symtab_section->sh_size) / sym_size;
+  str_sec = (Elf_Shdr *) ((char *) sections
+			  + (grub_target_to_host32 (symtab_section->sh_link)
+			     * section_entsize));
+  strtab = image + grub_target_to_host32 (str_sec->sh_offset);
+
+  for (i = 0; i < num_syms;
+       i++, sym = (Elf_Sym *) ((char *) sym + sym_size))
+    {
+      Elf_Section index;
+      char *name;
+
+      name = strtab + grub_target_to_host32 (sym->st_name);
+
+      if ((ELF_ST_BIND (sym->st_info) == STB_LOCAL) &&
+	  (strcmp (name, "grub_mod_init")) &&
+	  (strcmp (name, "grub_mod_fini")))
+	continue;
+
+      index = grub_target_to_host16 (sym->st_shndx);
+
+      if (index == STN_UNDEF)
+	continue;
+
+      if (index == STN_COMMON)
+	{
+	  if (! sym->st_value)
+	    continue;
+	  index = 0;
+	}
+
+      if ((index < num_sections) && (segments[index]))
+	{
+	  struct grub_util_obj_symbol *p;
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->name = xstrdup (name);
+	  p->segment = segments[index];
+	  if (! index)
+	    {
+	      p->symbol.offset = p->segment->segment.size;
+	      p->segment->segment.size += grub_target_to_host (sym->st_value);
+	    }
+	  else
+	    p->symbol.offset = grub_target_to_host (sym->st_value);
+
+	  grub_list_push (GRUB_AS_LIST_P (&obj->symbols),
+			  GRUB_AS_LIST (p));
+	}
+    }
+}
+
+static void
+add_relocs (struct grub_util_obj *obj,
+	    struct grub_util_obj_segment **segments,
+	    char *image,
+	    Elf_Shdr *sections, int section_entsize, int num_sections)
+{
+  int i;
+  Elf_Shdr *s;
+
+  for (i = 0, s = sections;
+       i < num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
+    if ((s->sh_type == grub_cpu_to_le32 (SHT_REL)) ||
+	(s->sh_type == grub_cpu_to_le32 (SHT_RELA)))
+      {
+	Elf_Rela *r;
+	Elf_Shdr *sym_sec, *str_sec;
+	int sym_size;
+	char *strtab;
+	Elf_Word r_size, num_rs, j;
+	Elf_Word target_index;
+
+	sym_sec = (Elf_Shdr *) ((char *) sections
+				+ (grub_target_to_host32 (s->sh_link)
+				   * section_entsize));
+	sym_size = grub_target_to_host32 (sym_sec->sh_entsize);
+
+	str_sec = (Elf_Shdr *) ((char *) sections
+				+ (grub_target_to_host32 (sym_sec->sh_link)
+				   * section_entsize));
+	strtab = image + grub_target_to_host32 (str_sec->sh_offset);
+
+	target_index = grub_target_to_host32 (s->sh_info);
+	if (! segments[target_index])
+	  continue;
+
+	r_size = grub_target_to_host32 (s->sh_entsize);
+	num_rs = grub_target_to_host32 (s->sh_size) / r_size;
+
+	r = (Elf_Rela *) (image + grub_target_to_host32 (s->sh_offset));
+	for (j = 0;
+	     j < num_rs;
+	     j++, r = (Elf_Rela *) ((char *) r + r_size))
+	  {
+	    struct grub_util_obj_reloc *p;
+	    Elf_Addr info, offset;
+	    Elf_Sym *sym;
+	    int sym_idx;
+	    char *addr;
+	    int type;
+
+	    offset = grub_target_to_host (r->r_offset);
+	    if (! segments[target_index]->data)
+	      grub_util_error ("can\'t relocate in .bss segment");
+
+	    addr = segments[target_index]->data + offset;
+	    info = grub_target_to_host (r->r_info);
+
+	    type = -1;
+	    switch (ELF_R_TYPE (info))
+	      {
+	      case R_386_NONE:
+		break;
+
+	      case R_386_32:
+		type = GRUB_OBJ_REL_DIR32;
+		break;
+
+	      case R_386_PC32:
+		type = GRUB_OBJ_REL_REL32;
+		break;
+
+	      default:
+		grub_util_error ("unknown relocation type %d",
+				 ELF_R_TYPE (info));
+	      }
+
+	    if (type < 0)
+	      continue;
+
+	    if ((grub_target_to_host32 (s->sh_type) == SHT_RELA) &&
+		(r->r_addend))
+	      {
+		if (type & GRUB_OBJ_REL_64BIT)
+		  *((grub_uint64_t *) addr) += r->r_addend;
+		else
+		  *((grub_uint32_t *) addr) += r->r_addend;
+	      }
+
+	    p = xmalloc_zero (sizeof (*p));
+	    p->segment = segments[target_index];
+	    p->reloc.type = type;
+	    p->reloc.offset = offset;
+
+	    sym = (Elf_Sym *) (image
+			       + grub_target_to_host32 (sym_sec->sh_offset)
+			       + (ELF_R_SYM (info) * sym_size));
+	    sym_idx = grub_target_to_host16 (sym->st_shndx);
+	    if (sym_idx == STN_ABS)
+	      grub_util_error ("can\'t relocate absolute symbol");
+
+	    if ((sym_idx != STN_UNDEF) && (sym_idx != STN_COMMON))
+	      {
+		if (! segments[sym_idx])
+		  grub_util_error ("no symbol segment");
+
+		p->symbol_segment = segments[sym_idx];
+	      }
+	    p->symbol_name = xstrdup (strtab +
+				      grub_target_to_host32 (sym->st_name));
+
+	    if (type & GRUB_OBJ_REL_64BIT)
+	      *((grub_uint64_t *) addr) += grub_target_to_host (sym->st_value);
+	    else
+	      *((grub_uint32_t *) addr) += grub_target_to_host (sym->st_value);
+
+	    grub_list_push (GRUB_AS_LIST_P (&obj->relocs),
+			    GRUB_AS_LIST (p));
+	  }
+      }
+}
+
+int
+elf_add_file (struct grub_util_obj *obj, char *image, int size)
+{
+  Elf_Ehdr *e;
+  Elf_Shdr *sections;
+  Elf_Off section_offset;
+  Elf_Half section_entsize;
+  Elf_Half num_sections;
+  struct grub_util_obj_segment **segments;
+
+  e = (Elf_Ehdr *) image;
+  if (! check_elf_header (e, size))
+    return 0;
+
+  section_offset = grub_target_to_host (e->e_shoff);
+  section_entsize = grub_target_to_host16 (e->e_shentsize);
+  num_sections = grub_target_to_host16 (e->e_shnum);
+
+  if (size < (int) (section_offset + section_entsize * num_sections))
+    grub_util_error ("invalid ELF format");
+
+  sections = (Elf_Shdr *) (image + section_offset);
+  segments = xmalloc_zero (num_sections * sizeof (segments[0]));
+  add_segments (obj, segments, image, sections, section_entsize, num_sections);
+  add_symbols (obj, segments, image, sections, section_entsize, num_sections);
+  add_relocs (obj, segments, image, sections, section_entsize, num_sections);
+
+  if (segments[0]->segment.size)
+    grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+		    GRUB_AS_LIST (segments[0]));
+  else
+    free (segments[0]);
+
+  free (segments);
+  return 1;
+}
diff --git a/util/obj_pe.c b/util/obj_pe.c
new file mode 100644
index 0000000..e168fb1
--- /dev/null
+++ b/util/obj_pe.c
@@ -0,0 +1,272 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 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/obj.h>
+#include <grub/util/misc.h>
+#include <grub/efi/pe32.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void
+add_segments (struct grub_util_obj *obj,
+	      struct grub_util_obj_segment **segments,
+	      char *image,
+	      struct grub_pe32_section_table *pe_shdr, int num_secs)
+{
+  int i;
+
+  for (i = 0; i < num_secs; i++, pe_shdr++)
+    {
+      int type;
+
+      if (! strcmp (pe_shdr->name, ".text"))
+	type = GRUB_OBJ_SEG_TEXT;
+      else if (! strcmp (pe_shdr->name, ".data"))
+	type = GRUB_OBJ_SEG_DATA;
+      else if (! strcmp (pe_shdr->name, ".rdata"))
+	type = GRUB_OBJ_SEG_RDATA;
+      else if (! strcmp (pe_shdr->name, ".bss"))
+	type = GRUB_OBJ_SEG_BSS;
+      else
+	type = 0;
+
+      if ((type) && ((type == GRUB_OBJ_SEG_BSS) || (pe_shdr->raw_data_size)))
+	{
+	  struct grub_util_obj_segment *p;
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->segment.type = type;
+	  p->segment.align = 1 << (((pe_shdr->characteristics >>
+				     GRUB_PE32_SCN_ALIGN_SHIFT) &
+				    GRUB_PE32_SCN_ALIGN_MASK) - 1);
+	  p->segment.size = pe_shdr->raw_data_size;
+	  segments[i + 1] = p;
+
+	  if (type == GRUB_OBJ_SEG_BSS)
+	    {
+	      p->raw_size = p->segment.size;
+	      if (segments[0])
+		grub_util_error ("mutiple .bss segment");
+	      segments[0] = p;
+	    }
+	  else
+	    {
+	      p->raw_size = p->segment.size;
+	      p->data = xmalloc (pe_shdr->raw_data_size);
+	      memcpy (p->data, image + pe_shdr->raw_data_offset,
+		      pe_shdr->raw_data_size);
+
+	      grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+			      GRUB_AS_LIST (p));
+	    }
+	}
+    }
+
+  if (! segments[0])
+    grub_util_error ("no .bss segment");
+}
+
+static char *
+get_symbol_name (struct grub_pe32_symbol *pe_sym, char *pe_strtab)
+{
+  char short_name[9];
+  char *name;
+
+  if (pe_sym->long_name[0])
+    {
+      strncpy (short_name, pe_sym->short_name, 8);
+      short_name[8] = 0;
+      name = short_name;
+    }
+  else
+    name = pe_strtab + pe_sym->long_name[1];
+
+  if (*name == '_')
+    name++;
+
+  return xstrdup (name);
+}
+
+static void
+add_symbols (struct grub_util_obj *obj,
+	     struct grub_util_obj_segment **segments,
+	     struct grub_pe32_symbol *pe_symtab, int num_syms,
+	     char *pe_strtab)
+{
+  int i;
+
+  for (i = 0; i < num_syms;
+       i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
+    {
+      struct grub_util_obj_symbol *p;
+      char *name;
+
+      name = get_symbol_name (pe_symtab, pe_strtab);
+
+      if ((pe_symtab->section > num_syms) ||
+	  (! segments[pe_symtab->section]) ||
+	  ((pe_symtab->storage_class != GRUB_PE32_SYM_CLASS_EXTERNAL) &&
+	   (strcmp (name, "grub_mod_init")) &&
+	   (strcmp (name, "grub_mod_fini"))))
+	{
+	  free (name);
+	  continue;
+	}
+
+      if ((! pe_symtab->section) && (! pe_symtab->value))
+	{
+	  free (name);
+	  continue;
+	}
+
+      p = xmalloc_zero (sizeof (*p));
+      p->name = name;
+      p->segment = segments[pe_symtab->section];
+
+      if (! pe_symtab->section)
+	{
+	  p->symbol.offset = p->segment->segment.size;
+	  p->segment->segment.size += pe_symtab->value;
+	}
+      else
+	p->symbol.offset = pe_symtab->value;
+
+      grub_list_push (GRUB_AS_LIST_P (&obj->symbols), GRUB_AS_LIST (p));
+    }
+}
+
+static void
+add_relocs (struct grub_util_obj *obj,
+	    struct grub_util_obj_segment **segments,
+	    char *image,
+	    struct grub_pe32_section_table *pe_sec, int num_secs,
+	    struct grub_pe32_symbol *pe_symtab, int num_syms,
+	    char *pe_strtab)
+{
+  int i;
+
+  for (i = 0; i < num_secs; i++, pe_sec++)
+    {
+      struct grub_pe32_reloc *pe_rel;
+      int j;
+
+      if (! segments[i + 1])
+	continue;
+
+      pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
+      for (j = 0; j < pe_sec->num_relocations; j++, pe_rel++)
+	{
+	  struct grub_util_obj_reloc *p;
+	  struct grub_pe32_symbol *pe_sym;
+	  int type;
+
+	  pe_sym = pe_symtab + pe_rel->symtab_index;
+
+	  if (((int) pe_rel->symtab_index >= num_syms) ||
+	      ((pe_sym->section) && (! segments[pe_sym->section])))
+	    grub_util_error ("invalid symbol index");
+
+	  if (pe_rel->type == GRUB_PE32_REL_I386_DIR32)
+	    type = GRUB_OBJ_REL_DIR32;
+	  else if (pe_rel->type == GRUB_PE32_REL_I386_REL32)
+	    type = GRUB_OBJ_REL_REL32;
+	  else
+	    grub_util_error ("unknown pe relocation type %d\n", pe_rel->type);
+
+	  p = xmalloc_zero (sizeof (*p));
+	  p->segment = segments[i + 1];
+	  p->reloc.type = type;
+	  p->reloc.offset = pe_rel->offset - pe_sec->virtual_address;
+	  if (pe_sym->section)
+	    p->symbol_segment = segments[pe_sym->section];
+	  p->symbol_name = get_symbol_name (pe_sym, pe_strtab);
+
+	  if (! p->segment->data)
+	    grub_util_error ("can\'t relocate in .bss segment");
+
+	  if (type & GRUB_OBJ_REL_ISREL)
+	    {
+	      grub_uint8_t code;
+	      grub_uint32_t *addr;
+
+	      addr = (grub_uint32_t *) (p->segment->data + p->reloc.offset);
+	      code = p->segment->data[p->reloc.offset - 1];
+	      if (((code != 0xe8) && (code != 0xe9)) || (*addr))
+		grub_util_error ("invalid relocation (%x %x)", code, *addr);
+
+	      if (p->reloc.type & GRUB_OBJ_REL_64BIT)
+		*((grub_uint64_t *) addr) = -4;
+	      else
+		*((grub_uint32_t *) addr) = -4;
+	    }
+
+	  grub_list_push (GRUB_AS_LIST_P (&obj->relocs), GRUB_AS_LIST (p));
+	}
+    }
+}
+
+static int
+check_pe_header (struct grub_pe32_coff_header *c, size_t size)
+{
+  if ((size < sizeof (*c) ||
+       (grub_le_to_cpu16 (c->machine) != GRUB_PE32_MACHINE_I386)))
+    return 0;
+
+  return 1;
+}
+
+int
+pe_add_file (struct grub_util_obj *obj, char *image, int size)
+{
+  struct grub_pe32_coff_header *pe_chdr;
+  struct grub_pe32_section_table *pe_shdr;
+  struct grub_pe32_symbol *pe_symtab;
+  int num_secs, num_syms;
+  char *pe_strtab;
+  struct grub_util_obj_segment **segments;
+
+  pe_chdr = (struct grub_pe32_coff_header *) image;
+  if (! check_pe_header (pe_chdr, size))
+    return 0;
+
+  pe_shdr = (struct grub_pe32_section_table *) (pe_chdr + 1);
+  num_secs = pe_chdr->num_sections;
+  segments = xmalloc_zero ((num_secs + 1) * sizeof (segments[0]));
+  add_segments (obj, segments, image, pe_shdr, num_secs);
+
+  pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
+  num_syms = pe_chdr->num_symbols;
+  pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
+
+  add_symbols (obj, segments, pe_symtab, num_syms, pe_strtab);
+  add_relocs (obj, segments, image, pe_shdr, num_secs,
+	      pe_symtab, num_syms, pe_strtab);
+
+  if (segments[0]->segment.size)
+    grub_list_push (GRUB_AS_LIST_P (&obj->segments),
+		    GRUB_AS_LIST (segments[0]));
+  else
+    free (segments[0]);
+
+  free (segments);
+  return 1;
+}
diff --git a/util/resolve.c b/util/resolve.c
index 8b33beb..32c9e82 100644
--- a/util/resolve.c
+++ b/util/resolve.c
@@ -126,58 +126,6 @@ read_dep_list (FILE *fp)
   return dep_list;
 }
 
-static char *
-get_module_name (const char *str)
-{
-  char *base;
-  char *ext;
-
-  base = strrchr (str, '/');
-  if (! base)
-    base = (char *) str;
-  else
-    base++;
-
-  ext = strrchr (base, '.');
-  if (ext && strcmp (ext, ".mod") == 0)
-    {
-      char *name;
-
-      name = xmalloc (ext - base + 1);
-      memcpy (name, base, ext - base);
-      name[ext - base] = '\0';
-      return name;
-    }
-
-  return xstrdup (base);
-}
-
-static char *
-get_module_path (const char *prefix, const char *str)
-{
-  char *dir;
-  char *base;
-  char *ext;
-  char *ret;
-
-  ext = strrchr (str, '.');
-  if (ext && strcmp (ext, ".mod") == 0)
-    base = xstrdup (str);
-  else
-    {
-      base = xmalloc (strlen (str) + 4 + 1);
-      sprintf (base, "%s.mod", str);
-    }
-
-  dir = strchr (str, '/');
-  if (dir)
-    return base;
-
-  ret = grub_util_get_path (prefix, base);
-  free (base);
-  return ret;
-}
-
 static void
 add_module (const char *dir,
 	    struct dep_list *dep_list,
@@ -190,7 +138,7 @@ add_module (const char *dir,
   struct mod_list *mod;
   struct dep_list *dep;
 
-  mod_name = get_module_name (name);
+  mod_name = grub_util_get_module_name (name);
 
   /* Check if the module has already been added.  */
   for (mod = *mod_head; mod; mod = mod->next)
@@ -218,7 +166,7 @@ add_module (const char *dir,
 
   /* Add this path.  */
   path = (struct grub_util_path_list *) xmalloc (sizeof (*path));
-  path->name = get_module_path (dir, name);
+  path->name = grub_util_get_module_path (dir, name);
   path->next = *path_head;
   *path_head = path;
 }

^ permalink raw reply related	[flat|nested] 25+ messages in thread

* Re: please stop this
  2009-07-14 18:17     ` Bean
@ 2009-07-16 15:23       ` Robert Millan
  2009-07-16 16:10         ` Vladimir 'phcoder' Serbinenko
  0 siblings, 1 reply; 25+ messages in thread
From: Robert Millan @ 2009-07-16 15:23 UTC (permalink / raw)
  To: The development of GRUB 2

On Wed, Jul 15, 2009 at 02:17:39AM +0800, Bean wrote:
> On Wed, Jul 15, 2009 at 2:09 AM, Pavel Roskin<proski@gnu.org> wrote:
> > On Tue, 2009-07-14 at 19:57 +0200, Robert Millan wrote:
> >
> >> I agree that we have a problem due to lack of leadership, but this is not
> >> acceptable.  Marco is busy right now (traveling), so please put this on hold
> >> untill he's back, then we can discuss it.
> >
> > I agree that we should not rush with such changes.  However, publishing
> > patches for discussion and testing is a good thing and should not be
> > discouraged.
> 
> Hi,
> 
> Yeah, I don't mean to push it. In fact, I've created a git repository
> for my temporary work
> 
> http://repo.or.cz/w/grub2/bean.git

Hi,

Usually, I only go through the trouble of implementing things when it's
clear they will be merged in some form.  But I understand it's not
the same for everyone.  So if I missunderstood, please accept my apology.

In any case, this kind of changes need wider consensus, and including the
maintainers in it.  And in general, I think we should hold off from big
restructuring at this time.  Using branches is a good idea IMHO (be it
"someone's branch" or "pupa2" or whatever).

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: please stop this
  2009-07-16 15:23       ` Robert Millan
@ 2009-07-16 16:10         ` Vladimir 'phcoder' Serbinenko
  2009-07-18 18:15           ` Robert Millan
  0 siblings, 1 reply; 25+ messages in thread
From: Vladimir 'phcoder' Serbinenko @ 2009-07-16 16:10 UTC (permalink / raw)
  To: The development of GRUB 2

On Thu, Jul 16, 2009 at 5:23 PM, Robert Millan<rmh@aybabtu.com> wrote:
> On Wed, Jul 15, 2009 at 02:17:39AM +0800, Bean wrote:
>> On Wed, Jul 15, 2009 at 2:09 AM, Pavel Roskin<proski@gnu.org> wrote:
>> > On Tue, 2009-07-14 at 19:57 +0200, Robert Millan wrote:
>> >
>> >> I agree that we have a problem due to lack of leadership, but this is not
>> >> acceptable.  Marco is busy right now (traveling), so please put this on hold
>> >> untill he's back, then we can discuss it.
>> >
>> > I agree that we should not rush with such changes.  However, publishing
>> > patches for discussion and testing is a good thing and should not be
>> > discouraged.
>>
>> Hi,
>>
>> Yeah, I don't mean to push it. In fact, I've created a git repository
>> for my temporary work
>>
>> http://repo.or.cz/w/grub2/bean.git
>
> Hi,
>
> Usually, I only go through the trouble of implementing things when it's
> clear they will be merged in some form.  But I understand it's not
> the same for everyone.  So if I missunderstood, please accept my apology.
In some cases actually implementing something is needed to know
whether it will give an advantage
>
> In any case, this kind of changes need wider consensus, and including the
> maintainers in it.
The problem is that most comments are in the form "maybe I agree maybe
I don't". Such kind of discussions may never result in consensus.
Useful patches lying in bitrot somewhere on the list is unfortunately
something common. We need a better organisation and more dynamism if
we want project to advance. New maintainer can make these changes
happen. And generally I tend to accept a rule "absent person is wrong
and agrees". Not because I don't respect other people but just because
I don't see why project would eternally wait for someone to come by. I
think that main rules are Sane-o-cracy and Do-o-cracy: As long as
choice is sane and expandable doer decides
>  And in general, I think we should hold off from big
> restructuring at this time.  Using branches is a good idea IMHO (be it
> "someone's branch" or "pupa2" or whatever).
Having too many branches per developer may prevent GRUB from being
GRand and Unified.
>
> --
> Robert Millan
>
>  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
>  how) you may access your data; but nobody's threatening your freedom: we
>  still allow you to remove your data and not access it at all."
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>



-- 
Regards
Vladimir 'phcoder' Serbinenko

Personal git repository: http://repo.or.cz/w/grub2/phcoder.git



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: please stop this
  2009-07-16 16:10         ` Vladimir 'phcoder' Serbinenko
@ 2009-07-18 18:15           ` Robert Millan
  2009-07-18 18:53             ` Vladimir 'phcoder' Serbinenko
  0 siblings, 1 reply; 25+ messages in thread
From: Robert Millan @ 2009-07-18 18:15 UTC (permalink / raw)
  To: The development of GRUB 2

On Thu, Jul 16, 2009 at 06:10:48PM +0200, Vladimir 'phcoder' Serbinenko wrote:
> >
> > Usually, I only go through the trouble of implementing things when it's
> > clear they will be merged in some form.  But I understand it's not
> > the same for everyone.  So if I missunderstood, please accept my apology.
> In some cases actually implementing something is needed to know
> whether it will give an advantage

Agreed, in some cases it helps.

> > In any case, this kind of changes need wider consensus, and including the
> > maintainers in it.
> The problem is that most comments are in the form "maybe I agree maybe
> I don't". Such kind of discussions may never result in consensus.
> Useful patches lying in bitrot somewhere on the list is unfortunately
> something common. We need a better organisation and more dynamism if
> we want project to advance. New maintainer can make these changes
> happen. And generally I tend to accept a rule "absent person is wrong
> and agrees". Not because I don't respect other people but just because
> I don't see why project would eternally wait for someone to come by. I
> think that main rules are Sane-o-cracy and Do-o-cracy: As long as
> choice is sane and expandable doer decides

I agree we need better organisation, but I don't think even more dynamism is
the answer.  Actually I think we've got _more_ dynamism than we can handle
(I don't think dynamism is bad, but we have to acknowledge our limits).

Some people do lots of work, sometimes things we obviously want, and
sometimes design changes that need careful review and analisys.  The
problem is that our resources to do the latter are not a la par with the
amount of contribution we receive.

What we have now is that often a single person comes up with an idea that
changes GRUB in significant ways, it is proposed, and because we lack the
resources to review it it's not reviewed, or only barely so.  Then the
"absent person is wrong and agrees" rule prevails, change is merged, and
we may later find that this is not really what we wanted.

Marco's been in Catalonia this week.  Last thursday he came to Barcelona and
we had some talk about the situation with GRUB.  I can assure you that he's
concerned about this.  He sees this "maintainer's not here so let's do what
ever we want" approach as a problem, although it's pretty understandable
given the situation (he acknoledges that as well).

> >  And in general, I think we should hold off from big
> > restructuring at this time.  Using branches is a good idea IMHO (be it
> > "someone's branch" or "pupa2" or whatever).
> Having too many branches per developer may prevent GRUB from being
> GRand and Unified.

I think the "Unified" means it supports multiple filesystems, the Multiboot
standard, etc, allowing it to support a wide variety of OSes.  But it's just
a word anyway...

IMO, branches are useful sometimes, because they make it easier to experiment
with new things without risk of breaking our core functionality.

-- 
Robert Millan

  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
  how) you may access your data; but nobody's threatening your freedom: we
  still allow you to remove your data and not access it at all."



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: please stop this
  2009-07-18 18:15           ` Robert Millan
@ 2009-07-18 18:53             ` Vladimir 'phcoder' Serbinenko
  0 siblings, 0 replies; 25+ messages in thread
From: Vladimir 'phcoder' Serbinenko @ 2009-07-18 18:53 UTC (permalink / raw)
  To: The development of GRUB 2

>> > In any case, this kind of changes need wider consensus, and including the
>> > maintainers in it.
>> The problem is that most comments are in the form "maybe I agree maybe
>> I don't". Such kind of discussions may never result in consensus.
>> Useful patches lying in bitrot somewhere on the list is unfortunately
>> something common. We need a better organisation and more dynamism if
>> we want project to advance. New maintainer can make these changes
>> happen. And generally I tend to accept a rule "absent person is wrong
>> and agrees". Not because I don't respect other people but just because
>> I don't see why project would eternally wait for someone to come by. I
>> think that main rules are Sane-o-cracy and Do-o-cracy: As long as
>> choice is sane and expandable doer decides
>
> I agree we need better organisation, but I don't think even more dynamism is
> the answer.  Actually I think we've got _more_ dynamism than we can handle
> (I don't think dynamism is bad, but we have to acknowledge our limits).
>
> Some people do lots of work, sometimes things we obviously want, and
> sometimes design changes that need careful review and analisys.  The
> problem is that our resources to do the latter are not a la par with the
> amount of contribution we receive.
I don't think that grub as a project isn't able to handle incoming
patches. I think that lack of organisation just wastes the effort.
Perhaps we could use some kind of core team approach. One example is
to designate N core team members and need a confirmation of floor
(N/2) core members for design changes and no oppositions and that at
least one weak passed after such consensus and a confirmation of one
core team member for ordinary patches or in case if author is in core
team and no further replies one weak that patch is laying on the list.
This way even if some core members are on vacation the system
continues to work
>
> What we have now is that often a single person comes up with an idea that
> changes GRUB in significant ways, it is proposed, and because we lack the
> resources to review it it's not reviewed, or only barely so.  Then the
> "absent person is wrong and agrees" rule prevails, change is merged, and
> we may later find that this is not really what we wanted.
>
> Marco's been in Catalonia this week.  Last thursday he came to Barcelona and
> we had some talk about the situation with GRUB.  I can assure you that he's
> concerned about this.  He sees this "maintainer's not here so let's do what
> ever we want" approach as a problem, although it's pretty understandable
> given the situation (he acknoledges that as well).
>
If he'll be soonish available for discussion we should wait for him
and discuss this altogether.
> I think the "Unified" means it supports multiple filesystems, the Multiboot
> standard, etc, allowing it to support a wide variety of OSes.
Exactly. And if patches have problems to get in we risk not to recieve
features the project merits and gets loads of forks (=fragmentation
instead of unification) instead
>  But it's just
> a word anyway...
>
> IMO, branches are useful sometimes, because they make it easier to experiment
> with new things without risk of breaking our core functionality.
>
Could we perhaps create a developement branch in svn to share code easier?
> --
> Robert Millan
>
>  The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and
>  how) you may access your data; but nobody's threatening your freedom: we
>  still allow you to remove your data and not access it at all."
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>



-- 
Regards
Vladimir 'phcoder' Serbinenko

Personal git repository: http://repo.or.cz/w/grub2/phcoder.git



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-14 15:49 [PATCH] New object format Bean
  2009-07-14 17:57 ` please stop this Robert Millan
  2009-07-14 21:02 ` [PATCH] New object format Bean
@ 2009-07-19  1:52 ` Isaac Dupree
  2009-07-19 10:03   ` Bean
  2 siblings, 1 reply; 25+ messages in thread
From: Isaac Dupree @ 2009-07-19  1:52 UTC (permalink / raw)
  To: The development of GRUB 2

Bean wrote:
> Hi,
> 
> This patch implement a new object format, the advantages are:
> 
> Reduce size dramatically, some result:
> 
> Size of all modules:
> 
> original: 645575
> new: 519093

[I thought I sent this message a few days ago, guess not?]

What are the compressed-size comparisons?

-Isaac



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-19  1:52 ` Isaac Dupree
@ 2009-07-19 10:03   ` Bean
  2009-07-21 20:55     ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-19 10:03 UTC (permalink / raw)
  To: The development of GRUB 2

On Sun, Jul 19, 2009 at 9:52 AM, Isaac
Dupree<ml@isaac.cedarswampstudios.org> wrote:
> Bean wrote:
>>
>> Hi,
>>
>> This patch implement a new object format, the advantages are:
>>
>> Reduce size dramatically, some result:
>>
>> Size of all modules:
>>
>> original: 645575
>> new: 519093
>
> [I thought I sent this message a few days ago, guess not?]
>
> What are the compressed-size comparisons?

Hi,

An example (reiserfs on lvm on raid)

grub-mkimage -d . -o core.img pc biosdisk reiserfs lvm raid mdraid
raid5rec raid6rec

old: 33248
new: 31939

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-19 10:03   ` Bean
@ 2009-07-21 20:55     ` Bean
  2009-07-22 11:12       ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-21 20:55 UTC (permalink / raw)
  To: The development of GRUB 2

Update:

Various bug fix
Support EFI platform,
i386-efi: both elf and pe are ok
x86_64-efi: elf ok

BTW, it's very closed to build x86_64-efi using pe64. In order to
build it, you need to download mingw-w64, and link
x86_64-w64-mingw32-gcc.exe to x86_64-gcc.exe, etc, then:

./configure CC=x86_64-gcc --with-platform=efi --target=x86_64

compile ok, grub-mkimage also generate the efi image, but it crash when loading.

The source code have been pushed to my git repository:

http://github.com/bean123/grub

lib branch
-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-21 20:55     ` Bean
@ 2009-07-22 11:12       ` Bean
  2009-07-22 11:47         ` Javier Martín
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-22 11:12 UTC (permalink / raw)
  To: The development of GRUB 2

Update:

Now fully support mingw-w64, the configure line is this:

./configure --with-platform=efi --target=x86_64-w64-mingw32

Compile ok in both XP64 and linux with mingw-w64 cross compiler, the
generated grub.efi tested all right on macbook.

Fix some wrong assumption in types.h and efi header files. For
example, grub_efi_uint_t is defined as unsigned long, but it should be
grub_uint64_t in 64-bit EFI, this problem won't show previously as
unsigned long is 64-bit in elf64 gcc, but it's 32-bit in mingw-w64
gcc.

Fix a bug in grub-symdb.

New code updated to http://github.com/bean123/grub.

On Wed, Jul 22, 2009 at 4:55 AM, Bean<bean123ch@gmail.com> wrote:
> Update:
>
> Various bug fix
> Support EFI platform,
> i386-efi: both elf and pe are ok
> x86_64-efi: elf ok
>
> BTW, it's very closed to build x86_64-efi using pe64. In order to
> build it, you need to download mingw-w64, and link
> x86_64-w64-mingw32-gcc.exe to x86_64-gcc.exe, etc, then:
>
> ./configure CC=x86_64-gcc --with-platform=efi --target=x86_64
>
> compile ok, grub-mkimage also generate the efi image, but it crash when loading.
>
> The source code have been pushed to my git repository:
>
> http://github.com/bean123/grub
>
> lib branch
> --
> Bean
>



-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-22 11:12       ` Bean
@ 2009-07-22 11:47         ` Javier Martín
  2009-07-22 13:34           ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Javier Martín @ 2009-07-22 11:47 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 989 bytes --]

El mié, 22-07-2009 a las 19:12 +0800, Bean escribió:
> Fix some wrong assumption in types.h and efi header files. For
> example, grub_efi_uint_t is defined as unsigned long, but it should be
> grub_uint64_t in 64-bit EFI, this problem won't show previously as
> unsigned long is 64-bit in elf64 gcc, but it's 32-bit in mingw-w64
> gcc.
I think you haven't corrected _all_ such assumptions. For example, in
your grub/types.h:

#if GRUB_CPU_SIZEOF_VOID_P == 8
# define GRUB_ULONG_MAX 18446744073709551615UL
# define GRUB_LONG_MAX 9223372036854775807L
# define GRUB_LONG_MIN (-9223372036854775807L - 1)
#else
# define GRUB_ULONG_MAX 4294967295UL
# define GRUB_LONG_MAX 2147483647L
# define GRUB_LONG_MIN (-2147483647L - 1)
#endif

In mingw64, sizeof(void*) = 8, but ULONG_MAX = 2^32-1.
grub/machine/types.h defines a GRUB_TARGET_SIZEOF_LONG that might be
suitable for this. Or am I mixing "target" with "host"?

-- 
-- Lazy, Oblivious, Recurrent Disaster -- Habbit

[-- Attachment #2: Esto es una parte de mensaje firmado digitalmente --]
[-- Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-22 11:47         ` Javier Martín
@ 2009-07-22 13:34           ` Bean
  2009-07-22 13:42             ` Javier Martín
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-22 13:34 UTC (permalink / raw)
  To: The development of GRUB 2

2009/7/22 Javier Martín <lordhabbit@gmail.com>:
> El mié, 22-07-2009 a las 19:12 +0800, Bean escribió:
>> Fix some wrong assumption in types.h and efi header files. For
>> example, grub_efi_uint_t is defined as unsigned long, but it should be
>> grub_uint64_t in 64-bit EFI, this problem won't show previously as
>> unsigned long is 64-bit in elf64 gcc, but it's 32-bit in mingw-w64
>> gcc.
> I think you haven't corrected _all_ such assumptions. For example, in
> your grub/types.h:
>
> #if GRUB_CPU_SIZEOF_VOID_P == 8
> # define GRUB_ULONG_MAX 18446744073709551615UL
> # define GRUB_LONG_MAX 9223372036854775807L
> # define GRUB_LONG_MIN (-9223372036854775807L - 1)
> #else
> # define GRUB_ULONG_MAX 4294967295UL
> # define GRUB_LONG_MAX 2147483647L
> # define GRUB_LONG_MIN (-2147483647L - 1)
> #endif
>
> In mingw64, sizeof(void*) = 8, but ULONG_MAX = 2^32-1.
> grub/machine/types.h defines a GRUB_TARGET_SIZEOF_LONG that might be
> suitable for this. Or am I mixing "target" with "host"?

Hi,

Oh, thanks for the note. In fact, we can use  GRUB_CPU_SIZEOF_LONG,
its value is GRUB_TARGET_SIZEOF_LONG when building target, and
SIZEOF_LONG when building utilities. For example, I now define
grub_uint64_t as:

#if GRUB_CPU_SIZEOF_LONG == 8
typedef unsigned long		grub_uint64_t;
#else
typedef unsigned long long	grub_uint64_t;
#endif

The previous definition is not correct:

#if GRUB_CPU_SIZEOF_VOID_P == 8
typedef unsigned long		grub_uint64_t;
#else
typedef unsigned long long	grub_uint64_t;
#endif

As GRUB_CPU_SIZEOF_LONG  doesn't necessary equal to
GRUB_CPU_SIZEOF_VOID_P. (It actually avoid this by generating an
#errror message when GRUB_CPU_SIZEOF_VOID_P != GRUB_CPU_SIZEOF_LONG).

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-22 13:34           ` Bean
@ 2009-07-22 13:42             ` Javier Martín
  2009-07-22 14:52               ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Javier Martín @ 2009-07-22 13:42 UTC (permalink / raw)
  To: The development of GRUB 2

[-- Attachment #1: Type: text/plain, Size: 2028 bytes --]

El mié, 22-07-2009 a las 21:34 +0800, Bean escribió:
> 2009/7/22 Javier Martín <lordhabbit@gmail.com>:
> > El mié, 22-07-2009 a las 19:12 +0800, Bean escribió:
> >> Fix some wrong assumption in types.h and efi header files. For
> >> example, grub_efi_uint_t is defined as unsigned long, but it should be
> >> grub_uint64_t in 64-bit EFI, this problem won't show previously as
> >> unsigned long is 64-bit in elf64 gcc, but it's 32-bit in mingw-w64
> >> gcc.
> > I think you haven't corrected _all_ such assumptions. For example, in
> > your grub/types.h:
> >
> > #if GRUB_CPU_SIZEOF_VOID_P == 8
> > # define GRUB_ULONG_MAX 18446744073709551615UL
> > # define GRUB_LONG_MAX 9223372036854775807L
> > # define GRUB_LONG_MIN (-9223372036854775807L - 1)
> > #else
> > # define GRUB_ULONG_MAX 4294967295UL
> > # define GRUB_LONG_MAX 2147483647L
> > # define GRUB_LONG_MIN (-2147483647L - 1)
> > #endif
> >
> > In mingw64, sizeof(void*) = 8, but ULONG_MAX = 2^32-1.
> > grub/machine/types.h defines a GRUB_TARGET_SIZEOF_LONG that might be
> > suitable for this. Or am I mixing "target" with "host"?
> 
> Hi,
> 
> Oh, thanks for the note. In fact, we can use  GRUB_CPU_SIZEOF_LONG,
> its value is GRUB_TARGET_SIZEOF_LONG when building target, and
> SIZEOF_LONG when building utilities.
Wonderful idea. I'm researching this for a possible implementation of a
GRUB equivalent to the C99 fixed-length integers print specifiers like
PRIx64. It would most likely go into <grub/types.h>, so I'm paying
special attention to anyone working on that file.

> As GRUB_CPU_SIZEOF_LONG  doesn't necessary equal to
> GRUB_CPU_SIZEOF_VOID_P. (It actually avoid this by generating an
> #errror message when GRUB_CPU_SIZEOF_VOID_P != GRUB_CPU_SIZEOF_LONG).
I think you lost me here. This check is in the trunk GRUB, but not in
your "lib" branch, and actually it would make it impossible to build on
mingw64, where void* is 64-bit and long is 32-bit.

-- 
-- Lazy, Oblivious, Recurrent Disaster -- Habbit

[-- Attachment #2: Esto es una parte de mensaje firmado digitalmente --]
[-- Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-22 13:42             ` Javier Martín
@ 2009-07-22 14:52               ` Bean
  2009-07-25 19:33                 ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-22 14:52 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

>> Oh, thanks for the note. In fact, we can use  GRUB_CPU_SIZEOF_LONG,
>> its value is GRUB_TARGET_SIZEOF_LONG when building target, and
>> SIZEOF_LONG when building utilities.
> Wonderful idea. I'm researching this for a possible implementation of a
> GRUB equivalent to the C99 fixed-length integers print specifiers like
> PRIx64. It would most likely go into <grub/types.h>, so I'm paying
> special attention to anyone working on that file.

Yeah, that would be very useful, most warning message from mingw64 is
caused by grub_printf format.

>
>> As GRUB_CPU_SIZEOF_LONG  doesn't necessary equal to
>> GRUB_CPU_SIZEOF_VOID_P. (It actually avoid this by generating an
>> #errror message when GRUB_CPU_SIZEOF_VOID_P != GRUB_CPU_SIZEOF_LONG).
> I think you lost me here. This check is in the trunk GRUB, but not in
> your "lib" branch, and actually it would make it impossible to build on
> mingw64, where void* is 64-bit and long is 32-bit.

The check is in svn trunk so that you can't compile at all, I remove
it in my lib branch so that it can be compiled in mingw64.

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-22 14:52               ` Bean
@ 2009-07-25 19:33                 ` Bean
  2009-07-27 18:41                   ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-25 19:33 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

New tool grub-mkrawimage, used to generate img file from object files
directly, no need for ld and objcopy anymore. It inject the
grub_bss_start and grub_bss_end symbol, so no need to check for them
in configure.ac.

Remove the memcpy alias in kern/misc.c, instead, it map memcpy to
grub_memmove when generating the module file.

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-25 19:33                 ` Bean
@ 2009-07-27 18:41                   ` Bean
  2009-07-28 11:00                     ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-27 18:41 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

Now the attributes are stored inside mod file, for example, "fs:",
"command:ls", etc. grub-symdb uses it to generate the list files
automatically (fs.lst, partmap.lst, parttool.lst, command.lst,
handler.lst). Don't use sed to scan source file, and eliminate the
fs-*.lst, partmap-*.lst, parttool-*.lst, command-*.lst and
handler-*.lst.

grub-objdump can show the mod attributes, for example:

# grub-objdump gfxterm.mod
filename: gfxterm.mod
mod name: gfxterm
mod deps: bitmap font video
mod attr: handler:terminal_output.gfxterm command:background_image
init func: 0xcad
fini func: 0xce6

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-27 18:41                   ` Bean
@ 2009-07-28 11:00                     ` Bean
  2009-07-30 14:51                       ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-28 11:00 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

Now global symbol must be defined using GRUB_EXPORT macro, otherwise
it'd be removed from the module symbol list. I also add GRUB_EXPORT to
modules in i386-pc platform.

Some reason to define global symbols explicitly:

For modules that consists of multiple files, some external function is
used to communicate with each other, they should not be exported, for
example, sh.mod and lua.mod all have many function that's not static.
It'd be nice to distinguish between function we want to export and
those used by other files in the same module.

For normal module, exported function is not common, for those that
consist of functions mainly, we can move it to the library, which
export all external function so that we don't need GRUB_EXPORT anyway.

The export symbol is not cheap, by exporting only the symbols we need,
we can save some space.

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-28 11:00                     ` Bean
@ 2009-07-30 14:51                       ` Bean
  2009-07-31 18:10                         ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-30 14:51 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

Now change kernel.img to kernel.mod, and export symbol using
GRUB_EXPORT instead of EXPORT_FUNC and EXPORT_VAR. grub-mkimage would
link all modules files to generate core.img. The symbol list is
generated by grub-mkimage, no need for symlist.c anymore.

Here are some test results using the latest lib branch and
corresponding unpatched version (svn r2435). The test uses mingw in a
windows host:

Compile time:
svn:
real    3m52.625s
user    1m9.647s
sys     2m8.904s

lib:
real    1m13.282s
user    0m11.365s
sys     0m22.748s

Files generated in the compile process: (not counting include/machine
and include/cpu)
svn:
Files 2599 (24308485 bytes)

lib:
Files 1007 (20610273 bytes)

Size of all modules:
svn:
635991

lib:
549199

Size of core.img using modules: pc biosdisk reiserfs raid raid6rec mdraid lvm
svn:
33035

lib:
29822

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-30 14:51                       ` Bean
@ 2009-07-31 18:10                         ` Bean
  2009-08-05 17:59                           ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-07-31 18:10 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

Fix for i386-efi and x86_64-efi platform, now grub-mkimage works just
like i386-pc, it linked the modules to form the runtime image instead
of embedding them.

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-07-31 18:10                         ` Bean
@ 2009-08-05 17:59                           ` Bean
  2009-08-07 17:18                             ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-08-05 17:59 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

Support mach-o object file, now you can compile grub2 in OSX using gcc
4.0 from Xcode. No other tool is needed except gcc and as.

Tested ok with i386-pc and i386-efi.

BTW, here are a list of issues of apple gcc 4.0:

Local jumps with more that one digit (such as 10f, 10b) doesn't work,
this affect the lzma decoder. I change it with symbol label like
lzma_10.

Relocation in code16 segment is trouble, apple cc generate wrong
information sometimes. So use local symbol (L_xx) in 16-bit code.

Line
asm volatile ("jmp *%2" : : "b" (0), "S" (real_mode_mem), "g"
(params->code32_start));

in loader/i386/linux.c generate an error message, i comment it out so
that compilation can continue, so the linux loader would be broken for
the time being.

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-08-05 17:59                           ` Bean
@ 2009-08-07 17:18                             ` Bean
  2009-08-09 19:10                               ` Bean
  0 siblings, 1 reply; 25+ messages in thread
From: Bean @ 2009-08-07 17:18 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

Support 64-bit mach-o object files, now you can build i386-pc,
i386-efi and x86_64-efi in OSX.

Use PIC in x86_64-efi. Relocation R_X86_64_32S would cause problem in
machine with 2G or more memory, the only way to get rid of it is to
use large memory model, or use PIC. Large model is only available in
gcc-4.3 or later, and the generated image is quite large as all
address is 8 bytes long. PIC is always enabled in mingw-w64 and
apple's gcc, it just need a little work to support PIC in elf
platform. Now all system uses PIC instead of large memory model.

As with this version, it's possible to build i386-pc, i386-efi and
x86_64-efi in Linux/OSX/Windows, using the native build tool, no cross
compiler. Only gcc and as is needed to generated object file *.o from
source code, linker (ld) and converter (objcopy, objconv, grub-pe2elf,
grub-macho2img) are not needed as the conversion is handled by
grub-mkrawimage and grub-mkmod.

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] New object format
  2009-08-07 17:18                             ` Bean
@ 2009-08-09 19:10                               ` Bean
  0 siblings, 0 replies; 25+ messages in thread
From: Bean @ 2009-08-09 19:10 UTC (permalink / raw)
  To: The development of GRUB 2

Hi,

Update:

Add support for i386-ieee1275, i386-qemu and i386-coreboot, now the
x86 platform is complete, you can build either one of it in
Linux/OSX/Windows:

Note: grub-mkfont doesn't compile in OSX, so you should add
--disable-grub-mkfont, grub-emu could also cause problem in
OSX/Windows, you can use --disable-grub-emu to disable it.

Here are some information on how to compile them:

i386-pc: (Linux/OSX/Windows)
./configure --with-platform=pc
make
.
i386-efi: (Linux/OSX/Windows)
./configure --with-platform=efi
make

i386-ieee1275 (Linux/OSX/Windows)
./configure --with-platform=ieee1275
make

i386-qemu (Linux/OSX/Windows)
./configure --with-platform=qemu
make

i386-coreboot (Linux/OSX/Windows)
./configure --with-platform=coreboot
make

x86_64-efi
Linux: (needs gcc-multilib on 32-bit system)
./configure --with-platform=efi --target=x86_64
make

OSX:
./configure --with-platform=efi --target=x86_64
make

Windows: (needs mingw-w64)
./configure --with-platform=efi --target=x86_64-w64-mingw32
make

-- 
Bean



^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2009-08-09 19:10 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-07-14 15:49 [PATCH] New object format Bean
2009-07-14 17:57 ` please stop this Robert Millan
2009-07-14 18:09   ` Pavel Roskin
2009-07-14 18:17     ` Bean
2009-07-16 15:23       ` Robert Millan
2009-07-16 16:10         ` Vladimir 'phcoder' Serbinenko
2009-07-18 18:15           ` Robert Millan
2009-07-18 18:53             ` Vladimir 'phcoder' Serbinenko
2009-07-14 21:02 ` [PATCH] New object format Bean
2009-07-19  1:52 ` Isaac Dupree
2009-07-19 10:03   ` Bean
2009-07-21 20:55     ` Bean
2009-07-22 11:12       ` Bean
2009-07-22 11:47         ` Javier Martín
2009-07-22 13:34           ` Bean
2009-07-22 13:42             ` Javier Martín
2009-07-22 14:52               ` Bean
2009-07-25 19:33                 ` Bean
2009-07-27 18:41                   ` Bean
2009-07-28 11:00                     ` Bean
2009-07-30 14:51                       ` Bean
2009-07-31 18:10                         ` Bean
2009-08-05 17:59                           ` Bean
2009-08-07 17:18                             ` Bean
2009-08-09 19:10                               ` Bean

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.