Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / scripts / mod / modpost.c
index d0f86ed..dfde0e8 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <ctype.h>
 #include "modpost.h"
+#include "../../include/linux/license.h"
 
 /* Are we using CONFIG_MODVERSIONS? */
 int modversions = 0;
@@ -22,6 +23,11 @@ int have_vmlinux = 0;
 static int all_versions = 0;
 /* If we are modposting external module set to 1 */
 static int external_module = 0;
+/* How a symbol is exported */
+enum export {
+       export_plain,      export_unused,     export_gpl,
+       export_unused_gpl, export_gpl_future, export_unknown
+};
 
 void fatal(const char *fmt, ...)
 {
@@ -97,6 +103,7 @@ static struct module *new_module(char *modname)
 
        /* add to list */
        mod->name = p;
+       mod->gpl_compatible = -1;
        mod->next = modules;
        modules = mod;
 
@@ -118,6 +125,7 @@ struct symbol {
        unsigned int kernel:1;     /* 1 if symbol is from kernel
                                    *  (only for external modules) **/
        unsigned int preloaded:1;  /* 1 if symbol from Module.symvers */
+       enum export  export;       /* Type of export */
        char name[0];
 };
 
@@ -153,7 +161,8 @@ static struct symbol *alloc_symbol(const char *name, unsigned int weak,
 }
 
 /* For the hash of exported symbols */
-static struct symbol *new_symbol(const char *name, struct module *module)
+static struct symbol *new_symbol(const char *name, struct module *module,
+                                enum export export)
 {
        unsigned int hash;
        struct symbol *new;
@@ -161,6 +170,7 @@ static struct symbol *new_symbol(const char *name, struct module *module)
        hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
        new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]);
        new->module = module;
+       new->export = export;
        return new;
 }
 
@@ -179,16 +189,63 @@ static struct symbol *find_symbol(const char *name)
        return NULL;
 }
 
+static struct {
+       const char *str;
+       enum export export;
+} export_list[] = {
+       { .str = "EXPORT_SYMBOL",            .export = export_plain },
+       { .str = "EXPORT_UNUSED_SYMBOL",     .export = export_unused },
+       { .str = "EXPORT_SYMBOL_GPL",        .export = export_gpl },
+       { .str = "EXPORT_UNUSED_SYMBOL_GPL", .export = export_unused_gpl },
+       { .str = "EXPORT_SYMBOL_GPL_FUTURE", .export = export_gpl_future },
+       { .str = "(unknown)",                .export = export_unknown },
+};
+
+
+static const char *export_str(enum export ex)
+{
+       return export_list[ex].str;
+}
+
+static enum export export_no(const char * s)
+{
+       int i;
+       if (!s)
+               return export_unknown;
+       for (i = 0; export_list[i].export != export_unknown; i++) {
+               if (strcmp(export_list[i].str, s) == 0)
+                       return export_list[i].export;
+       }
+       return export_unknown;
+}
+
+static enum export export_from_sec(struct elf_info *elf, Elf_Section sec)
+{
+       if (sec == elf->export_sec)
+               return export_plain;
+       else if (sec == elf->export_unused_sec)
+               return export_unused;
+       else if (sec == elf->export_gpl_sec)
+               return export_gpl;
+       else if (sec == elf->export_unused_gpl_sec)
+               return export_unused_gpl;
+       else if (sec == elf->export_gpl_future_sec)
+               return export_gpl_future;
+       else
+               return export_unknown;
+}
+
 /**
  * Add an exported symbol - it may have already been added without a
  * CRC, in this case just update the CRC
  **/
