diff options
author | Jon Masters <jcm@jonmasters.org> | 2009-05-19 04:50:08 -0400 |
---|---|---|
committer | Jon Masters <jcm@jonmasters.org> | 2009-05-19 04:50:08 -0400 |
commit | aa6fe1585412d5ad603fc81b7075dffb93a8b8f2 (patch) | |
tree | 5df1a6e2aa8aa42518b1cc02d75bd1b4e0662f86 | |
parent | 243fc689453662f4573f2601a64c5c7f9312906d (diff) | |
parent | 686703ef1f9f6b123ef67360a008d1ffe9bdc074 (diff) | |
download | module-init-tools-aa6fe1585412d5ad603fc81b7075dffb93a8b8f2.tar.gz |
Merge branch 'elf_cleanup' of ../module_init_tools_andr345
-rw-r--r-- | Makefile.am | 12 | ||||
-rw-r--r-- | depmod.c | 80 | ||||
-rw-r--r-- | depmod.h | 39 | ||||
-rw-r--r-- | elf_core.c | 43 | ||||
-rw-r--r-- | elfops.c | 57 | ||||
-rw-r--r-- | elfops.h | 78 | ||||
-rw-r--r-- | elfops_core.c | 303 | ||||
-rw-r--r-- | modinfo.c | 1 | ||||
-rw-r--r-- | modprobe.c | 1 | ||||
-rw-r--r-- | moduleops.c | 25 | ||||
-rw-r--r-- | moduleops.h | 28 | ||||
-rw-r--r-- | moduleops_core.c | 241 | ||||
-rw-r--r-- | tables.c | 86 | ||||
-rw-r--r-- | util.c | 91 | ||||
-rw-r--r-- | util.h | 17 |
15 files changed, 618 insertions, 484 deletions
diff --git a/Makefile.am b/Makefile.am index 424aa8f..be697dc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,8 +4,8 @@ insmod_SOURCES = insmod.c testing.h lsmod_SOURCES = lsmod.c testing.h modprobe_SOURCES = modprobe.c zlibsupport.c zlibsupport.h testing.h rmmod_SOURCES = rmmod.c testing.h -depmod_SOURCES = depmod.c zlibsupport.c moduleops.c tables.c \ - zlibsupport.h moduleops.h tables.h testing.h +depmod_SOURCES = depmod.c zlibsupport.c tables.c \ + zlibsupport.h tables.h testing.h modinfo_SOURCES = modinfo.c zlibsupport.c zlibsupport.h testing.h modindex_SOURCES = modindex.c zlibsupport.c zlibsupport.h testing.h @@ -19,14 +19,14 @@ EXTRA_lsmod_SOURCES = EXTRA_modprobe_SOURCES = EXTRA_rmmod_SOURCES = EXTRA_insmod_static_SOURCES = -EXTRA_depmod_SOURCES = moduleops_core.c +EXTRA_depmod_SOURCES = EXTRA_modinfo_SOURCES = -libmodtools_a_SOURCES = util.c logging.c index.c config_filter.c \ - util.h depmod.h logging.h index.h list.h config_filter.h +libmodtools_a_SOURCES = util.c logging.c index.c config_filter.c elfops.c \ + util.h depmod.h logging.h index.h list.h config_filter.h elfops.h libmodtools_a_CFLAGS = -ffunction-sections -EXTRA_libmodtools_a_SOURCES = elf_core.c +EXTRA_libmodtools_a_SOURCES = elf_core.c elfops_core.c insmod_LDADD = $(LDADD) libmodtools.a lsmod_LDADD = $(LDADD) libmodtools.a @@ -26,7 +26,7 @@ #include "depmod.h" #include "logging.h" #include "index.h" -#include "moduleops.h" +#include "elfops.h" #include "tables.h" #include "config_filter.h" @@ -264,6 +264,7 @@ static int ends_in(const char *name, const char *ext) static struct module *grab_module(const char *dirname, const char *filename) { struct module *new; + struct elf_file *file; new = NOFAIL(malloc(sizeof(*new) + strlen(dirname?:"") + 1 + strlen(filename) + 1)); @@ -276,19 +277,21 @@ static struct module *grab_module(const char *dirname, const char *filename) INIT_LIST_HEAD(&new->dep_list); new->order = INDEX_PRIORITY_MIN; - new->data = grab_file(new->pathname, &new->len); - if (!new->data) { + file = &new->file; + + file->data = grab_file(new->pathname, &file->len); + if (!file->data) { warn("Can't read module %s: %s\n", new->pathname, strerror(errno)); goto fail_data; } - switch (elf_ident(new->data, new->len, &new->conv)) { + switch (elf_ident(file->data, file->len, &file->conv)) { case ELFCLASS32: - new->ops = &mod_ops32; + file->ops = &mod_ops32; break; case ELFCLASS64: - new->ops = &mod_ops64; + file->ops = &mod_ops64; break; case -ENOEXEC: warn("Module %s is not an elf object\n", new->pathname); @@ -303,7 +306,7 @@ static struct module *grab_module(const char *dirname, const char *filename) return new; fail: - release_file(new->data, new->len); + release_file(file->data, new->file.len); fail_data: free(new); return NULL; @@ -667,17 +670,62 @@ static struct module *sort_modules(const char *dirname, struct module *list) return tlist; } +/* Calculate the dependencies for this module */ +static void calculate_deps(struct module *module) +{ + unsigned int i; + struct string_table *symnames; + struct string_table *symtypes; + struct elf_file *file; + + module->num_deps = 0; + module->deps = NULL; + file = &module->file; + + symnames = file->ops->load_dep_syms(module->pathname, file, &symtypes); + if (!symnames || !symtypes) + return; + + for (i = 0; i < symnames->cnt; i++) { + const char *name; + struct module *owner; + int weak; + + name = symnames->str[i]; + weak = (*(symtypes->str[i]) == 'W'); + owner = find_symbol(name, module->pathname, weak); + if (owner) { + info("%s needs \"%s\": %s\n", + module->pathname, name, + owner->pathname); + add_dep(module, owner); + } + } + + free(symnames); + free(symtypes); +} + static struct module *parse_modules(struct module *list) { struct module *i; + struct elf_file *file; + struct string_table *syms; + int j; for (i = list; i; i = i->next) { - i->ops->load_symbols(i); - i->ops->fetch_tables(i); + file = &i->file; + syms = file->ops->load_symbols(file); + if (syms) { + for (j = 0; j < syms->cnt; j++) + add_symbol(syms->str[j], i); + free(syms); + } + file->ops->fetch_tables(file, &i->tables); } for (i = list; i; i = i->next) - i->ops->calculate_deps(i); + calculate_deps(i); /* Strip out modules with dependency loops. */ again: @@ -747,6 +795,7 @@ static void output_symbols_bin(struct module *unused, FILE *out, char *dirname) static void output_aliases(struct module *modules, FILE *out, char *dirname) { struct module *i; + struct elf_file *file; const char *p; unsigned long size; @@ -754,16 +803,17 @@ static void output_aliases(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { char modname[strlen(i->pathname)+1]; + file = &i->file; filename2modname(modname, i->pathname); /* Grab from old-style .modalias section. */ - for (p = i->ops->get_aliases(i, &size); + for (p = file->ops->get_aliases(file, &size); p; p = next_string(p, &size)) fprintf(out, "alias %s %s\n", p, modname); /* Grab form new-style .modinfo section. */ - for (p = i->ops->get_modinfo(i, &size); + for (p = file->ops->get_modinfo(file, &size); p; p = next_string(p, &size)) { if (strstarts(p, "alias=")) @@ -776,6 +826,7 @@ static void output_aliases(struct module *modules, FILE *out, char *dirname) static void output_aliases_bin(struct module *modules, FILE *out, char *dirname) { struct module *i; + struct elf_file *file; const char *p; char *alias; unsigned long size; @@ -787,10 +838,11 @@ static void output_aliases_bin(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { char modname[strlen(i->pathname)+1]; + file = &i->file; filename2modname(modname, i->pathname); /* Grab from old-style .modalias section. */ - for (p = i->ops->get_aliases(i, &size); + for (p = file->ops->get_aliases(file, &size); p; p = next_string(p, &size)) { alias = NOFAIL(strdup(p)); @@ -803,7 +855,7 @@ static void output_aliases_bin(struct module *modules, FILE *out, char *dirname) } /* Grab from new-style .modinfo section. */ - for (p = i->ops->get_modinfo(i, &size); + for (p = file->ops->get_modinfo(file, &size); p; p = next_string(p, &size)) { if (strstarts(p, "alias=")) { @@ -1,25 +1,15 @@ #ifndef MODINITTOOLS_DEPMOD_H #define MODINITTOOLS_DEPMOD_H #include "list.h" +#include "elfops.h" struct module; -/* Functions provided by depmod.c */ -void add_symbol(const char *name, struct module *owner); -struct module *find_symbol(const char *name, const char *modname, int weak); -void add_dep(struct module *mod, struct module *depends_on); - struct module { /* Next module in list of all modules */ struct module *next; - /* 64 or 32 bit? */ - struct module_ops *ops; - - /* Convert endian? */ - int conv; - /* Dependencies: filled in by ops->calculate_deps() */ unsigned int num_deps; struct module **deps; @@ -31,30 +21,9 @@ struct module unsigned int order; /* Tables extracted from module by ops->fetch_tables(). */ - unsigned int pci_size; - void *pci_table; - unsigned int usb_size; - void *usb_table; - unsigned int ieee1394_size; - void *ieee1394_table; - unsigned int ccw_size; - void *ccw_table; - unsigned int pnp_size; - void *pnp_table; - unsigned int pnp_card_size; - unsigned int pnp_card_offset; - void *pnp_card_table; - unsigned int input_size; - void *input_table; - unsigned int input_table_size; - unsigned int serio_size; - void *serio_table; - unsigned int of_size; - void *of_table; - - /* File contents and length. */ - void *data; - unsigned long len; + struct module_tables tables; + + struct elf_file file; char *basename; /* points into pathname */ char pathname[0]; diff --git a/elf_core.c b/elf_core.c deleted file mode 100644 index 1525c36..0000000 --- a/elf_core.c +++ /dev/null @@ -1,43 +0,0 @@ -void *PERBIT(get_section)(void *file, - unsigned long fsize, - const char *secname, - unsigned long *secsize, - int conv) -{ - ElfPERBIT(Ehdr) *hdr; - ElfPERBIT(Shdr) *sechdrs; - ElfPERBIT(Off) e_shoff; - ElfPERBIT(Half) e_shnum, e_shstrndx; - - const char *secnames; - unsigned int i; - - if (fsize > 0 && fsize < sizeof(*hdr)) - return NULL; - - hdr = file; - e_shoff = END(hdr->e_shoff, conv); - e_shnum = END(hdr->e_shnum, conv); - e_shstrndx = END(hdr->e_shstrndx, conv); - - if (fsize > 0 && fsize < e_shoff + e_shnum * sizeof(sechdrs[0])) - return NULL; - - sechdrs = file + e_shoff; - - if (fsize > 0 && fsize < END(sechdrs[e_shstrndx].sh_offset, conv)) - return NULL; - - /* Find section by name, return pointer and size. */ - - secnames = file + END(sechdrs[e_shstrndx].sh_offset, conv); - for (i = 1; i < e_shnum; i++) { - if (streq(secnames + END(sechdrs[i].sh_name, conv), secname)) { - *secsize = END(sechdrs[i].sh_size, conv); - return file + END(sechdrs[i].sh_offset, conv); - } - } - *secsize = 0; - return NULL; -} - diff --git a/elfops.c b/elfops.c new file mode 100644 index 0000000..9ae77ef --- /dev/null +++ b/elfops.c @@ -0,0 +1,57 @@ +/* The nasty work of reading 32 and 64-bit modules is in here. */ +#include <elf.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "depmod.h" +#include "util.h" +#include "logging.h" +#include "elfops.h" +#include "tables.h" + +/* Symbol types, returned by load_dep_syms */ +static const char *weak_sym = "W"; +static const char *undef_sym = "U"; + +#define ELF32BIT +#include "elfops_core.c" +#undef ELF32BIT + +#define ELF64BIT +#include "elfops_core.c" +#undef ELF64BIT + +/* + * Check ELF file header. + */ +int elf_ident(void *file, unsigned long fsize, int *conv) +{ + /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */ + unsigned char *ident = file; + + if (fsize < EI_CLASS || memcmp(file, ELFMAG, SELFMAG) != 0) + return -ENOEXEC; /* Not an ELF object */ + if (ident[EI_DATA] == 0 || ident[EI_DATA] > 2) + return -EINVAL; /* Unknown endianness */ + + if (conv != NULL) + *conv = native_endianness() != ident[EI_DATA]; + return ident[EI_CLASS]; +} + +void *get_section(void *file, unsigned long filesize, + const char *secname, unsigned long *secsize) +{ + int conv; + + switch (elf_ident(file, filesize, &conv)) { + case ELFCLASS32: + return get_section32(file, filesize, secname, secsize, conv); + case ELFCLASS64: + return get_section64(file, filesize, secname, secsize, conv); + default: + return NULL; + } +} diff --git a/elfops.h b/elfops.h new file mode 100644 index 0000000..dc10c3d --- /dev/null +++ b/elfops.h @@ -0,0 +1,78 @@ +#ifndef MODINITTOOLS_MODULEOPS_H +#define MODINITTOOLS_MODULEOPS_H +#include <stdio.h> + +/* All the icky stuff to do with manipulating 64 and 32-bit modules + belongs here. */ +struct kernel_symbol32 { + char value[4]; + char name[64 - 4]; +}; + +struct kernel_symbol64 { + char value[8]; + char name[64 - 8]; +}; + +struct elf_file +{ + /* File operations */ + struct module_ops *ops; + + /* Convert endian? */ + int conv; + + /* File contents and length. */ + void *data; + unsigned long len; +}; + +/* Tables extracted from module by ops->fetch_tables(). */ +struct module_tables +{ + unsigned int pci_size; + void *pci_table; + unsigned int usb_size; + void *usb_table; + unsigned int ieee1394_size; + void *ieee1394_table; + unsigned int ccw_size; + void *ccw_table; + unsigned int pnp_size; + void *pnp_table; + unsigned int pnp_card_size; + unsigned int pnp_card_offset; + void *pnp_card_table; + unsigned int input_size; + void *input_table; + unsigned int input_table_size; + unsigned int serio_size; + void *serio_table; + unsigned int of_size; + void *of_table; +}; + +struct module_ops +{ + struct string_table *(*load_strings)(struct elf_file *module, + const char *secname, struct string_table *tbl); + struct string_table *(*load_symbols)(struct elf_file *module); + struct string_table *(*load_dep_syms)(const char *pathname, + struct elf_file *module, struct string_table **types); + void (*fetch_tables)(struct elf_file *module, + struct module_tables *tables); + char *(*get_aliases)(struct elf_file *module, unsigned long *size); + char *(*get_modinfo)(struct elf_file *module, unsigned long *size); +}; + +extern struct module_ops mod_ops32, mod_ops64; + +int elf_ident(void *file, unsigned long fsize, int *conv); +void *get_section(void *file, unsigned long filesize, + const char *secname, unsigned long *secsize); +void *get_section32(void *file, unsigned long filesize, + const char *secname, unsigned long *secsize, int conv); +void *get_section64(void *file, unsigned long filesize, + const char *secname, unsigned long *secsize, int conv); + +#endif /* MODINITTOOLS_MODULEOPS_H */ diff --git a/elfops_core.c b/elfops_core.c new file mode 100644 index 0000000..8b7f4de --- /dev/null +++ b/elfops_core.c @@ -0,0 +1,303 @@ +#if defined(ELF32BIT) + +#define PERBIT(x) x##32 +#define ElfPERBIT(x) Elf32_##x +#define ELFPERBIT(x) ELF32_##x + +#elif defined(ELF64BIT) + +#define PERBIT(x) x##64 +#define ElfPERBIT(x) Elf64_##x +#define ELFPERBIT(x) ELF64_##x + +#else +# error "Undefined ELF word length" +#endif + +void *PERBIT(get_section)(void *file, + unsigned long fsize, + const char *secname, + unsigned long *secsize, + int conv) +{ + ElfPERBIT(Ehdr) *hdr; + ElfPERBIT(Shdr) *sechdrs; + ElfPERBIT(Off) e_shoff; + ElfPERBIT(Half) e_shnum, e_shstrndx; + + const char *secnames; + unsigned int i; + + if (fsize > 0 && fsize < sizeof(*hdr)) + return NULL; + + hdr = file; + e_shoff = END(hdr->e_shoff, conv); + e_shnum = END(hdr->e_shnum, conv); + e_shstrndx = END(hdr->e_shstrndx, conv); + + if (fsize > 0 && fsize < e_shoff + e_shnum * sizeof(sechdrs[0])) + return NULL; + + sechdrs = file + e_shoff; + + if (fsize > 0 && fsize < END(sechdrs[e_shstrndx].sh_offset, conv)) + return NULL; + + /* Find section by name, return pointer and size. */ + + secnames = file + END(sechdrs[e_shstrndx].sh_offset, conv); + for (i = 1; i < e_shnum; i++) { + if (streq(secnames + END(sechdrs[i].sh_name, conv), secname)) { + *secsize = END(sechdrs[i].sh_size, conv); + return file + END(sechdrs[i].sh_offset, conv); + } + } + *secsize = 0; + return NULL; +} + +/* Load the given section: NULL on error. */ +static void *PERBIT(load_section)(struct elf_file *module, + const char *secname, + unsigned long *secsize) +{ + return PERBIT(get_section)(module->data, 0, secname, secsize, module->conv); +} + +static struct string_table *PERBIT(load_strings)(struct elf_file *module, + const char *secname, + struct string_table *tbl) +{ + unsigned long size; + const char *strings; + + strings = PERBIT(load_section)(module, secname, &size); + if (strings) { + /* Skip any zero padding. */ + while (!strings[0]) { + strings++; + if (size-- <= 1) + return tbl; + } + for (; strings; strings = next_string(strings, &size)) + tbl = NOFAIL(strtbl_add(strings, tbl)); + } + return tbl; +} + +static struct string_table *PERBIT(load_symbols)(struct elf_file *module) +{ + struct PERBIT(kernel_symbol) *ksyms; + struct string_table *symtbl; + unsigned long i, size; + + symtbl = NULL; + + /* New-style: strings are in this section. */ + symtbl = PERBIT(load_strings)(module, "__ksymtab_strings", symtbl); + if (symtbl) { + /* GPL symbols too */ + return PERBIT(load_strings)(module, "__ksymtab_strings_gpl", + symtbl); + } + + /* Old-style. */ + ksyms = PERBIT(load_section)(module, "__ksymtab", &size); + for (i = 0; i < size / sizeof(struct PERBIT(kernel_symbol)); i++) + symtbl = NOFAIL(strtbl_add(ksyms[i].name, symtbl)); + ksyms = PERBIT(load_section)(module, "__gpl_ksymtab", &size); + for (i = 0; i < size / sizeof(struct PERBIT(kernel_symbol)); i++) + symtbl = NOFAIL(strtbl_add(ksyms[i].name, symtbl)); + + return symtbl; +} + +static char *PERBIT(get_aliases)(struct elf_file *module, unsigned long *size) +{ + return PERBIT(load_section)(module, ".modalias", size); +} + +static char *PERBIT(get_modinfo)(struct elf_file *module, unsigned long *size) +{ + return PERBIT(load_section)(module, ".modinfo", size); +} + +#ifndef STT_REGISTER +#define STT_REGISTER 13 /* Global register reserved to app. */ +#endif + +static struct string_table *PERBIT(load_dep_syms)(const char *pathname, + struct elf_file *module, + struct string_table **types) +{ + unsigned int i; + unsigned long size; + char *strings; + ElfPERBIT(Sym) *syms; + ElfPERBIT(Ehdr) *hdr; + int handle_register_symbols; + struct string_table *names; + int conv; + + names = NULL; + *types = NULL; + + strings = PERBIT(load_section)(module, ".strtab", &size); + syms = PERBIT(load_section)(module, ".symtab", &size); + + if (!strings || !syms) { + warn("Couldn't find symtab and strtab in module %s\n", + pathname); + return NULL; + } + + hdr = module->data; + conv = module->conv; + + handle_register_symbols = + (END(hdr->e_machine, conv) == EM_SPARC || + END(hdr->e_machine, conv) == EM_SPARCV9); + + for (i = 1; i < size / sizeof(syms[0]); i++) { + if (END(syms[i].st_shndx, conv) == SHN_UNDEF) { + /* Look for symbol */ + const char *name; + int weak; + + name = strings + END(syms[i].st_name, conv); + + /* Not really undefined: sparc gcc 3.3 creates + U references when you have global asm + variables, to avoid anyone else misusing + them. */ + if (handle_register_symbols + && (ELFPERBIT(ST_TYPE)(END(syms[i].st_info, conv)) + == STT_REGISTER)) + continue; + + weak = (ELFPERBIT(ST_BIND)(END(syms[i].st_info, conv)) + == STB_WEAK); + names = strtbl_add(name, names); + *types = strtbl_add(weak ? weak_sym : undef_sym, *types); + } + } + return names; +} + +static void *PERBIT(deref_sym)(ElfPERBIT(Ehdr) *hdr, + ElfPERBIT(Shdr) *sechdrs, + ElfPERBIT(Sym) *sym, + unsigned int *secsize, + int conv) +{ + /* In BSS? Happens for empty device tables on + * recent GCC versions. */ + if (END(sechdrs[END(sym->st_shndx, conv)].sh_type,conv) == SHT_NOBITS) + return NULL; + + if (secsize) + *secsize = END(sym->st_size, conv); + return (void *)hdr + + END(sechdrs[END(sym->st_shndx, conv)].sh_offset, conv) + + END(sym->st_value, conv); +} + +/* FIXME: Check size, unless we end up using aliases anyway --RR */ +static void PERBIT(fetch_tables)(struct elf_file *module, + struct module_tables *tables) +{ + unsigned int i; + unsigned long size; + char *strings; + ElfPERBIT(Ehdr) *hdr; + ElfPERBIT(Sym) *syms; + ElfPERBIT(Shdr) *sechdrs; + int conv; + + hdr = module->data; + conv = module->conv; + + sechdrs = (void *)hdr + END(hdr->e_shoff, conv); + strings = PERBIT(load_section)(module, ".strtab", &size); + syms = PERBIT(load_section)(module, ".symtab", &size); + + /* Don't warn again: we already have above */ + if (!strings || !syms) + return; + + tables->pci_table = NULL; + tables->usb_table = NULL; + tables->ccw_table = NULL; + tables->ieee1394_table = NULL; + tables->pnp_table = NULL; + tables->pnp_card_table = NULL; + tables->input_table = NULL; + tables->serio_table = NULL; + tables->of_table = NULL; + + for (i = 0; i < size / sizeof(syms[0]); i++) { + char *name = strings + END(syms[i].st_name, conv); + + if (!tables->pci_table && streq(name, "__mod_pci_device_table")) { + tables->pci_size = PERBIT(PCI_DEVICE_SIZE); + tables->pci_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + } + else if (!tables->usb_table && streq(name, "__mod_usb_device_table")) { + tables->usb_size = PERBIT(USB_DEVICE_SIZE); + tables->usb_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + } + else if (!tables->ccw_table && streq(name, "__mod_ccw_device_table")) { + tables->ccw_size = PERBIT(CCW_DEVICE_SIZE); + tables->ccw_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + } + else if (!tables->ieee1394_table && streq(name, "__mod_ieee1394_device_table")) { + tables->ieee1394_size = PERBIT(IEEE1394_DEVICE_SIZE); + tables->ieee1394_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + } + else if (!tables->pnp_table && streq(name, "__mod_pnp_device_table")) { + tables->pnp_size = PERBIT(PNP_DEVICE_SIZE); + tables->pnp_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + } + else if (!tables->pnp_card_table && streq(name, "__mod_pnp_card_device_table")) { + tables->pnp_card_size = PERBIT(PNP_CARD_DEVICE_SIZE); + tables->pnp_card_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + tables->pnp_card_offset = PERBIT(PNP_CARD_DEVICE_OFFSET); + } + else if (!tables->input_table && streq(name, "__mod_input_device_table")) { + tables->input_size = PERBIT(INPUT_DEVICE_SIZE); + tables->input_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + &tables->input_table_size, + conv); + } + else if (!tables->serio_table && streq(name, "__mod_serio_device_table")) { + tables->serio_size = PERBIT(SERIO_DEVICE_SIZE); + tables->serio_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + } + else if (!tables->of_table && streq(name, "__mod_of_device_table")) { + tables->of_size = PERBIT(OF_DEVICE_SIZE); + tables->of_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], + NULL, conv); + } + } +} + +struct module_ops PERBIT(mod_ops) = { + .load_strings = PERBIT(load_strings), + .load_symbols = PERBIT(load_symbols), + .load_dep_syms = PERBIT(load_dep_syms), + .fetch_tables = PERBIT(fetch_tables), + .get_aliases = PERBIT(get_aliases), + .get_modinfo = PERBIT(get_modinfo), +}; + +#undef PERBIT +#undef ElfPERBIT +#undef ELFPERBIT @@ -14,6 +14,7 @@ #include <sys/mman.h> #include "util.h" +#include "elfops.h" #include "zlibsupport.h" #include "testing.h" @@ -40,6 +40,7 @@ #include <syslog.h> #include "util.h" +#include "elfops.h" #include "zlibsupport.h" #include "logging.h" #include "index.h" diff --git a/moduleops.c b/moduleops.c deleted file mode 100644 index 1ee52be..0000000 --- a/moduleops.c +++ /dev/null @@ -1,25 +0,0 @@ -/* The nasty work of reading 32 and 64-bit modules is in here. */ -#include <elf.h> -#include <sys/types.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include "depmod.h" -#include "util.h" -#include "logging.h" -#include "moduleops.h" -#include "tables.h" - -#define PERBIT(x) x##32 -#define ElfPERBIT(x) Elf32_##x -#define ELFPERBIT(x) ELF32_##x -#include "moduleops_core.c" - -#undef PERBIT -#undef ElfPERBIT -#undef ELFPERBIT -#define PERBIT(x) x##64 -#define ElfPERBIT(x) Elf64_##x -#define ELFPERBIT(x) ELF64_##x -#include "moduleops_core.c" diff --git a/moduleops.h b/moduleops.h deleted file mode 100644 index 67c60ca..0000000 --- a/moduleops.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MODINITTOOLS_MODULEOPS_H -#define MODINITTOOLS_MODULEOPS_H -#include <stdio.h> - -/* All the icky stuff to do with manipulating 64 and 32-bit modules - belongs here. */ -struct kernel_symbol32 { - char value[4]; - char name[64 - 4]; -}; - -struct kernel_symbol64 { - char value[8]; - char name[64 - 8]; -}; - -struct module_ops -{ - void (*load_symbols)(struct module *module); - void (*calculate_deps)(struct module *module); - void (*fetch_tables)(struct module *module); - char *(*get_aliases)(struct module *module, unsigned long *size); - char *(*get_modinfo)(struct module *module, unsigned long *size); -}; - -extern struct module_ops mod_ops32, mod_ops64; - -#endif /* MODINITTOOLS_MODULEOPS_H */ diff --git a/moduleops_core.c b/moduleops_core.c deleted file mode 100644 index 0313e27..0000000 --- a/moduleops_core.c +++ /dev/null @@ -1,241 +0,0 @@ -/* Load the given section: NULL on error. */ -static void *PERBIT(load_section)(ElfPERBIT(Ehdr) *hdr, - const char *secname, - unsigned long *secsize, - int conv) -{ - return PERBIT(get_section)(hdr, 0, secname, secsize, conv); -} - -static void PERBIT(load_symbols)(struct module *module) -{ - struct PERBIT(kernel_symbol) *ksyms; - char *ksymstrings; - unsigned long i, size; - - /* New-style: strings are in this section. */ - ksymstrings = PERBIT(load_section)(module->data, "__ksymtab_strings", - &size, module->conv); - if (ksymstrings) { - unsigned int i = 0; - for (;;) { - /* Skip any zero padding. */ - while (!ksymstrings[i]) - if (++i >= size) - return; - add_symbol(ksymstrings+i, module); - i += strlen(ksymstrings+i); - } - /* GPL symbols too */ - ksymstrings = PERBIT(load_section)(module->data, - "__ksymtab_strings_gpl", - &size, module->conv); - for (;;) { - /* Skip any zero padding. */ - while (!ksymstrings[i]) - if (++i >= size) - return; - add_symbol(ksymstrings+i, module); - i += strlen(ksymstrings+i); - } - return; - } - - /* Old-style. */ - ksyms = PERBIT(load_section)(module->data, "__ksymtab", &size, - module->conv); - for (i = 0; i < size / sizeof(struct PERBIT(kernel_symbol)); i++) - add_symbol(ksyms[i].name, module); - ksyms = PERBIT(load_section)(module->data, "__gpl_ksymtab", &size, - module->conv); - for (i = 0; i < size / sizeof(struct PERBIT(kernel_symbol)); i++) - add_symbol(ksyms[i].name, module); -} - -static char *PERBIT(get_aliases)(struct module *module, unsigned long *size) -{ - return PERBIT(load_section)(module->data, ".modalias", size, - module->conv); -} - -static char *PERBIT(get_modinfo)(struct module *module, unsigned long *size) -{ - return PERBIT(load_section)(module->data, ".modinfo", size, - module->conv); -} - -#ifndef STT_REGISTER -#define STT_REGISTER 13 /* Global register reserved to app. */ -#endif - -/* Calculate the dependencies for this module */ -static void PERBIT(calculate_deps)(struct module *module) -{ - unsigned int i; - unsigned long size; - char *strings; - ElfPERBIT(Sym) *syms; - ElfPERBIT(Ehdr) *hdr; - int handle_register_symbols; - - strings = PERBIT(load_section)(module->data, ".strtab", &size, - module->conv); - syms = PERBIT(load_section)(module->data, ".symtab", &size, - module->conv); - - module->num_deps = 0; - module->deps = NULL; - - if (!strings || !syms) { - warn("Couldn't find symtab and strtab in module %s\n", - module->pathname); - return; - } - - hdr = module->data; - handle_register_symbols = 0; - if (END(hdr->e_machine, module->conv) == EM_SPARC || - END(hdr->e_machine, module->conv) == EM_SPARCV9) - handle_register_symbols = 1; - - for (i = 1; i < size / sizeof(syms[0]); i++) { - if (END(syms[i].st_shndx, module->conv) == SHN_UNDEF) { - /* Look for symbol */ - const char *name; - struct module *owner; - int weak; - - name = strings + END(syms[i].st_name, module->conv); - - /* Not really undefined: sparc gcc 3.3 creates - U references when you have global asm - variables, to avoid anyone else misusing - them. */ - if (handle_register_symbols - && (ELFPERBIT(ST_TYPE)(END(syms[i].st_info, - module->conv)) - == STT_REGISTER)) - continue; - - weak = (ELFPERBIT(ST_BIND)(END(syms[i].st_info, - module->conv)) - == STB_WEAK); - owner = find_symbol(name, module->pathname, weak); - if (owner) { - info("%s needs \"%s\": %s\n", - module->pathname, name, - owner->pathname); - add_dep(module, owner); - } - } - } -} - -static void *PERBIT(deref_sym)(ElfPERBIT(Ehdr) *hdr, - ElfPERBIT(Shdr) *sechdrs, - ElfPERBIT(Sym) *sym, - unsigned int *secsize, - int conv) -{ - /* In BSS? Happens for empty device tables on - * recent GCC versions. */ - if (END(sechdrs[END(sym->st_shndx, conv)].sh_type,conv) == SHT_NOBITS) - return NULL; - - if (secsize) - *secsize = END(sym->st_size, conv); - return (void *)hdr - + END(sechdrs[END(sym->st_shndx, conv)].sh_offset, conv) - + END(sym->st_value, conv); -} - -/* FIXME: Check size, unless we end up using aliases anyway --RR */ -static void PERBIT(fetch_tables)(struct module *module) -{ - unsigned int i; - unsigned long size; - char *strings; - ElfPERBIT(Ehdr) *hdr; - ElfPERBIT(Sym) *syms; - ElfPERBIT(Shdr) *sechdrs; - - hdr = module->data; - - sechdrs = (void *)hdr + END(hdr->e_shoff, module->conv); - strings = PERBIT(load_section)(hdr, ".strtab", &size, module->conv); - syms = PERBIT(load_section)(hdr, ".symtab", &size, module->conv); - - /* Don't warn again: we already have above */ - if (!strings || !syms) - return; - - module->pci_table = NULL; - module->usb_table = NULL; - module->ccw_table = NULL; - module->ieee1394_table = NULL; - module->pnp_table = NULL; - module->pnp_card_table = NULL; - module->input_table = NULL; - module->serio_table = NULL; - module->of_table = NULL; - - for (i = 0; i < size / sizeof(syms[0]); i++) { - char *name = strings + END(syms[i].st_name, module->conv); - - if (!module->pci_table && streq(name, "__mod_pci_device_table")) { - module->pci_size = PERBIT(PCI_DEVICE_SIZE); - module->pci_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - } - else if (!module->usb_table && streq(name, "__mod_usb_device_table")) { - module->usb_size = PERBIT(USB_DEVICE_SIZE); - module->usb_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - } - else if (!module->ccw_table && streq(name, "__mod_ccw_device_table")) { - module->ccw_size = PERBIT(CCW_DEVICE_SIZE); - module->ccw_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - } - else if (!module->ieee1394_table && streq(name, "__mod_ieee1394_device_table")) { - module->ieee1394_size = PERBIT(IEEE1394_DEVICE_SIZE); - module->ieee1394_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - } - else if (!module->pnp_table && streq(name, "__mod_pnp_device_table")) { - module->pnp_size = PERBIT(PNP_DEVICE_SIZE); - module->pnp_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - } - else if (!module->pnp_card_table && streq(name, "__mod_pnp_card_device_table")) { - module->pnp_card_size = PERBIT(PNP_CARD_DEVICE_SIZE); - module->pnp_card_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - module->pnp_card_offset = PERBIT(PNP_CARD_DEVICE_OFFSET); - } - else if (!module->input_table && streq(name, "__mod_input_device_table")) { - module->input_size = PERBIT(INPUT_DEVICE_SIZE); - module->input_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - &module->input_table_size, - module->conv); - } - else if (!module->serio_table && streq(name, "__mod_serio_device_table")) { - module->serio_size = PERBIT(SERIO_DEVICE_SIZE); - module->serio_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - } - else if (!module->of_table && streq(name, "__mod_of_device_table")) { - module->of_size = PERBIT(OF_DEVICE_SIZE); - module->of_table = PERBIT(deref_sym)(hdr, sechdrs, &syms[i], - NULL, module->conv); - } - } -} - -struct module_ops PERBIT(mod_ops) = { - .load_symbols = PERBIT(load_symbols), - .calculate_deps = PERBIT(calculate_deps), - .fetch_tables = PERBIT(fetch_tables), - .get_aliases = PERBIT(get_aliases), - .get_modinfo = PERBIT(get_modinfo), -}; @@ -44,13 +44,14 @@ void output_pci_table(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { struct pci_device_id *e; char shortname[strlen(i->pathname) + 1]; + struct module_tables *t = &i->tables; - if (!i->pci_table) + if (!t->pci_table) continue; make_shortname(shortname, i->pathname); - for (e = i->pci_table; e->vendor; e = (void *)e + i->pci_size) - output_pci_entry(e, shortname, out, i->conv); + for (e = t->pci_table; e->vendor; e = (void *)e + t->pci_size) + output_pci_entry(e, shortname, out, i->file.conv); } } @@ -92,15 +93,16 @@ void output_usb_table(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { struct usb_device_id *e; char shortname[strlen(i->pathname) + 1]; + struct module_tables *t = &i->tables; - if (!i->usb_table) + if (!t->usb_table) continue; make_shortname(shortname, i->pathname); - for (e = i->usb_table; + for (e = t->usb_table; e->idVendor || e->bDeviceClass || e->bInterfaceClass; - e = (void *)e + i->usb_size) - output_usb_entry(e, shortname, out, i->conv); + e = (void *)e + t->usb_size) + output_usb_entry(e, shortname, out, i->file.conv); } } @@ -126,14 +128,15 @@ void output_ieee1394_table(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { struct ieee1394_device_id *fw; char shortname[strlen(i->pathname) + 1]; + struct module_tables *t = &i->tables; - if (!i->ieee1394_table) + if (!t->ieee1394_table) continue; make_shortname(shortname, i->pathname); - for (fw = i->ieee1394_table; fw->match_flags; - fw = (void *) fw + i->ieee1394_size) - output_ieee1394_entry(fw, shortname, out, i->conv); + for (fw = t->ieee1394_table; fw->match_flags; + fw = (void *) fw + t->ieee1394_size) + output_ieee1394_entry(fw, shortname, out, i->file.conv); } } @@ -158,15 +161,16 @@ void output_ccw_table(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { struct ccw_device_id *e; char shortname[strlen(i->pathname) + 1]; + struct module_tables *t = &i->tables; - if (!i->ccw_table) + if (!t->ccw_table) continue; make_shortname(shortname, i->pathname); - for (e = i->ccw_table; + for (e = t->ccw_table; e->cu_type || e->cu_model || e->dev_type || e->dev_model; - e = (void *) e + i->ccw_size) - output_ccw_entry(e, shortname, out, i->conv); + e = (void *) e + t->ccw_size) + output_ccw_entry(e, shortname, out, i->file.conv); } } @@ -197,13 +201,14 @@ void output_isapnp_table(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { char shortname[strlen(i->pathname) + 1]; + struct module_tables *t = &i->tables; - if (i->pnp_table) { + if (t->pnp_table) { struct pnp_device_id *id; make_shortname(shortname, i->pathname); - for (id = i->pnp_table; + for (id = t->pnp_table; id->id[0]; - id = (void *)id + i->pnp_size) { + id = (void *)id + t->pnp_size) { fprintf(out, "%-20s", shortname); fprintf(out, " 0xffff 0xffff "); fprintf(out, " 0x00000000 "); /* driver_data */ @@ -211,15 +216,15 @@ void output_isapnp_table(struct module *modules, FILE *out, char *dirname) fprintf(out, "\n"); } } - if (i->pnp_card_table) { + if (t->pnp_card_table) { void *id; make_shortname(shortname, i->pathname); - for (id = i->pnp_card_table; + for (id = t->pnp_card_table; ((char *)id)[0]; - id += i->pnp_card_size) { + id += t->pnp_card_size) { int idx; struct pnp_card_devid *devid - = id + i->pnp_card_offset; + = id + t->pnp_card_offset; fprintf(out, "%-20s", shortname); put_isapnp_id(out, id); @@ -419,46 +424,47 @@ void output_input_table(struct module *modules, FILE *out, char *dirname) void *p; char shortname[strlen(i->pathname) + 1]; int done = 0; + struct module_tables *t = &i->tables; - if (!i->input_table) + if (!t->input_table) continue; make_shortname(shortname, i->pathname); /* Guess what size it really is, based on size of * whole table. Table changed in 2.6.14. This is a hack. */ - if (i->input_size == sizeof(struct input_device_id_old_64)) { - if ((i->input_table_size % i->input_size) != 0) { - i->input_size + if (t->input_size == sizeof(struct input_device_id_old_64)) { + if ((t->input_table_size % t->input_size) != 0) { + t->input_size = sizeof(struct input_device_id_64); } } else { - if ((i->input_table_size % i->input_size) != 0) { - i->input_size + if ((t->input_table_size % t->input_size) != 0) { + t->input_size = sizeof(struct input_device_id_32); } } - for (p = i->input_table; !done; p += i->input_size) { - switch (i->input_size) { + for (p = t->input_table; !done; p += t->input_size) { + switch (t->input_size) { case sizeof(struct input_device_id_old_64): done = output_input_entry_64_old(p, shortname, out, - i->conv); + i->file.conv); break; case sizeof(struct input_device_id_64): done = output_input_entry_64(p, shortname, - out, i->conv); + out, i->file.conv); break; case sizeof(struct input_device_id_old_32): done = output_input_entry_32_old(p, shortname, out, - i->conv); + i->file.conv); break; case sizeof(struct input_device_id_32): done = output_input_entry_32(p, shortname, - out, i->conv); + out, i->file.conv); break; } } @@ -486,12 +492,13 @@ void output_serio_table(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { struct serio_device_id *e; char shortname[strlen(i->pathname) + 1]; + struct module_tables *t = &i->tables; - if (!i->serio_table) + if (!t->serio_table) continue; make_shortname(shortname, i->pathname); - for (e = i->serio_table; e->type || e->proto; e = (void *)e + i->serio_size) + for (e = t->serio_table; e->type || e->proto; e = (void *)e + t->serio_size) output_serio_entry(e, shortname, out); } } @@ -544,13 +551,14 @@ void output_of_table(struct module *modules, FILE *out, char *dirname) for (i = modules; i; i = i->next) { struct of_device_id *e; char shortname[strlen(i->pathname) + 1]; + struct module_tables *t = &i->tables; - if (!i->of_table) + if (!t->of_table) continue; make_shortname(shortname, i->pathname); - for (e = i->of_table; e->name[0]|e->type[0]|e->compatible[0]; - e = (void *)e + i->of_size) + for (e = t->of_table; e->name[0]|e->type[0]|e->compatible[0]; + e = (void *)e + t->of_size) output_of_entry(e, shortname, out); } } @@ -114,6 +114,52 @@ char *underscores(char *string) } /* + * strtbl_add - add a string to a string table. + * + * @str: string to add + * @tbl: current string table. NULL = allocate new table + * + * Allocates an array of pointers to strings. + * The strings themselves are not actually kept in the table. + * + * Returns reallocated and updated string table. NULL = out of memory. + * + * Implementation note: The string table is designed to be lighter-weight + * and faster than a more conventional linked list that stores the strings + * in the list elements, as it does far fewer malloc/realloc calls + * and avoids copying entirely. + */ +struct string_table *strtbl_add(const char *str, struct string_table *tbl) +{ + if (tbl == NULL) { + const char max = 100; + tbl = malloc(sizeof(*tbl) + sizeof(char *) * max); + if (!tbl) + return NULL; + tbl->max = max; + tbl->cnt = 0; + } + if (tbl->cnt >= tbl->max) { + tbl->max *= 2; + tbl = realloc(tbl, sizeof(*tbl) + sizeof(char *) * tbl->max); + if (!tbl) + return NULL; + } + tbl->str[tbl->cnt] = str; + tbl->cnt += 1; + + return tbl; +} + +/* + * strtbl_destroy - string table destructor + */ +void strtbl_free(struct string_table *tbl) +{ + free(tbl); +} + +/* * Get the basename in a pathname. * Unlike the standard implementation, this does not copy the string. */ @@ -157,48 +203,3 @@ int __attribute__ ((pure)) native_endianness() return (char) *((uint32_t*)("\1\0\0\2")); } -/* - * Check ELF file header. - */ -int elf_ident(void *file, unsigned long fsize, int *conv) -{ - /* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */ - unsigned char *ident = file; - - if (fsize < EI_CLASS || memcmp(file, ELFMAG, SELFMAG) != 0) - return -ENOEXEC; /* Not an ELF object */ - if (ident[EI_DATA] == 0 || ident[EI_DATA] > 2) - return -EINVAL; /* Unknown endianness */ - - if (conv != NULL) - *conv = native_endianness() != ident[EI_DATA]; - return ident[EI_CLASS]; -} - -#define PERBIT(x) x##32 -#define ElfPERBIT(x) Elf32_##x -#define ELFPERBIT(x) ELF32_##x -#include "elf_core.c" - -#undef PERBIT -#undef ElfPERBIT -#undef ELFPERBIT -#define PERBIT(x) x##64 -#define ElfPERBIT(x) Elf64_##x -#define ELFPERBIT(x) ELF64_##x -#include "elf_core.c" - -void *get_section(void *file, unsigned long filesize, - const char *secname, unsigned long *secsize) -{ - int conv; - - switch (elf_ident(file, filesize, &conv)) { - case ELFCLASS32: - return get_section32(file, filesize, secname, secsize, conv); - case ELFCLASS64: - return get_section64(file, filesize, secname, secsize, conv); - default: - return NULL; - } -} @@ -3,12 +3,21 @@ #include <stdio.h> +struct string_table +{ + unsigned int cnt; + unsigned int max; + const char *str[0]; +}; + char *getline_wrapped(FILE *file, unsigned int *linenum); void filename2modname(char *modname, const char *filename); char *underscores(char *string); char *my_basename(const char *path); +struct string_table *strtbl_add(const char *str, struct string_table *tbl); + const char *next_string(const char *string, unsigned long *secsize); /* @@ -31,14 +40,6 @@ static inline void __swap_bytes(const void *src, void *dest, unsigned int size) int native_endianness(void); -int elf_ident(void *file, unsigned long fsize, int *conv); -void *get_section(void *file, unsigned long filesize, - const char *secname, unsigned long *secsize); -void *get_section32(void *file, unsigned long filesize, - const char *secname, unsigned long *secsize, int conv); -void *get_section64(void *file, unsigned long filesize, - const char *secname, unsigned long *secsize, int conv); - #define streq(a,b) (strcmp((a),(b)) == 0) #define strstarts(a,start) (strncmp((a),(start), strlen(start)) == 0) |