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 += address@hidden #{mod_obj} #{mod_src} #{pre_obj} #{objs_str} #{undsym}
-ifneq ($(#{prefix}_EXPORTS),no)
-CLEANFILES += #{defsym}
-DEFSYMFILES += #{defsym}
-endif
+ "CLEANFILES += address@hidden #{objs_str}
MOSTLYCLEANFILES += #{deps_str}
-UNDSYMFILES += #{undsym}
-
-ifneq ($(TARGET_APPLE_CC),1)
address@hidden: #{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
address@hidden: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF)
- -rm -f $@
- -rm -f address@hidden
- $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o address@hidden #{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 address@hidden $@
- -rm -f address@hidden
-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
address@hidden: $(#{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 += address@hidden
" + 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 .
+ */
+
+#ifndef GRUB_OBJ_H
+#define GRUB_OBJ_H 1
+
+#include
+
+#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
#include
+#include
#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 .
+ */
+
+#ifndef GRUB_UTIL_OBJ_HEADER
+#define GRUB_UTIL_OBJ_HEADER 1
+
+#include
+#include
+#include
+#include
+
+#include
+
+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
-#include
#include
+#include
#include
#include
#include
@@ -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 .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#define _GNU_SOURCE 1
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+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;
}