kbuild: introduce __init_refok/__initdata_refok to supress section mismatch warnings
[safe/jmp/linux-2.6] / scripts / mod / modpost.c
index 7e22689..7c87267 100644 (file)
@@ -23,8 +23,13 @@ int have_vmlinux = 0;
 static int all_versions = 0;
 /* If we are modposting external module set to 1 */
 static int external_module = 0;
+/* Only warn about unresolved symbols */
+static int warn_unresolved = 0;
 /* How a symbol is exported */
-enum export {export_plain, export_gpl, export_gpl_future, export_unknown};
+enum export {
+       export_plain,      export_unused,     export_gpl,
+       export_unused_gpl, export_gpl_future, export_unknown
+};
 
 void fatal(const char *fmt, ...)
 {
@@ -50,6 +55,17 @@ void warn(const char *fmt, ...)
        va_end(arglist);
 }
 
+void merror(const char *fmt, ...)
+{
+       va_list arglist;
+
+       fprintf(stderr, "ERROR: ");
+
+       va_start(arglist, fmt);
+       vfprintf(stderr, fmt, arglist);
+       va_end(arglist);
+}
+
 static int is_vmlinux(const char *modname)
 {
        const char *myname;
@@ -191,7 +207,9 @@ static struct {
        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 },
 };
@@ -205,6 +223,8 @@ static const char *export_str(enum export ex)
 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;
@@ -216,8 +236,12 @@ 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
@@ -320,22 +344,31 @@ void release_file(void *file, unsigned long size)
        munmap(file, size);
 }
 
-static void parse_elf(struct elf_info *info, const char *filename)
+static int parse_elf(struct elf_info *info, const char *filename)
 {
        unsigned int i;
-       Elf_Ehdr *hdr = info->hdr;
+       Elf_Ehdr *hdr;
        Elf_Shdr *sechdrs;
        Elf_Sym  *sym;
 
        hdr = grab_file(filename, &info->size);
        if (!hdr) {
                perror(filename);
-               abort();
+               exit(1);
        }
        info->hdr = hdr;
-       if (info->size < sizeof(*hdr))
-               goto truncated;
-
+       if (info->size < sizeof(*hdr)) {
+               /* file too small, assume this is an empty .o file */
+               return 0;
+       }
+       /* Is this a valid ELF file? */
+       if ((hdr->e_ident[EI_MAG0] != ELFMAG0) ||
+           (hdr->e_ident[EI_MAG1] != ELFMAG1) ||
+           (hdr->e_ident[EI_MAG2] != ELFMAG2) ||
+           (hdr->e_ident[EI_MAG3] != ELFMAG3)) {
+               /* Not an ELF file - silently ignore it */
+               return 0;
+       }
        /* Fix endianness in ELF header */
        hdr->e_shoff    = TO_NATIVE(hdr->e_shoff);
        hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx);
@@ -351,6 +384,7 @@ static void parse_elf(struct elf_info *info, const char *filename)
                sechdrs[i].sh_size   = TO_NATIVE(sechdrs[i].sh_size);
                sechdrs[i].sh_link   = TO_NATIVE(sechdrs[i].sh_link);
                sechdrs[i].sh_name   = TO_NATIVE(sechdrs[i].sh_name);
+               sechdrs[i].sh_info   = TO_NATIVE(sechdrs[i].sh_info);
        }
        /* Find symbol table. */
        for (i = 1; i < hdr->e_shnum; i++) {
@@ -358,16 +392,22 @@ static void parse_elf(struct elf_info *info, const char *filename)
                        = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
                const char *secname;
 
-               if (sechdrs[i].sh_offset > info->size)
-                       goto truncated;
+               if (sechdrs[i].sh_offset > info->size) {
+                       fatal("%s is truncated. sechdrs[i].sh_offset=%u > sizeof(*hrd)=%ul\n", filename, (unsigned int)sechdrs[i].sh_offset, sizeof(*hdr));
+                       return 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;
 
@@ -390,10 +430,7 @@ static void parse_elf(struct elf_info *info, const char *filename)
                sym->st_value = TO_NATIVE(sym->st_value);
                sym->st_size  = TO_NATIVE(sym->st_size);
        }
-       return;
-
- truncated:
-       fatal("%s is truncated.\n", filename);
+       return 1;
 }
 
 static void parse_elf_finish(struct elf_info *info)
