Add a new option -E Module.symvers to read symbol versions from a
Module.symvers files and modules and warn about mismatches if -e is
given.

Signed-off-by: Michal Marek <[email protected]>
---
 depmod.c      |  100 +++++++++++++++++++++++++++++++++++---------
 elfops.h      |    5 +-
 elfops_core.c |  131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 208 insertions(+), 28 deletions(-)

diff --git a/depmod.c b/depmod.c
index ba405fe..fe843ad 100644
--- a/depmod.c
+++ b/depmod.c
@@ -68,6 +68,7 @@ struct symbol
 {
        struct symbol *next;
        struct module *owner;
+       uint64_t ver;
        char name[0];
 };
 
@@ -86,12 +87,13 @@ static inline unsigned int tdb_hash(const char *name)
        return (1103515243 * value + 12345);
 }
 
-void add_symbol(const char *name, struct module *owner)
+void add_symbol(const char *name, uint64_t ver, struct module *owner)
 {
        unsigned int hash;
        struct symbol *new = NOFAIL(malloc(sizeof *new + strlen(name) + 1));
 
        new->owner = owner;
+       new->ver = ver;
        strcpy(new->name, name);
 
        hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
@@ -99,9 +101,10 @@ void add_symbol(const char *name, struct module *owner)
        symbolhash[hash] = new;
 }
 
-static int print_unknown;
+static int print_unknown, check_symvers;
 
-struct module *find_symbol(const char *name, const char *modname, int weak)
+struct module *find_symbol(const char *name, uint64_t ver,
+               const char *modname, int weak)
 {
        struct symbol *s;
 
@@ -111,7 +114,13 @@ struct module *find_symbol(const char *name, const char 
*modname, int weak)
 
        for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s=s->next) {
                if (streq(s->name, name))
-                       return s->owner;
+                       break;
+       }
+       if (s) {
+               if (ver && s->ver && s->ver != ver && print_unknown && !weak)
+                       warn("%s disagrees about version of symbol %s\n",
+                                       modname, name);
+               return s->owner;
        }
 
        if (print_unknown && !weak)
@@ -132,6 +141,14 @@ void add_dep(struct module *mod, struct module *depends_on)
        mod->deps[mod->num_deps++] = depends_on;
 }
 
+static void add_fake_syms(void)
+{
+       /* __this_module is magic inserted by kernel loader. */
+       add_symbol("__this_module", 0, NULL);
+       /* On S390, this is faked up too */
+       add_symbol("_GLOBAL_OFFSET_TABLE_", 0, NULL);
+}
+
 static void load_system_map(const char *filename)
 {
        FILE *system_map;
@@ -158,15 +175,38 @@ static void load_system_map(const char *filename)
 
                /* Covers gpl-only and normal symbols. */
                if (strstarts(ptr+1, ksymstr))
-                       add_symbol(ptr+1+ksymstr_len, NULL);
+                       add_symbol(ptr+1+ksymstr_len, 0, NULL);
        }
 
        fclose(system_map);
+       add_fake_syms();
+}
 