-static struct symbol *sym_add_exported(const char *name, struct module *mod)
+static struct symbol *sym_add_exported(const char *name, struct module *mod,
+                                      enum export export)
 {
        struct symbol *s = find_symbol(name);
 
        if (!s) {
-               s = new_symbol(name, mod);
+               s = new_symbol(name, mod, export);
        } else {
                if (!s->preloaded) {
                        warn("%s: '%s' exported twice. Previous export "
@@ -200,16 +257,17 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod)
        s->preloaded = 0;
        s->vmlinux   = is_vmlinux(mod->name);
        s->kernel    = 0;
+       s->export    = export;
        return s;
 }
 
 static void sym_update_crc(const char *name, struct module *mod,
-                          unsigned int crc)
+                          unsigned int crc, enum export export)
 {
        struct symbol *s = find_symbol(name);
 
        if (!s)
-               s = new_symbol(name, mod);
+               s = new_symbol(name, mod, export);
        s->crc = crc;
        s->crc_valid = 1;
 }
@@ -283,7 +341,7 @@ static void parse_elf(struct elf_info *info, const char *filename)
        hdr = grab_file(filename, &info->size);
        if (!hdr) {
                perror(filename);
-               abort();
+               exit(1);
        }
        info->hdr = hdr;
        if (info->size < sizeof(*hdr))
@@ -309,13 +367,25 @@ static void parse_elf(struct elf_info *info, const char *filename)
        for (i = 1; i < hdr->e_shnum; i++) {
                const char *secstrings
                        = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+               const char *secname;
 
                if (sechdrs[i].sh_offset > info->size)
                        goto truncated;
-               if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) {
+               secname = secstrings + sechdrs[i].sh_name;
+               if (strcmp(secname, ".modinfo") == 0) {
                        info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
                        info->modinfo_len = sechdrs[i].sh_size;
-               }
+               } else if (strcmp(secname, "__ksymtab") == 0)
+                       info->export_sec = i;
+               else if (strcmp(secname, "__ksymtab_unused") == 0)
+                       info->export_unused_sec = i;
+               else if (strcmp(secname, "__ksymtab_gpl") == 0)
+                       info->export_gpl_sec = i;
+               else if (strcmp(secname, "__ksymtab_unused_gpl") == 0)
+                       info->export_unused_gpl_sec = i;
+               else if (strcmp(secname, "__ksymtab_gpl_future") == 0)
+                       info->export_gpl_future_sec = i;
+
                if (sechdrs[i].sh_type != SHT_SYMTAB)
                        continue;
 
@@ -353,6 +423,7 @@ static void handle_modversions(struct module *mod, struct elf_info *info,
                               Elf_Sym *sym, const char *symname)
 {
        unsigned int crc;
+       enum export export = export_from_sec(info, sym->st_shndx);
 
        switch (sym->st_shndx) {
        case SHN_COMMON:
@@ -362,7 +433,8 @@ static void handle_modversions(struct module *mod, struct elf_info *info,
                /* CRC'd symbol */
                if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) {
                        crc = (unsigned int) sym->st_value;
-                       sym_update_crc(symname + strlen(CRC_PFX), mod, crc);
+                       sym_update_crc(symname + strlen(CRC_PFX), mod, crc,
+                                       export);
                }
                break;
        case SHN_UNDEF:
@@ -406,7 +478,8 @@ static void handle_modversions(struct module *mod, struct elf_info *info,
        default:
                /* All exported symbols */
                if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) {
-                       sym_add_exported(symname + strlen(KSYMTAB_PFX), mod);
+                       sym_add_exported(symname + strlen(KSYMTAB_PFX), mod,
+                                       export);
                }
                if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0)
                        mod->has_init = 1;
@@ -437,13 +510,18 @@ static char *next_string(char *string, unsigned long *secsize)
        return string;
 }
 
-static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
-                        const char *tag)
+static char *get_next_modinfo(void *modinfo, unsigned long modinfo_len,
+                             const char *tag, char *info)
 {
        char *p;
        unsigned int taglen = strlen(tag);
        unsigned long size = modinfo_len;
 
+       if (info) {
+               size -= info - (char *)modinfo;
+               modinfo = next_string(info, &size);
+       }
+
        for (p = modinfo; p; p = next_string(p, &size)) {
                if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=')
                        return p + taglen + 1;
@@ -451,6 +529,13 @@ static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
        return NULL;
 }
 
+static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
+                        const char *tag)
+
+{
+       return get_next_modinfo(modinfo, modinfo_len, tag, NULL);
+}
+
 /**
  * Test if string s ends in string sub
  * return 0 if match
@@ -821,6 +906,10 @@ static int init_section_ref_ok(const char *name)
                ".pci_fixup_final",
                ".pdr",
                "__param",
+               "__ex_table",
+               ".fixup",
+               ".smp_locks",
+               ".plt",  /* seen on ARCH=um build on x86_64. Harmless */
                NULL
        };
        /* Start of section names */
@@ -846,6 +935,8 @@ static int init_section_ref_ok(const char *name)
        for (s = namelist3; *s; s++)
                if (strstr(name, *s) != NULL)
                        return 1;
+       if (strrcmp(name, ".init") == 0)
+               return 1;
        return 0;
 }
 
@@ -892,6 +983,10 @@ static int exit_section_ref_ok(const char *name)
                ".exitcall.exit",
                ".eh_frame",
                ".stab",
+               "__ex_table",
+               ".fixup",
+               ".smp_locks",
+               ".plt",  /* seen on ARCH=um build on x86_64. Harmless */
                NULL
        };
        /* Start of section names */
@@ -921,6 +1016,7 @@ static void read_symbols(char *modname)
 {
        const char *symname;
        char *version;
+       char *license;
        struct module *mod;
        struct elf_info info = { };
        Elf_Sym *sym;
@@ -936,6 +1032,18 @@ static void read_symbols(char *modname)
                mod->skip = 1;
        }
 
+       license = get_modinfo(info.modinfo, info.modinfo_len, "license");
+       while (license) {
+               if (license_is_gpl_compatible(license))
+                       mod->gpl_compatible = 1;
+               else {
+                       mod->gpl_compatible = 0;
+                       break;
+               }
+               license = get_next_modinfo(info.modinfo, info.modinfo_len,
+                                          "license", license);
+       }
+
        for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
                symname = info.strtab + sym->st_name;
 