@@ -546,6 +583,12 @@ static int strrcmp(const char *s, const char *sub)
 
 /**
  * Whitelist to allow certain references to pass with no warning.
+ *
+ * Pattern 0:
+ *   Do not warn if funtion/data are marked with __init_refok/__initdata_refok.
+ *   The pattern is identified by:
+ *   fromsec = .text.init.refok | .data.init.refok
+ *
  * Pattern 1:
  *   If a module parameter is declared __initdata and permissions=0
  *   then this is legal despite the warning generated.
@@ -564,10 +607,63 @@ static int strrcmp(const char *s, const char *sub)
  *   the pattern is identified by:
  *   tosec   = .init.text | .exit.text | .init.data
  *   fromsec = .data
- *   atsym = *driver, *_template, *_sht, *_ops, *_probe, *probe_one
+ *   atsym = *driver, *_template, *_sht, *_ops, *_probe, *probe_one, *_console
+ *
+ * Pattern 3:
+ *   Whitelist all references from .pci_fixup* section to .init.text
+ *   This is part of the PCI init when built-in
+ *
+ * Pattern 4:
+ *   Whitelist all refereces from .text.head to .init.data
+ *   Whitelist all refereces from .text.head to .init.text
+ *
+ * Pattern 5:
+ *   Some symbols belong to init section but still it is ok to reference
+ *   these from non-init sections as these symbols don't have any memory
+ *   allocated for them and symbol address and value are same. So even
+ *   if init section is freed, its ok to reference those symbols.
+ *   For ex. symbols marking the init section boundaries.
+ *   This pattern is identified by
+ *   refsymname = __init_begin, _sinittext, _einittext
+ *
+ * Pattern 6:
+ *   During the early init phase we have references from .init.text to
+ *   .text we have an intended section mismatch - do not warn about it.
+ *   See kernel_init() in init/main.c
+ *   tosec   = .init.text
+ *   fromsec = .text
+ *   atsym = kernel_init
+ *
+ * Pattern 7:
+ *  Logos used in drivers/video/logo reside in __initdata but the
+ *  funtion that references them are EXPORT_SYMBOL() so cannot be
+ *  marker __init. So we whitelist them here.
+ *  The pattern is:
+ *  tosec      = .init.data
+ *  fromsec    = .text*
+ *  refsymname = logo_
+ *
+ * Pattern 8:
+ *  Symbols contained in .paravirtprobe may safely reference .init.text.
+ *  The pattern is:
+ *  tosec   = .init.text
+ *  fromsec  = .paravirtprobe
+ *
+ * Pattern 9:
+ *  Some of functions are common code between boot time and hotplug
+ *  time. The bootmem allocater is called only boot time in its
+ *  functions. So it's ok to reference.
+ *  tosec    = .init.text
+ *
+ * Pattern 10:
+ *  ia64 has machvec table for each platform and
+ *  powerpc has a machine desc table for each platform.
+ *  It is mixture of function pointers of .init.text and .text.
+ *  fromsec  = .machvec | .machine.desc
  **/
