/* Postprocess module symbol versions
*
* Copyright 2003 Kai Germaschewski
- * 2002-2003 Rusty Russell, IBM Corporation
+ * Copyright 2002-2004 Rusty Russell, IBM Corporation
*
* Based in part on module-init-tools/depmod.c,file2alias
*
int modversions = 0;
/* Warn about undefined symbols? (do so if we have vmlinux) */
int have_vmlinux = 0;
+/* Is CONFIG_MODULE_SRCVERSION_ALL set? */
+static int all_versions = 0;
void
fatal(const char *fmt, ...)
struct module *module;
unsigned int crc;
int crc_valid;
+ unsigned int weak:1;
char name[0];
};
* the list of unresolved symbols per module */
struct symbol *
-alloc_symbol(const char *name, struct symbol *next)
+alloc_symbol(const char *name, unsigned int weak, struct symbol *next)
{
struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
memset(s, 0, sizeof(*s));
strcpy(s->name, name);
+ s->weak = weak;
s->next = next;
return s;
}
struct symbol *new;
hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
- new = symbolhash[hash] = alloc_symbol(name, symbolhash[hash]);
+ new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]);
new->module = module;
if (crc) {
new->crc = *crc;
static char line[4096];
int skip = 1;
size_t len = 0;
- char *p = (char *)file + *pos;
+ signed char *p = (signed char *)file + *pos;
char *s = line;
for (; *pos < size ; (*pos)++)
break;
case SHN_UNDEF:
/* undefined symbol */
- if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL)
+ if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
+ ELF_ST_BIND(sym->st_info) != STB_WEAK)
break;
/* ignore global offset table */
if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0)
strlen(MODULE_SYMBOL_PREFIX)) == 0)
mod->unres = alloc_symbol(symname +
strlen(MODULE_SYMBOL_PREFIX),
+ ELF_ST_BIND(sym->st_info) == STB_WEAK,
mod->unres);
break;
default:
return strcmp(myname, "vmlinux") == 0;
}
+/* Parse tag=value strings from .modinfo section */
+static char *next_string(char *string, unsigned long *secsize)
+{
+ /* Skip non-zero chars */
+ while (string[0]) {
+ string++;
+ if ((*secsize)-- <= 1)
+ return NULL;
+ }
+
+ /* Skip any zero padding. */
+ while (!string[0]) {
+ string++;
+ if ((*secsize)-- <= 1)
+ return NULL;
+ }
+ return string;
+}
+
+static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
+ const char *tag)
+{
+ char *p;
+ unsigned int taglen = strlen(tag);
+ unsigned long size = modinfo_len;
+
+ for (p = modinfo; p; p = next_string(p, &size)) {
+ if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
+ return p + taglen + 1;
+ }
+ return NULL;
+}
+
void
read_symbols(char *modname)
{
const char *symname;
+ char *version;
struct module *mod;
struct elf_info info = { };
Elf_Sym *sym;
handle_modversions(mod, &info, sym, symname);
handle_moddevtable(mod, &info, sym, symname);
}
- maybe_frob_version(modname, info.modinfo, info.modinfo_len,
- (void *)info.modinfo - (void *)info.hdr);
+
+ version = get_modinfo(info.modinfo, info.modinfo_len, "version");
+ if (version)
+ maybe_frob_rcs_version(modname, version, info.modinfo,
+ version - (char *)info.hdr);
+ if (version || (all_versions && !is_vmlinux(modname)))
+ get_src_version(modname, mod->srcversion,
+ sizeof(mod->srcversion)-1);
+
parse_elf_finish(&info);
/* Our trick to get versioning for struct_module - it's
* the automatic versioning doesn't pick it up, but it's really
* important anyhow */
if (modversions)
- mod->unres = alloc_symbol("struct_module", mod->unres);
+ mod->unres = alloc_symbol("struct_module", 0, mod->unres);
}
#define SZ 500
for (s = mod->unres; s; s = s->next) {
exp = find_symbol(s->name);
if (!exp || exp->module == mod) {
- if (have_vmlinux)
+ if (have_vmlinux && !s->weak)
fprintf(stderr, "*** Warning: \"%s\" [%s.ko] "
"undefined!\n", s->name, mod->name);
continue;
buf_printf(b, "\";\n");
}
+void
+add_srcversion(struct buffer *b, struct module *mod)
+{
+ if (mod->srcversion[0]) {
+ buf_printf(b, "\n");
+ buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n",
+ mod->srcversion);
+ }
+}
+
void
write_if_changed(struct buffer *b, const char *fname)
{
char *dump_read = NULL, *dump_write = NULL;
int opt;
- while ((opt = getopt(argc, argv, "i:mo:")) != -1) {
+ while ((opt = getopt(argc, argv, "i:mo:a")) != -1) {
switch(opt) {
case 'i':
dump_read = optarg;
case 'o':
dump_write = optarg;
break;
+ case 'a':
+ all_versions = 1;
+ break;
default:
exit(1);
}
add_versions(&buf, mod);
add_depends(&buf, mod, modules);
add_moddevtable(&buf, mod);
+ add_srcversion(&buf, mod);
sprintf(fname, "%s.mod.c", mod->name);
write_if_changed(&buf, fname);