vserver 1.9.5.x5
[linux-2.6.git] / scripts / mod / modpost.c
index 2a174e5..347549c 100644 (file)
@@ -1,7 +1,7 @@
 /* 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
  *
@@ -18,6 +18,8 @@
 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, ...)
@@ -102,6 +104,7 @@ struct symbol {
        struct module *module;
        unsigned int crc;
        int crc_valid;
+       unsigned int weak:1;
        char name[0];
 };
 
@@ -124,12 +127,13 @@ static inline unsigned int tdb_hash(const char *name)
  * 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;
 }
@@ -143,7 +147,7 @@ new_symbol(const char *name, struct module *module, unsigned int *crc)
        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;
@@ -215,7 +219,7 @@ get_next_line(unsigned long *pos, void *file, unsigned long size)
        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)++)
@@ -347,7 +351,8 @@ handle_modversions(struct module *mod, struct elf_info *info,
                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)
@@ -368,6 +373,7 @@ handle_modversions(struct module *mod, struct elf_info *info,
                           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:
@@ -397,10 +403,44 @@ is_vmlinux(const char *modname)
        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;
@@ -424,8 +464,15 @@ read_symbols(char *modname)
                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
@@ -433,7 +480,7 @@ read_symbols(char *modname)
         * 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
@@ -505,7 +552,7 @@ add_versions(struct buffer *b, struct module *mod)
        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;
@@ -570,6 +617,16 @@ add_depends(struct buffer *b, struct module *mod, struct module *modules)
        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)
 {
@@ -691,7 +748,7 @@ main(int argc, char **argv)
        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;
@@ -702,6 +759,9 @@ main(int argc, char **argv)
                        case 'o':
                                dump_write = optarg;
                                break;
+                       case 'a':
+                               all_versions = 1;
+                               break;
                        default:
                                exit(1);
                }
@@ -724,6 +784,7 @@ main(int argc, char **argv)
                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);