-static int secref_whitelist(const char *tosec, const char *fromsec,
-                           const char *atsym)
+static int secref_whitelist(const char *modname, const char *tosec,
+                           const char *fromsec, const char *atsym,
+                           const char *refsymname)
 {
        int f1 = 1, f2 = 1;
        const char **s;
@@ -578,9 +674,29 @@ static int secref_whitelist(const char *tosec, const char *fromsec,
                "_ops",
                "_probe",
                "_probe_one",
+               "_console",
+               "apic_es7000",
                NULL
        };
 
+       const char *pat3refsym[] = {
+               "__init_begin",
+               "_sinittext",
+               "_einittext",
+               NULL
+       };
+
+       const char *pat4sym[] = {
+               "sparse_index_alloc",
+               "zone_wait_table_init",
+               NULL
+       };
+
+       /* Check for pattern 0 */
+       if ((strcmp(fromsec, ".text.init.refok") == 0) ||
+           (strcmp(fromsec, ".data.init.refok") == 0))
+               return 1;
+
        /* Check for pattern 1 */
        if (strcmp(tosec, ".init.data") != 0)
                f1 = 0;
@@ -603,8 +719,55 @@ static int secref_whitelist(const char *tosec, const char *fromsec,
        for (s = pat2sym; *s; s++)
                if (strrcmp(atsym, *s) == 0)
                        f1 = 1;
+       if (f1 && f2)
+               return 1;
+
+       /* Check for pattern 3 */
+       if ((strncmp(fromsec, ".pci_fixup", strlen(".pci_fixup")) == 0) &&
+           (strcmp(tosec, ".init.text") == 0))
+       return 1;
+
+       /* Check for pattern 4 */
+       if ((strcmp(fromsec, ".text.head") == 0) &&
+               ((strcmp(tosec, ".init.data") == 0) ||
+               (strcmp(tosec, ".init.text") == 0)))
+       return 1;
+
+       /* Check for pattern 5 */
+       for (s = pat3refsym; *s; s++)
+               if (strcmp(refsymname, *s) == 0)
+                       return 1;
+
+       /* Check for pattern 6 */
+       if ((strcmp(tosec, ".init.text") == 0) &&
+           (strcmp(fromsec, ".text") == 0) &&
+           (strcmp(refsymname, "kernel_init") == 0))
+               return 1;
+
+       /* Check for pattern 7 */
+       if ((strcmp(tosec, ".init.data") == 0) &&
+           (strncmp(fromsec, ".text", strlen(".text")) == 0) &&
+           (strncmp(refsymname, "logo_", strlen("logo_")) == 0))
+               return 1;
 
-       return f1 && f2;
+       /* Check for pattern 8 */
+       if ((strcmp(tosec, ".init.text") == 0) &&
+           (strcmp(fromsec, ".paravirtprobe") == 0))
+               return 1;
+
+       /* Check for pattern 9 */
+       if ((strcmp(tosec, ".init.text") == 0) &&
+           (strcmp(fromsec, ".text") == 0))
+               for (s = pat4sym; *s; s++)
+                       if (strcmp(atsym, *s) == 0)
+                               return 1;
+
+       /* Check for pattern 10 */
+       if ((strcmp(fromsec, ".machvec") == 0) ||
+           (strcmp(fromsec, ".machine.desc") == 0))
+               return 1;
+
+       return 0;
 }
 
 /**
@@ -624,12 +787,38 @@ static Elf_Sym *find_elf_symbol(struct elf_info *elf, Elf_Addr addr,
        for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
                if (sym->st_shndx != relsym->st_shndx)
                        continue;
+               if (ELF_ST_TYPE(sym->st_info) == STT_SECTION)
+                       continue;
                if (sym->st_value == addr)
                        return sym;
        }
        return NULL;
 }
 
+static inline int is_arm_mapping_symbol(const char *str)
+{
+       return str[0] == '$' && strchr("atd", str[1])
+              && (str[2] == '\0' || str[2] == '.');
+}
+
+/*
+ * If there's no name there, ignore it; likewise, ignore it if it's
+ * one of the magic symbols emitted used by current ARM tools.
+ *
+ * Otherwise if find_symbols_between() returns those symbols, they'll
+ * fail the whitelist tests and cause lots of false alarms ... fixable
+ * only by merging __exit and __init sections into __text, bloating
+ * the kernel (which is especially evil on embedded platforms).
+ */
+static inline int is_valid_name(struct elf_info *elf, Elf_Sym *sym)
+{
+       const char *name = elf->strtab + sym->st_name;
+
+       if (!name || !strlen(name))
+               return 0;
+       return !is_arm_mapping_symbol(name);
+}
+
 /*
  * Find symbols before or equal addr and after addr - in the section sec.
  * If we find two symbols with equal offset prefer one with a valid name.
@@ -658,16 +847,15 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr,
                symsec = secstrings + elf->sechdrs[sym->st_shndx].sh_name;
                if (strcmp(symsec, sec) != 0)
                        continue;
+               if (!is_valid_name(elf, sym))
+                       continue;
                if (sym->st_value <= addr) {
                        if ((addr - sym->st_value) < beforediff) {
                                beforediff = addr - sym->st_value;
                                *before = sym;
                        }
                        else if ((addr - sym->st_value) == beforediff) {
-                               /* equal offset, valid name? */
-                               const char *name = elf->strtab + sym->st_name;
-                               if (name && strlen(name))
-                                       *before = sym;
+                               *before = sym;
                        }
                }
                else
