aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Masters <jcm@jonmasters.org>2009-06-23 04:07:06 -0400
committerJon Masters <jcm@jonmasters.org>2009-06-23 04:07:06 -0400
commitcd45f76ad8ff107224fb446c43e795a57772abf9 (patch)
tree5d9146f6eff470ee70ae2a005b1b16f881c94ecd
parent0ed506fa85309f5520e42833480d5b74516df054 (diff)
downloadmodule-init-tools-cd45f76ad8ff107224fb446c43e795a57772abf9.tar.gz
depmod: add ability to check symbol versions
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 <mmarek@suse.cz> Signed-off-by: Jon Masters <jcm@jonmasters.org>
-rw-r--r--depmod.c101
-rw-r--r--elfops.h5
-rw-r--r--elfops_core.c131
3 files changed, 209 insertions, 28 deletions
diff --git a/depmod.c b/depmod.c
index 932412e..4c8caa0 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,21 +175,45 @@ 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' },
{ "quick", 0, NULL, 'A' },
{ "basedir", 1, NULL, 'b' },
{ "config", 1, NULL, 'C' },
+ { "symvers", 1, NULL, 'E' },
{ "filesyms", 1, NULL, 'F' },
{ "errsyms", 0, NULL, 'e' },
{ "unresolved-error", 0, NULL, 'u' },
@@ -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);
}
@@ -1175,14 +1228,15 @@ struct module_overrides *overrides = NULL;
int main(int argc, char *argv[])
{
int opt, all = 0, maybe_all = 0, doing_stdout = 0;
- char *basedir = "", *dirname, *version, *system_map = NULL;
+ char *basedir = "", *dirname, *version;
+ char *system_map = NULL, *module_symvers = NULL;
int i;
const char *config = NULL;
if (native_endianness() == 0)
abort();
- while ((opt = getopt_long(argc, argv, "aAb:C:F:euqrvnhVwm", options, NULL))
+ while ((opt = getopt_long(argc, argv, "aAb:C:E:F:euqrvnhVwm", options, NULL))
!= -1) {
switch (opt) {
case 'a':
@@ -1198,6 +1252,10 @@ int main(int argc, char *argv[])
case 'C':
config = optarg;
break;
+ case 'E':
+ module_symvers = optarg;
+ check_symvers = 1;
+ break;
case 'F':
system_map = optarg;
break;
@@ -1233,11 +1291,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;
}