-       /* __this_module is magic inserted by kernel loader. */
-       add_symbol("__this_module", NULL);
-       /* On S390, this is faked up too */
-       add_symbol("_GLOBAL_OFFSET_TABLE_", NULL);
+static void load_module_symvers(const char *filename)
+{
+       FILE *module_symvers;
+       char line[10240];
+
+       module_symvers = fopen(filename, "r");
+       if (!module_symvers)
+               fatal("Could not open '%s': %s\n", filename, strerror(errno));
+
+       /* eg. "0xb352177e\tfind_first_bit\tvmlinux\tEXPORT_SYMBOL" */
+       while (fgets(line, sizeof(line)-1, module_symvers)) {
+               const char *ver, *sym, *where;
+
+               ver = strtok(line, " \t");
+               sym = strtok(NULL, " \t");
+               where = strtok(NULL, " \t");
+               if (!ver || !sym || !where)
+                       continue;
+
+               if (streq(where, "vmlinux"))
+                       add_symbol(sym, strtoull(ver, NULL, 16), NULL);
+       }
+
+       fclose(module_symvers);
+       add_fake_syms();
 }
 
 static struct option options[] = { { "all", 0, NULL, 'a' },
@@ -174,6 +214,7 @@ static struct option options[] = { { "all", 0, NULL, 'a' },
                                   { "basedir", 1, NULL, 'b' },
                                   { "errsyms", 0, NULL, 'e' },
                                   { "filesyms", 1, NULL, 'F' },
+                                  { "symvers", 1, NULL, 'E' },
                                   { "help", 0, NULL, 'h' },
                                   { "show", 0, NULL, 'n' },
                                   { "dry-run", 0, NULL, 'n' },
@@ -242,7 +283,10 @@ static void print_usage(const char *name)
        "\t    --basedir basedirectory    Use an image of a module tree.\n"
        "\t-F kernelsyms\n"
        "\t    --filesyms kernelsyms      Use the file instead of the\n"
-       "\t                               current kernel symbols.\n",
+       "\t                               current kernel symbols.\n"
+       "\t-E Module.symvers\n"
+       "\t    --symvers Module.symvers   Use Module.symvers file to check\n"
+       "\t                               symbol versions.\n",
        "depmod", "depmod");
 }
 
@@ -650,24 +694,28 @@ static void calculate_deps(struct module *module)
        unsigned int i;
        struct string_table *symnames;
        struct string_table *symtypes;
+       uint64_t *symvers = NULL;
        struct elf_file *file;
 
        module->num_deps = 0;
        module->deps = NULL;
        file = module->file;
 