@@ -677,10 +865,7 @@ static void find_symbols_between(struct elf_info *elf, Elf_Addr addr,
                                *after = sym;
                        }
                        else if ((sym->st_value - addr) == afterdiff) {
-                               /* equal offset, valid name? */
-                               const char *name = elf->strtab + sym->st_name;
-                               if (name && strlen(name))
-                                       *after = sym;
+                               *after = sym;
                        }
                }
        }
@@ -711,36 +896,103 @@ static void warn_sec_mismatch(const char *modname, const char *fromsec,
 
        /* check whitelist - we may ignore it */
        if (before &&
-           secref_whitelist(secname, fromsec, elf->strtab + before->st_name))
+           secref_whitelist(modname, secname, fromsec,
+                            elf->strtab + before->st_name, refsymname))
+               return;
+
+       /* fromsec whitelist - without a valid 'before'
+        * powerpc has a GOT table in .got2 section */
+       if (strcmp(fromsec, ".got2") == 0)
                return;
 
        if (before && after) {
-               warn("%s - Section mismatch: reference to %s:%s from %s "
-                    "between '%s' (at offset 0x%llx) and '%s'\n",
-                    modname, secname, refsymname, fromsec,
+               warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s "
+                    "(between '%s' and '%s')\n",
+                    modname, fromsec, (unsigned long long)r.r_offset,
+                    secname, refsymname,
                     elf->strtab + before->st_name,
-                    (long long)r.r_offset,
                     elf->strtab + after->st_name);
        } else if (before) {
-               warn("%s - Section mismatch: reference to %s:%s from %s "
-                    "after '%s' (at offset 0x%llx)\n",
-                    modname, secname, refsymname, fromsec,
-                    elf->strtab + before->st_name,
-                    (long long)r.r_offset);
+               warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s "
+                    "(after '%s')\n",
+                    modname, fromsec, (unsigned long long)r.r_offset,
+                    secname, refsymname,
+                    elf->strtab + before->st_name);
        } else if (after) {
-               warn("%s - Section mismatch: reference to %s:%s from %s "
+               warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s "
                     "before '%s' (at offset -0x%llx)\n",
-                    modname, secname, refsymname, fromsec,
-                    elf->strtab + after->st_name,
-                    (long long)r.r_offset);
+                    modname, fromsec, (unsigned long long)r.r_offset,
+                    secname, refsymname,
+                    elf->strtab + after->st_name);
        } else {
-               warn("%s - Section mismatch: reference to %s:%s from %s "
-                    "(offset 0x%llx)\n",
-                    modname, secname, fromsec, refsymname,
-                    (long long)r.r_offset);
+               warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s\n",
+                    modname, fromsec, (unsigned long long)r.r_offset,
+                    secname, refsymname);
+       }
+}
+
+static void addend_386_rel(struct elf_info *elf, int section, Elf_Rela *r)
+{
+       Elf_Shdr *sechdrs = elf->sechdrs;
+       unsigned int r_typ;
+       unsigned int *location;
+
+       r_typ = ELF_R_TYPE(r->r_info);
+       location = (void *)elf->hdr +
+               sechdrs[sechdrs[section].sh_info].sh_offset + r->r_offset;
+       switch (r_typ) {
+       case R_386_32:
+               r->r_addend = TO_NATIVE(*location);
+               break;
+       case R_386_PC32:
+               r->r_addend = TO_NATIVE(*location) + 4;
+               break;
+       }
+}
+
+static void addend_arm_rel(struct elf_info *elf, int section, Elf_Rela *r)
+{
+       Elf_Shdr *sechdrs = elf->sechdrs;
+       unsigned int r_typ;
+       unsigned int *location;
+
+       r_typ = ELF_R_TYPE(r->r_info);
+       location = (void *)elf->hdr +
+               sechdrs[sechdrs[section].sh_info].sh_offset + r->r_offset;
+       switch (r_typ) {
+       case R_ARM_ABS32:
+               r->r_addend = TO_NATIVE(*location);
+               break;
+       case R_ARM_PC24:
+               r->r_addend = ((TO_NATIVE(*location) & 0x00ffffff) << 2) + 8;
+               break;
        }
 }
 