@@ -992,6 +1100,67 @@ void buf_write(struct buffer *buf, const char *s, int len)
        buf->pos += len;
 }
 
+static void check_for_gpl_usage(enum export exp, const char *m, const char *s)
+{
+       const char *e = is_vmlinux(m) ?"":".ko";
+
+       switch (exp) {
+       case export_gpl:
+               fatal("modpost: GPL-incompatible module %s%s "
+                     "uses GPL-only symbol '%s'\n", m, e, s);
+               break;
+       case export_unused_gpl:
+               fatal("modpost: GPL-incompatible module %s%s "
+                     "uses GPL-only symbol marked UNUSED '%s'\n", m, e, s);
+               break;
+       case export_gpl_future:
+               warn("modpost: GPL-incompatible module %s%s "
+                     "uses future GPL-only symbol '%s'\n", m, e, s);
+               break;
+       case export_plain:
+       case export_unused:
+       case export_unknown:
+               /* ignore */
+               break;
+       }
+}
+
+static void check_for_unused(enum export exp, const char* m, const char* s)
+{
+       const char *e = is_vmlinux(m) ?"":".ko";
+
+       switch (exp) {
+       case export_unused:
+       case export_unused_gpl:
+               warn("modpost: module %s%s "
+                     "uses symbol '%s' marked UNUSED\n", m, e, s);
+               break;
+       default:
+               /* ignore */
+               break;
+       }
+}
+
+static void check_exports(struct module *mod)
+{
+       struct symbol *s, *exp;
+
+       for (s = mod->unres; s; s = s->next) {
+               const char *basename;
+               exp = find_symbol(s->name);
+               if (!exp || exp->module == mod)
+                       continue;
+               basename = strrchr(mod->name, '/');
+               if (basename)
+                       basename++;
+               else
+                       basename = mod->name;
+               if (!mod->gpl_compatible)
+                       check_for_gpl_usage(exp->export, basename, exp->name);
+               check_for_unused(exp->export, basename, exp->name);
+        }
+}
+
 /**
  * Header for the generated file
  **/
@@ -1142,6 +1311,9 @@ static void write_if_changed(struct buffer *b, const char *fname)
        fclose(file);
 }
 
+/* parse Module.symvers file. line format:
+ * 0x12345678<tab>symbol<tab>module[[<tab>export]<tab>something]
+ **/
 static void read_dump(const char *fname, unsigned int kernel)
 {
        unsigned long size, pos = 0;
@@ -1153,7 +1325,7 @@ static void read_dump(const char *fname, unsigned int kernel)
                return;
 
        while ((line = get_next_line(&pos, file, size))) {
-               char *symname, *modname, *d;
+               char *symname, *modname, *d, *export, *end;
                unsigned int crc;
                struct module *mod;
                struct symbol *s;
@@ -1164,8 +1336,10 @@ static void read_dump(const char *fname, unsigned int kernel)
                if (!(modname = strchr(symname, '\t')))
                        goto fail;
                *modname++ = '\0';
-               if (strchr(modname, '\t'))
-                       goto fail;
+               if ((export = strchr(modname, '\t')) != NULL)
+                       *export++ = '\0';
+               if (export && ((end = strchr(export, '\t')) != NULL))
+                       *end = '\0';
                crc = strtoul(line, &d, 16);
                if (*symname == '\0' || *modname == '\0' || *d != '\0')
                        goto fail;
@@ -1177,10 +1351,10 @@ static void read_dump(const char *fname, unsigned int kernel)
                        mod = new_module(NOFAIL(strdup(modname)));
                        mod->skip = 1;
                }
-               s = sym_add_exported(symname, mod);
+               s = sym_add_exported(symname, mod, export_no(export));
                s->kernel    = kernel;
                s->preloaded = 1;
-               sym_update_crc(symname, mod, crc);
+               sym_update_crc(symname, mod, crc, export_no(export));
        }
        return;
 fail:
@@ -1210,9 +1384,10 @@ static void write_dump(const char *fname)
                symbol = symbolhash[n];
                while (symbol) {
                        if (dump_sym(symbol))
-                               buf_printf(&buf, "0x%08x\t%s\t%s\n",
+                               buf_printf(&buf, "0x%08x\t%s\t%s\t%s\n",
                                        symbol->crc, symbol->name,
-                                       symbol->module->name);
+                                       symbol->module->name,
+                                       export_str(symbol->export));
                        symbol = symbol->next;
                }
        }
@@ -1260,6 +1435,12 @@ int main(int argc, char **argv)
                read_symbols(argv[optind++]);
        }
 
+       for (mod = modules; mod; mod = mod->next) {
+               if (mod->skip)
+                       continue;
+               check_exports(mod);
+       }
+
        for (mod = modules; mod; mod = mod->next) {
                if (mod->skip)
                        continue;