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; }