+static int addend_mips_rel(struct elf_info *elf, int section, Elf_Rela *r)
+{
+       Elf_Shdr *sechdrs = elf->sechdrs;
+       unsigned int r_typ;
+       unsigned int *location;
+       unsigned int inst;
+
+       r_typ = ELF_R_TYPE(r->r_info);
+       if (r_typ == R_MIPS_HI16)
+               return 1;       /* skip this */
+       location = (void *)elf->hdr +
+               sechdrs[sechdrs[section].sh_info].sh_offset + r->r_offset;
+       inst = TO_NATIVE(*location);
+       switch (r_typ) {
+       case R_MIPS_LO16:
+               r->r_addend = ((inst & 0xffff) ^ 0x8000) - 0x8000;
+               break;
+       case R_MIPS_26:
+               r->r_addend = (inst & 0x03ffffff) << 2;
+               break;
+       }
+       return 0;
+}
+
 /**
  * A module includes a number of sections that are discarded
  * either when loaded or when used as built-in.
@@ -784,8 +1036,11 @@ static void check_sec_ref(struct module *mod, const char *modname,
                                r.r_offset = TO_NATIVE(rela->r_offset);
 #if KERNEL_ELFCLASS == ELFCLASS64
                                if (hdr->e_machine == EM_MIPS) {
+                                       unsigned int r_typ;
                                        r_sym = ELF64_MIPS_R_SYM(rela->r_info);
                                        r_sym = TO_NATIVE(r_sym);
+                                       r_typ = ELF64_MIPS_R_TYPE(rela->r_info);
+                                       r.r_info = ELF64_R_INFO(r_sym, r_typ);
                                } else {
                                        r.r_info = TO_NATIVE(rela->r_info);
                                        r_sym = ELF_R_SYM(r.r_info);
@@ -818,8 +1073,11 @@ static void check_sec_ref(struct module *mod, const char *modname,
                                r.r_offset = TO_NATIVE(rel->r_offset);
 #if KERNEL_ELFCLASS == ELFCLASS64
                                if (hdr->e_machine == EM_MIPS) {
+                                       unsigned int r_typ;
                                        r_sym = ELF64_MIPS_R_SYM(rel->r_info);
                                        r_sym = TO_NATIVE(r_sym);
+                                       r_typ = ELF64_MIPS_R_TYPE(rel->r_info);
+                                       r.r_info = ELF64_R_INFO(r_sym, r_typ);
                                } else {
                                        r.r_info = TO_NATIVE(rel->r_info);
                                        r_sym = ELF_R_SYM(r.r_info);
@@ -829,6 +1087,14 @@ static void check_sec_ref(struct module *mod, const char *modname,
                                r_sym = ELF_R_SYM(r.r_info);
 #endif
                                r.r_addend = 0;
+                               if (hdr->e_machine == EM_386)
+                                       addend_386_rel(elf, i, &r);
+                               else if (hdr->e_machine == EM_ARM)
+                                       addend_arm_rel(elf, i, &r);
+                               else if (hdr->e_machine == EM_MIPS) {
+                                       if (addend_mips_rel(elf, i, &r))
+                                               continue;
+                               }
                                sym = elf->symtab_start + r_sym;
                                /* Skip special sections */
                                if (sym->st_shndx >= SHN_LORESERVE)