-       symnames = file->ops->load_dep_syms(file, &symtypes);
+       symnames = file->ops->load_dep_syms(file, &symtypes,
+                       check_symvers ? &symvers : NULL);
        if (!symnames || !symtypes)
                return;
 
        for (i = 0; i < symnames->cnt; i++) {
                const char *name;
+               uint64_t ver;
                struct module *owner;
                int weak;
 
                name = symnames->str[i];
+               ver = symvers ? symvers[i] : 0;
                weak = (*(symtypes->str[i]) == 'W');
-               owner = find_symbol(name, module->pathname, weak);
+               owner = find_symbol(name, ver, module->pathname, weak);
                if (owner) {
                        info("%s needs \"%s\": %s\n",
                               module->pathname, name,
@@ -678,6 +726,7 @@ static void calculate_deps(struct module *module)
 
        free(symnames);
        free(symtypes);
+       free(symvers);
 }
 
 static struct module *parse_modules(struct module *list)
@@ -688,13 +737,17 @@ static struct module *parse_modules(struct module *list)
        int j;
 
        for (i = list; i; i = i->next) {
+               uint64_t *symvers = NULL;
                file = i->file;
-               syms = file->ops->load_symbols(file);
+               syms = file->ops->load_symbols(file,
+                               check_symvers ? &symvers : NULL);
                if (syms) {
                        for (j = 0; j < syms->cnt; j++)
-                               add_symbol(syms->str[j], i);
+                               add_symbol(syms->str[j],
+                                       symvers ? symvers[j] : 0, i);
                        strtbl_free(syms);
                }
+               free(symvers);
                file->ops->fetch_tables(file, &i->tables);
        }
        
@@ -1176,7 +1229,7 @@ int main(int argc, char *argv[])
 {
        int opt, all = 0, maybe_all = 0, doing_stdout = 0;
        char *basedir = "", *dirname, *version, *badopt = NULL,
-               *system_map = NULL;
+               *system_map = NULL, *module_symvers = NULL;
        int i;
        const char *config = NULL;
 
@@ -1186,7 +1239,7 @@ int main(int argc, char *argv[])
        /* Don't print out any errors just yet, we might want to exec
            backwards compat version. */
        opterr = 0;
-       while ((opt = getopt_long(argc, argv, "ab:ArehnqruvVF:C:wm", options, 
NULL))
+       while ((opt = getopt_long(argc, argv, "ab:ArehnqruvVE:F:C:wm", options, 
NULL))
               != -1) {
                switch (opt) {
                case 'a':
@@ -1199,6 +1252,10 @@ int main(int argc, char *argv[])
                case 'A':
                        maybe_all = 1;
                        break;
+               case 'E':
+                       module_symvers = optarg;
+                       check_symvers = 1;
+                       break;
                case 'F':
                        system_map = optarg;
                        break;
@@ -1236,11 +1293,14 @@ int main(int argc, char *argv[])
                }
        }
 
-       /* We can't print unknowns without a System.map */
-       if (!system_map)
-               print_unknown = 0;
-       else
+       if (module_symvers)
+               load_module_symvers(module_symvers);
+       else if (system_map)
                load_system_map(system_map);
+       else if (print_unknown) {
+               warn("-e needs -E or -F");
+               print_unknown = 0;
+       }
 
        /* They can specify the version naked on the command line */
        if (optind < argc && is_version_number(argv[optind])) {
diff --git a/elfops.h b/elfops.h
index 6cdfc07..0fb5167 100644
--- a/elfops.h
+++ b/elfops.h
@@ -64,9 +64,10 @@ struct module_ops
                const char *secname, unsigned long *secsize);
        struct string_table *(*load_strings)(struct elf_file *module,
                const char *secname, struct string_table *tbl, errfn_t error);
-       struct string_table *(*load_symbols)(struct elf_file *module);
+       struct string_table *(*load_symbols)(struct elf_file *module,
+               uint64_t **versions);
        struct string_table *(*load_dep_syms)(struct elf_file *module,
-                       struct string_table **types);
+               struct string_table **types, uint64_t **versions);
        void (*fetch_tables)(struct elf_file *module,
                struct module_tables *tables);
        char *(*get_aliases)(struct elf_file *module, unsigned long *size);
diff --git a/elfops_core.c b/elfops_core.c
index 5df9f25..1495f68 100644
--- a/elfops_core.c
+++ b/elfops_core.c
@@ -100,10 +100,53 @@ static struct string_table *PERBIT(load_strings)(struct 
elf_file *module,
        return tbl;
 }
 
-static struct string_table *PERBIT(load_symbols)(struct elf_file *module)
+static struct string_table *PERBIT(load_symbols)(struct elf_file *module,
+                                                 uint64_t **versions)
 {
        struct string_table *symtbl = NULL;
 
+       if (versions) {
+               static const char crc[] = "__crc_";
+               static const int crc_len = sizeof(crc) - 1;
+               unsigned int num_syms, i;
+               unsigned long size;
+               ElfPERBIT(Sym) *syms;
+               char *strings;
+               int conv;
+
+               *versions = NULL;
+               strings = PERBIT(load_section)(module, ".strtab", &size);
+               syms = PERBIT(load_section)(module, ".symtab", &size);
+               if (!strings || !syms)
+                       goto fallback;
+               num_syms = size / sizeof(syms[0]);
+               *versions = NOFAIL(calloc(sizeof(**versions), num_syms));
+
+               conv = module->conv;
+               for (i = 1; i < num_syms; i++) {
+                       const char *name;
+                       name = strings + END(syms[i].st_name, conv);
+                       if (strncmp(name, crc, crc_len) != 0)
+                               continue;
+                       name += crc_len;
+                       symtbl = NOFAIL(strtbl_add(name, symtbl));
+                       (*versions)[symtbl->cnt - 1] = END(syms[i].st_value,
+                                       conv);
+               }
+               if (!symtbl) {
+                       /* Either this module does not export any symbols, or
+                        * it was compiled without CONFIG_MODVERSIONS. If the
+                        * latter, we will print a warning in load_dep_syms,
+                        * so just silently fallback to __ksymtab_strings in
+                        * both cases.
+                        */
+                       free(*versions);
+                       *versions = NULL;
+                       goto fallback;
+               }
+               return symtbl;
+       }
+fallback:
        return PERBIT(load_strings)(module, "__ksymtab_strings", symtbl,
                        fatal);
 }
@@ -123,37 +166,78 @@ static char *PERBIT(get_modinfo)(struct elf_file *module, 
unsigned long *size)
 #endif
 
 static struct string_table *PERBIT(load_dep_syms)(struct elf_file *module,
-                                                 struct string_table **types)
+                                                 struct string_table **types,
+                                                 uint64_t **versions)
 {
-       unsigned int i;
+       unsigned int i, num_syms;
+       unsigned int j, num_symvers, versions_size;
        unsigned long size;
        char *strings;
        ElfPERBIT(Sym) *syms;
        ElfPERBIT(Ehdr) *hdr;
+       struct PERBIT(modver_info) **symvers;
        int handle_register_symbols;
        struct string_table *names;
        int conv;
 
        names = NULL;
        *types = NULL;
+       symvers = NULL;
+       num_symvers = versions_size = 0;
+
+       if (versions) {
+               int ok = 1;
+               *versions = NULL;
+               struct PERBIT(modver_info) *symvers_sec;
+
+               symvers_sec = module->ops->load_section(module, "__versions",
+                               &size);
+               if (!symvers_sec) {
+                       warn("%s is built without modversions",
+                                       module->pathname);
+                       ok = 0;
+               }
+               if (size % sizeof(symvers[0]) != 0) {
+                       warn("invalid __versions section size in %s",
+                                       module->pathname);
+                       ok = 0;
+               }
+               if (ok) {
+                       num_symvers = size / sizeof(symvers_sec[0]);
+                       /* symvers is used to keep track of each visited entry.
+                        * The table also contains the fake struct_module /
+                        * module_layout symbol which we don't want to miss.
+                        */
+                       symvers = NOFAIL(malloc(num_symvers *
+                                               sizeof(symvers[0])));
+                       for (j = 0; j < num_symvers; j++)
+                               symvers[j] = &symvers_sec[j];
+               } else {
+                       versions = 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",
                     module->pathname);
-               return NULL;
+               goto out;
        }
 
+       num_syms = size / sizeof(syms[0]);
        hdr = module->data;
        conv = module->conv;
+       if (versions) {
+               versions_size = num_syms;
+               *versions = NOFAIL(calloc(sizeof(**versions), versions_size));
+       }
 
        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++) {
+       for (i = 1; i < num_syms; i++) {
                if (END(syms[i].st_shndx, conv) == SHN_UNDEF) {
                        /* Look for symbol */
                        const char *name;
@@ -175,8 +259,43 @@ static struct string_table *PERBIT(load_dep_syms)(struct 
elf_file *module,
                        names = NOFAIL(strtbl_add(name, names));
                        *types = NOFAIL(strtbl_add(weak ? weak_sym : undef_sym,
                                *types));
+
+                       if (!versions)
+                               continue;
+                       /* Not optimal, but the number of required symbols
+                        * is usually not huge and this is only called by
+                        * depmod.
+                        */
+                       for (j = 0; j < num_symvers; j++) {
+                               struct PERBIT(modver_info) *info = symvers[j];
+
+                               if (!info)
+                                       continue;
+                               if (streq(name, info->name)) {
+                                       (*versions)[names->cnt - 1] =
+                                               END(info->crc, conv);
+                                       symvers[j] = NULL;
+                                       break;
+                               }
+                       }
+               }
+       }
+       /* add struct_module / module_layout */
+       for (j = 0; j < num_symvers; j++) {
+               struct PERBIT(modver_info) *info = symvers[j];
+
+               if (!info)
+                       continue;
+               if ((names ? names->cnt : 0) >= versions_size) {
+                       versions_size++;
+                       *versions = NOFAIL(realloc(*versions, versions_size));
                }
+               names = NOFAIL(strtbl_add(info->name, names));
+               *types = NOFAIL(strtbl_add(undef_sym, *types));
+               (*versions)[names->cnt - 1] = END(info->crc, conv);
        }
+out:
+       free(symvers);
        return names;
 }
 
-- 
1.6.3
--
To unsubscribe from this list: send the line "unsubscribe linux-modules" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to