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

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.