@@ -884,7 +1150,8 @@ static int init_section_ref_ok(const char *name)
                ".opd",   /* see comment [OPD] at exit_section_ref_ok() */
                ".toc1",  /* used by ppc64 */
                ".stab",
-               ".rodata",
+               ".data.rel.ro", /* used by parisc64 */
+               ".parainstructions",
                ".text.lock",
                "__bug_table", /* used by powerpc for BUG() */
                ".pci_fixup_header",
@@ -895,6 +1162,8 @@ static int init_section_ref_ok(const char *name)
                ".fixup",
                ".smp_locks",
                ".plt",  /* seen on ARCH=um build on x86_64. Harmless */
+               "__ftr_fixup",          /* powerpc cpu feature fixup */
+               "__fw_ftr_fixup",       /* powerpc firmware feature fixup */
                NULL
        };
        /* Start of section names */
@@ -903,6 +1172,8 @@ static int init_section_ref_ok(const char *name)
                ".altinstructions",
                ".eh_frame",
                ".debug",
+               ".parainstructions",
+               ".rodata",
                NULL
        };
        /* part of section name */
@@ -967,6 +1238,7 @@ static int exit_section_ref_ok(const char *name)
                "__bug_table", /* used by powerpc for BUG() */
                ".exitcall.exit",
                ".eh_frame",
+               ".parainstructions",
                ".stab",
                "__ex_table",
                ".fixup",
@@ -1006,7 +1278,8 @@ static void read_symbols(char *modname)
        struct elf_info info = { };
        Elf_Sym *sym;
 
-       parse_elf(&info, modname);
+       if (!parse_elf(&info, modname))
+               return;
 
        mod = new_module(modname);
 
@@ -1085,38 +1358,64 @@ void buf_write(struct buffer *buf, const char *s, int len)
        buf->pos += len;
 }
 
-void check_license(struct module *mod)
+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;
-               if (mod->gpl_compatible == 1) {
-                       /* GPL-compatible modules may use all symbols */
-                       continue;
-               }
                exp = find_symbol(s->name);
                if (!exp || exp->module == mod)
                        continue;
                basename = strrchr(mod->name, '/');
                if (basename)
                        basename++;
-               switch (exp->export) {
-                       case export_gpl:
-                               fatal("modpost: GPL-incompatible module %s "
-                                     "uses GPL-only symbol '%s'\n",
-                                basename ? basename : mod->name,
-                               exp->name);
-                               break;
-                       case export_gpl_future:
-                               warn("modpost: GPL-incompatible module %s "
-                                     "uses future GPL-only symbol '%s'\n",
-                                     basename ? basename : mod->name,
-                                     exp->name);
-                               break;
-                       case export_plain: /* ignore */ break;
-                       case export_unknown: /* ignore */ break;
-               }
+               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);
         }
 }
 
@@ -1140,22 +1439,31 @@ static void add_header(struct buffer *b, struct module *mod)
                buf_printf(b, "#ifdef CONFIG_MODULE_UNLOAD\n"
                              " .exit = cleanup_module,\n"
                              "#endif\n");
+       buf_printf(b, " .arch = MODULE_ARCH_INIT,\n");
        buf_printf(b, "};\n");
 }
 
 /**
  * Record CRCs for unresolved symbols
  **/
-static void add_versions(struct buffer *b, struct module *mod)
+static int add_versions(struct buffer *b, struct module *mod)
 {
        struct symbol *s, *exp;
+       int err = 0;
 
        for (s = mod->unres; s; s = s->next) {
                exp = find_symbol(s->name);
                if (!exp || exp->module == mod) {
-                       if (have_vmlinux && !s->weak)
-                               warn("\"%s\" [%s.ko] undefined!\n",
-                                    s->name, mod->name);
+                       if (have_vmlinux && !s->weak) {
+                               if (warn_unresolved) {
+                                       warn("\"%s\" [%s.ko] undefined!\n",
+                                            s->name, mod->name);
+                               } else {
+                                       merror("\"%s\" [%s.ko] undefined!\n",
+                                                 s->name, mod->name);
+                                       err = 1;
+                               }
+                       }
                        continue;
                }
                s->module = exp->module;
@@ -1164,7 +1472,7 @@ static void add_versions(struct buffer *b, struct module *mod)
        }
 
        if (!modversions)
-               return;
+               return err;
 
        buf_printf(b, "\n");
        buf_printf(b, "static const struct modversion_info ____versions[]\n");
@@ -1184,6 +1492,8 @@ static void add_versions(struct buffer *b, struct module *mod)
        }
 
        buf_printf(b, "};\n");
+
+       return err;
 }
 
 static void add_depends(struct buffer *b, struct module *mod,
@@ -1203,6 +1513,7 @@ static void add_depends(struct buffer *b, struct module *mod,
        buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n");
        buf_printf(b, "\"depends=");
        for (s = mod->unres; s; s = s->next) {
+               const char *p;
                if (!s->module)
                        continue;
 
@@ -1210,8 +1521,11 @@ static void add_depends(struct buffer *b, struct module *mod,
                        continue;
 
                s->module->seen = 1;
-               buf_printf(b, "%s%s", first ? "" : ",",
-                          strrchr(s->module->name, '/') + 1);
+               if ((p = strrchr(s->module->name, '/')) != NULL)
+                       p++;
+               else
+                       p = s->module->name;
+               buf_printf(b, "%s%s", first ? "" : ",", p);
                first = 0;
        }
        buf_printf(b, "\";\n");
@@ -1271,7 +1585,7 @@ static void write_if_changed(struct buffer *b, const char *fname)
 }
 
 /* parse Module.symvers file. line format:
- * 0x12345678<tab>symbol<tab>module[<tab>export]
+ * 0x12345678<tab>symbol<tab>module[[<tab>export]<tab>something]
  **/
 static void read_dump(const char *fname, unsigned int kernel)
 {
@@ -1284,7 +1598,7 @@ static void read_dump(const char *fname, unsigned int kernel)
                return;
 
        while ((line = get_next_line(&pos, file, size))) {
-               char *symname, *modname, *d, *export;
+               char *symname, *modname, *d, *export, *end;
                unsigned int crc;
                struct module *mod;
                struct symbol *s;
@@ -1297,7 +1611,8 @@ static void read_dump(const char *fname, unsigned int kernel)
                *modname++ = '\0';
                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;
@@ -1360,8 +1675,9 @@ int main(int argc, char **argv)
        char *kernel_read = NULL, *module_read = NULL;
        char *dump_write = NULL;
        int opt;
+       int err;
 
-       while ((opt = getopt(argc, argv, "i:I:mo:a")) != -1) {
+       while ((opt = getopt(argc, argv, "i:I:mo:aw")) != -1) {
                switch(opt) {
                        case 'i':
                                kernel_read = optarg;
@@ -1379,6 +1695,9 @@ int main(int argc, char **argv)
                        case 'a':
                                all_versions = 1;
                                break;
+                       case 'w':
+                               warn_unresolved = 1;
+                               break;
                        default:
                                exit(1);
                }
@@ -1396,9 +1715,11 @@ int main(int argc, char **argv)
        for (mod = modules; mod; mod = mod->next) {
                if (mod->skip)
                        continue;
-               check_license(mod);
+               check_exports(mod);
        }
 
+       err = 0;
+
        for (mod = modules; mod; mod = mod->next) {
                if (mod->skip)
                        continue;
@@ -1406,7 +1727,7 @@ int main(int argc, char **argv)
                buf.pos = 0;
 
                add_header(&buf, mod);
-               add_versions(&buf, mod);
+               err |= add_versions(&buf, mod);
                add_depends(&buf, mod, modules);
                add_moddevtable(&buf, mod);
                add_srcversion(&buf, mod);
@@ -1418,5 +1739,5 @@ int main(int argc, char **argv)
        if (dump_write)
                write_dump(dump_write);
 
-       return 0;
+       return err;
 }