kbuild: fix NULL dereference in scripts/mod/modpost.c
[pandora-kernel.git] / scripts / mod / modpost.c
index f70ff13..7e8079a 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright 2003       Kai Germaschewski
  * Copyright 2002-2004  Rusty Russell, IBM Corporation
- *
+ * Copyright 2006       Sam Ravnborg
  * Based in part on module-init-tools/depmod.c,file2alias
  *
  * This software may be used and distributed according to the terms
@@ -20,9 +20,10 @@ int modversions = 0;
 int have_vmlinux = 0;
 /* Is CONFIG_MODULE_SRCVERSION_ALL set? */
 static int all_versions = 0;
+/* If we are modposting external module set to 1 */
+static int external_module = 0;
 
-void
-fatal(const char *fmt, ...)
+void fatal(const char *fmt, ...)
 {
        va_list arglist;
 
@@ -35,8 +36,7 @@ fatal(const char *fmt, ...)
        exit(1);
 }
 
-void
-warn(const char *fmt, ...)
+void warn(const char *fmt, ...)
 {
        va_list arglist;
 
@@ -47,6 +47,18 @@ warn(const char *fmt, ...)
        va_end(arglist);
 }
 
+static int is_vmlinux(const char *modname)
+{
+       const char *myname;
+
+       if ((myname = strrchr(modname, '/')))
+               myname++;
+       else
+               myname = modname;
+
+       return strcmp(myname, "vmlinux") == 0;
+}
+
 void *do_nofail(void *ptr, const char *expr)
 {
        if (!ptr) {
@@ -59,8 +71,7 @@ void *do_nofail(void *ptr, const char *expr)
 
 static struct module *modules;
 
-struct module *
-find_module(char *modname)
+static struct module *find_module(char *modname)
 {
        struct module *mod;
 
@@ -70,12 +81,11 @@ find_module(char *modname)
        return mod;
 }
 
-struct module *
-new_module(char *modname)
+static struct module *new_module(char *modname)
 {
        struct module *mod;
        char *p, *s;
-       
+
        mod = NOFAIL(malloc(sizeof(*mod)));
        memset(mod, 0, sizeof(*mod));
        p = NOFAIL(strdup(modname));
@@ -104,6 +114,10 @@ struct symbol {
        unsigned int crc;
        int crc_valid;
        unsigned int weak:1;
+       unsigned int vmlinux:1;    /* 1 if symbol is defined in vmlinux */
+       unsigned int kernel:1;     /* 1 if symbol is from kernel
+                                   *  (only for external modules) **/
+       unsigned int preloaded:1;  /* 1 if symbol from Module.symvers */
        char name[0];
 };
 
@@ -122,11 +136,12 @@ static inline unsigned int tdb_hash(const char *name)
        return (1103515243 * value + 12345);
 }
 
-/* Allocate a new symbols for use in the hash of exported symbols or
- * the list of unresolved symbols per module */
-
-struct symbol *
-alloc_symbol(const char *name, unsigned int weak, struct symbol *next)
+/**
+ * Allocate a new symbols for use in the hash of exported symbols or
+ * the list of unresolved symbols per module
+ **/
+static struct symbol *alloc_symbol(const char *name, unsigned int weak,
+                                  struct symbol *next)
 {
        struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
 
@@ -138,9 +153,7 @@ alloc_symbol(const char *name, unsigned int weak, struct symbol *next)
 }
 
 /* For the hash of exported symbols */
-
-void
-new_symbol(const char *name, struct module *module, unsigned int *crc)
+static struct symbol *new_symbol(const char *name, struct module *module)
 {
        unsigned int hash;
        struct symbol *new;
@@ -148,14 +161,10 @@ new_symbol(const char *name, struct module *module, unsigned int *crc)
        hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
        new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]);
        new->module = module;
-       if (crc) {
-               new->crc = *crc;
-               new->crc_valid = 1;
-       }
+       return new;
 }
 
-struct symbol *
-find_symbol(const char *name)
+static struct symbol *find_symbol(const char *name)
 {
        struct symbol *s;
 
@@ -170,25 +179,42 @@ find_symbol(const char *name)
        return NULL;
 }
 
-/* Add an exported symbol - it may have already been added without a
- * CRC, in this case just update the CRC */
-void
-add_exported_symbol(const char *name, struct module *module, unsigned int *crc)
+/**
+ * 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)
 {
        struct symbol *s = find_symbol(name);
 
        if (!s) {
-               new_symbol(name, module, crc);
-               return;
-       }
-       if (crc) {
-               s->crc = *crc;
-               s->crc_valid = 1;
+               s = new_symbol(name, mod);
+       } else {
+               if (!s->preloaded) {
+                       warn("%s: '%s' exported twice. Previous export "
+                            "was in %s%s\n", mod->name, name,
+                            s->module->name,
+                            is_vmlinux(s->module->name) ?"":".ko");
+               }
        }
+       s->preloaded = 0;
+       s->vmlinux   = is_vmlinux(mod->name);
+       s->kernel    = 0;
+       return s;
 }
 
-void *
-grab_file(const char *filename, unsigned long *size)
+static void sym_update_crc(const char *name, struct module *mod,
+                          unsigned int crc)
+{
+       struct symbol *s = find_symbol(name);
+
+       if (!s)
+               s = new_symbol(name, mod);
+       s->crc = crc;
+       s->crc_valid = 1;
+}
+
+void *grab_file(const char *filename, unsigned long *size)
 {
        struct stat st;
        void *map;
@@ -207,13 +233,12 @@ grab_file(const char *filename, unsigned long *size)
        return map;
 }
 
-/*
-   Return a copy of the next line in a mmap'ed file.
-   spaces in the beginning of the line is trimmed away.
-   Return a pointer to a static buffer.
-*/
-char*
-get_next_line(unsigned long *pos, void *file, unsigned long size)
+/**
+  * Return a copy of the next line in a mmap'ed file.
+  * spaces in the beginning of the line is trimmed away.
+  * Return a pointer to a static buffer.
+  **/
+char* get_next_line(unsigned long *pos, void *file, unsigned long size)
 {
        static char line[4096];
        int skip = 1;
@@ -243,14 +268,12 @@ get_next_line(unsigned long *pos, void *file, unsigned long size)
        return NULL;
 }
 
-void
-release_file(void *file, unsigned long size)
+void release_file(void *file, unsigned long size)
 {
        munmap(file, size);
 }
 
-void
-parse_elf(struct elf_info *info, const char *filename)
+static void parse_elf(struct elf_info *info, const char *filename)
 {
        unsigned int i;
        Elf_Ehdr *hdr = info->hdr;
@@ -297,14 +320,13 @@ parse_elf(struct elf_info *info, const char *filename)
                        continue;
 
                info->symtab_start = (void *)hdr + sechdrs[i].sh_offset;
-               info->symtab_stop  = (void *)hdr + sechdrs[i].sh_offset 
+               info->symtab_stop  = (void *)hdr + sechdrs[i].sh_offset
                                                 + sechdrs[i].sh_size;
-               info->strtab       = (void *)hdr + 
+               info->strtab       = (void *)hdr +
                                     sechdrs[sechdrs[i].sh_link].sh_offset;
        }
        if (!info->symtab_start) {
-               fprintf(stderr, "modpost: %s no symtab?\n", filename);
-               abort();
+               fatal("%s has no symtab?\n", filename);
        }
        /* Fix endianness in symbols */
        for (sym = info->symtab_start; sym < info->symtab_stop; sym++) {
@@ -316,36 +338,31 @@ parse_elf(struct elf_info *info, const char *filename)
        return;
 
  truncated:
-       fprintf(stderr, "modpost: %s is truncated.\n", filename);
-       abort();
+       fatal("%s is truncated.\n", filename);
 }
 
-void
-parse_elf_finish(struct elf_info *info)
+static void parse_elf_finish(struct elf_info *info)
 {
        release_file(info->hdr, info->size);
 }
 
-#define CRC_PFX     "__crc_"
-#define KSYMTAB_PFX "__ksymtab_"
+#define CRC_PFX     MODULE_SYMBOL_PREFIX "__crc_"
+#define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_"
 
-void
-handle_modversions(struct module *mod, struct elf_info *info,
-                  Elf_Sym *sym, const char *symname)
+static void handle_modversions(struct module *mod, struct elf_info *info,
+                              Elf_Sym *sym, const char *symname)
 {
        unsigned int crc;
 
        switch (sym->st_shndx) {
        case SHN_COMMON:
-               fprintf(stderr, "*** Warning: \"%s\" [%s] is COMMON symbol\n",
-                       symname, mod->name);
+               warn("\"%s\" [%s] is COMMON symbol\n", symname, mod->name);
                break;
        case SHN_ABS:
                /* CRC'd symbol */
                if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) {
                        crc = (unsigned int) sym->st_value;
-                       add_exported_symbol(symname + strlen(CRC_PFX),
-                                           mod, &crc);
+                       sym_update_crc(symname + strlen(CRC_PFX), mod, crc);
                }
                break;
        case SHN_UNDEF:
@@ -370,15 +387,15 @@ handle_modversions(struct module *mod, struct elf_info *info,
                        /* Ignore register directives. */
                        if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER)
                                break;
-                       if (symname[0] == '.') {
-                               char *munged = strdup(symname);
-                               munged[0] = '_';
-                               munged[1] = toupper(munged[1]);
-                               symname = munged;
-                       }
+                       if (symname[0] == '.') {
+                               char *munged = strdup(symname);
+                               munged[0] = '_';
+                               munged[1] = toupper(munged[1]);
+                               symname = munged;
+                       }
                }
 #endif
-               
+
                if (memcmp(symname, MODULE_SYMBOL_PREFIX,
                           strlen(MODULE_SYMBOL_PREFIX)) == 0)
                        mod->unres = alloc_symbol(symname +
@@ -389,8 +406,7 @@ handle_modversions(struct module *mod, struct elf_info *info,
        default:
                /* All exported symbols */
                if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) {
-                       add_exported_symbol(symname + strlen(KSYMTAB_PFX),
-                                           mod, NULL);
+                       sym_add_exported(symname + strlen(KSYMTAB_PFX), mod);
                }
                if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0)
                        mod->has_init = 1;
@@ -400,20 +416,9 @@ handle_modversions(struct module *mod, struct elf_info *info,
        }
 }
 
-int
-is_vmlinux(const char *modname)
-{
-       const char *myname;
-
-       if ((myname = strrchr(modname, '/')))
-               myname++;
-       else
-               myname = modname;
-
-       return strcmp(myname, "vmlinux") == 0;
-}
-
-/* Parse tag=value strings from .modinfo section */
+/**
+ * Parse tag=value strings from .modinfo section
+ **/
 static char *next_string(char *string, unsigned long *secsize)
 {
        /* Skip non-zero chars */
@@ -446,8 +451,418 @@ static char *get_modinfo(void *modinfo, unsigned long modinfo_len,
        return NULL;
 }
 
-void
-read_symbols(char *modname)
+/**
+ * Test if string s ends in string sub
+ * return 0 if match
+ **/
+static int strrcmp(const char *s, const char *sub)
+{
+        int slen, sublen;
+
+       if (!s || !sub)
+               return 1;
+
+       slen = strlen(s);
+        sublen = strlen(sub);
+
+       if ((slen == 0) || (sublen == 0))
+               return 1;
+
+        if (sublen > slen)
+                return 1;
+
+        return memcmp(s + slen - sublen, sub, sublen);
+}
+
+/**
+ * Whitelist to allow certain references to pass with no warning.
+ * Pattern 1:
+ *   If a module parameter is declared __initdata and permissions=0
+ *   then this is legal despite the warning generated.
+ *   We cannot see value of permissions here, so just ignore
+ *   this pattern.
+ *   The pattern is identified by:
+ *   tosec   = .init.data
+ *   fromsec = .data*
+ *   atsym   =__param*
+ *
+ * Pattern 2:
+ *   Many drivers utilise a *_driver container with references to
+ *   add, remove, probe functions etc.
+ *   These functions may often be marked __init and we do not want to
+ *   warn here.
+ *   the pattern is identified by:
+ *   tosec   = .init.text | .exit.text
+ *   fromsec = .data
+ *   atsym = *_driver, *_ops, *_probe, *probe_one
+ **/
+static int secref_whitelist(const char *tosec, const char *fromsec,
+                         const char *atsym)
+{
+       int f1 = 1, f2 = 1;
+       const char **s;
+       const char *pat2sym[] = {
+               "_driver",
+               "_ops",
+               "_probe",
+               "_probe_one",
+               NULL
+       };
+
+       /* Check for pattern 1 */
+       if (strcmp(tosec, ".init.data") != 0)
+               f1 = 0;
+       if (strncmp(fromsec, ".data", strlen(".data")) != 0)
+               f1 = 0;
+       if (strncmp(atsym, "__param", strlen("__param")) != 0)
+               f1 = 0;
+
+       if (f1)
+               return f1;
+
+       /* Check for pattern 2 */
+       if ((strcmp(tosec, ".init.text") != 0) &&
+           (strcmp(tosec, ".exit.text") != 0))
+               f2 = 0;
+       if (strcmp(fromsec, ".data") != 0)
+               f2 = 0;
+
+       for (s = pat2sym; *s; s++)
+               if (strrcmp(atsym, *s) == 0)
+                       f1 = 1;
+
+       return f1 && f2;
+}
+
+/**
+ * Find symbol based on relocation record info.
+ * In some cases the symbol supplied is a valid symbol so
+ * return refsym. If st_name != 0 we assume this is a valid symbol.
+ * In other cases the symbol needs to be looked up in the symbol table
+ * based on section and address.
+ *  **/
+static Elf_Sym *find_elf_symbol(struct elf_info *elf, Elf_Addr addr,
+                               Elf_Sym *relsym)
+{
+       Elf_Sym *sym;
+
+       if (relsym->st_name != 0)
+               return relsym;
+       for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
+               if (sym->st_shndx != relsym->st_shndx)
+                       continue;
+               if (sym->st_value == addr)
+                       return sym;
+       }
+       return NULL;
+}
+
+/*
+ * 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.
+ * The ELF format may have a better way to detect what type of symbol
+ * it is, but this works for now.
+ **/
+static void find_symbols_between(struct elf_info *elf, Elf_Addr addr,
+                                const char *sec,
+                                Elf_Sym **before, Elf_Sym **after)
+{
+       Elf_Sym *sym;
+       Elf_Ehdr *hdr = elf->hdr;
+       Elf_Addr beforediff = ~0;
+       Elf_Addr afterdiff = ~0;
+       const char *secstrings = (void *)hdr +
+                                elf->sechdrs[hdr->e_shstrndx].sh_offset;
+
+       *before = NULL;
+       *after = NULL;
+
+       for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
+               const char *symsec;
+
+               if (sym->st_shndx >= SHN_LORESERVE)
+                       continue;
+               symsec = secstrings + elf->sechdrs[sym->st_shndx].sh_name;
+               if (strcmp(symsec, sec) != 0)
+                       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;
+                       }
+               }
+               else
+               {
+                       if ((sym->st_value - addr) < afterdiff) {
+                               afterdiff = sym->st_value - 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;
+                       }
+               }
+       }
+}
+
+/**
+ * Print a warning about a section mismatch.
+ * Try to find symbols near it so user can find it.
+ * Check whitelist before warning - it may be a false positive.
+ **/
+static void warn_sec_mismatch(const char *modname, const char *fromsec,
+                             struct elf_info *elf, Elf_Sym *sym, Elf_Rela r)
+{
+       const char *refsymname = "";
+       Elf_Sym *before, *after;
+       Elf_Sym *refsym;
+       Elf_Ehdr *hdr = elf->hdr;
+       Elf_Shdr *sechdrs = elf->sechdrs;
+       const char *secstrings = (void *)hdr +
+                                sechdrs[hdr->e_shstrndx].sh_offset;
+       const char *secname = secstrings + sechdrs[sym->st_shndx].sh_name;
+
+       find_symbols_between(elf, r.r_offset, fromsec, &before, &after);
+
+       refsym = find_elf_symbol(elf, r.r_addend, sym);
+       if (refsym && strlen(elf->strtab + refsym->st_name))
+               refsymname = elf->strtab + refsym->st_name;
+
+       /* check whitelist - we may ignore it */
+       if (before &&
+           secref_whitelist(secname, fromsec, elf->strtab + before->st_name))
+               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,
+                    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);
+       } else if (after) {
+               warn("%s - Section mismatch: reference to %s:%s from %s "
+                    "before '%s' (at offset -0x%llx)\n",
+                    modname, secname, refsymname, fromsec,
+                    elf->strtab + after->st_name,
+                    (long long)r.r_offset);
+       } else {
+               warn("%s - Section mismatch: reference to %s:%s from %s "
+                    "(offset 0x%llx)\n",
+                    modname, secname, fromsec, refsymname,
+                    (long long)r.r_offset);
+       }
+}
+
+/**
+ * A module includes a number of sections that are discarded
+ * either when loaded or when used as built-in.
+ * For loaded modules all functions marked __init and all data
+ * marked __initdata will be discarded when the module has been intialized.
+ * Likewise for modules used built-in the sections marked __exit
+ * are discarded because __exit marked function are supposed to be called
+ * only when a moduel is unloaded which never happes for built-in modules.
+ * The check_sec_ref() function traverses all relocation records
+ * to find all references to a section that reference a section that will
+ * be discarded and warns about it.
+ **/
+static void check_sec_ref(struct module *mod, const char *modname,
+                         struct elf_info *elf,
+                         int section(const char*),
+                         int section_ref_ok(const char *))
+{
+       int i;
+       Elf_Sym  *sym;
+       Elf_Ehdr *hdr = elf->hdr;
+       Elf_Shdr *sechdrs = elf->sechdrs;
+       const char *secstrings = (void *)hdr +
+                                sechdrs[hdr->e_shstrndx].sh_offset;
+
+       /* Walk through all sections */
+       for (i = 0; i < hdr->e_shnum; i++) {
+               Elf_Rela *rela;
+               Elf_Rela *start = (void *)hdr + sechdrs[i].sh_offset;
+               Elf_Rela *stop  = (void*)start + sechdrs[i].sh_size;
+               const char *name = secstrings + sechdrs[i].sh_name +
+                                               strlen(".rela");
+               /* We want to process only relocation sections and not .init */
+               if (section_ref_ok(name) || (sechdrs[i].sh_type != SHT_RELA))
+                       continue;
+
+               for (rela = start; rela < stop; rela++) {
+                       Elf_Rela r;
+                       const char *secname;
+                       r.r_offset = TO_NATIVE(rela->r_offset);
+                       r.r_info   = TO_NATIVE(rela->r_info);
+                       r.r_addend = TO_NATIVE(rela->r_addend);
+                       sym = elf->symtab_start + ELF_R_SYM(r.r_info);
+                       /* Skip special sections */
+                       if (sym->st_shndx >= SHN_LORESERVE)
+                               continue;
+
+                       secname = secstrings + sechdrs[sym->st_shndx].sh_name;
+                       if (section(secname))
+                               warn_sec_mismatch(modname, name, elf, sym, r);
+               }
+       }
+}
+
+/**
+ * Functions used only during module init is marked __init and is stored in
+ * a .init.text section. Likewise data is marked __initdata and stored in
+ * a .init.data section.
+ * If this section is one of these sections return 1
+ * See include/linux/init.h for the details
+ **/
+static int init_section(const char *name)
+{
+       if (strcmp(name, ".init") == 0)
+               return 1;
+       if (strncmp(name, ".init.", strlen(".init.")) == 0)
+               return 1;
+       return 0;
+}
+
+/**
+ * Identify sections from which references to a .init section is OK.
+ *
+ * Unfortunately references to read only data that referenced .init
+ * sections had to be excluded. Almost all of these are false
+ * positives, they are created by gcc. The downside of excluding rodata
+ * is that there really are some user references from rodata to
+ * init code, e.g. drivers/video/vgacon.c:
+ *
+ * const struct consw vga_con = {
+ *        con_startup:            vgacon_startup,
+ *
+ * where vgacon_startup is __init.  If you want to wade through the false
+ * positives, take out the check for rodata.
+ **/
+static int init_section_ref_ok(const char *name)
+{
+       const char **s;
+       /* Absolute section names */
+       const char *namelist1[] = {
+               ".init",
+               ".opd",   /* see comment [OPD] at exit_section_ref_ok() */
+               ".toc1",  /* used by ppc64 */
+               ".stab",
+               ".rodata",
+               ".text.lock",
+               "__bug_table", /* used by powerpc for BUG() */
+               ".pci_fixup_header",
+               ".pci_fixup_final",
+               ".pdr",
+               "__param",
+               NULL
+       };
+       /* Start of section names */
+       const char *namelist2[] = {
+               ".init.",
+               ".altinstructions",
+               ".eh_frame",
+               ".debug",
+               NULL
+       };
+       /* part of section name */
+       const char *namelist3 [] = {
+               ".unwind",  /* sample: IA_64.unwind.init.text */
+               NULL
+       };
+
+       for (s = namelist1; *s; s++)
+               if (strcmp(*s, name) == 0)
+                       return 1;
+       for (s = namelist2; *s; s++)
+               if (strncmp(*s, name, strlen(*s)) == 0)
+                       return 1;
+       for (s = namelist3; *s; s++)
+               if (strstr(name, *s) != NULL)
+                       return 1;
+       return 0;
+}
+
+/*
+ * Functions used only during module exit is marked __exit and is stored in
+ * a .exit.text section. Likewise data is marked __exitdata and stored in
+ * a .exit.data section.
+ * If this section is one of these sections return 1
+ * See include/linux/init.h for the details
+ **/
+static int exit_section(const char *name)
+{
+       if (strcmp(name, ".exit.text") == 0)
+               return 1;
+       if (strcmp(name, ".exit.data") == 0)
+               return 1;
+       return 0;
+
+}
+
+/*
+ * Identify sections from which references to a .exit section is OK.
+ *
+ * [OPD] Keith Ownes <kaos@sgi.com> commented:
+ * For our future {in}sanity, add a comment that this is the ppc .opd
+ * section, not the ia64 .opd section.
+ * ia64 .opd should not point to discarded sections.
+ **/
+static int exit_section_ref_ok(const char *name)
+{
+       const char **s;
+       /* Absolute section names */
+       const char *namelist1[] = {
+               ".exit.text",
+               ".exit.data",
+               ".init.text",
+               ".opd", /* See comment [OPD] */
+               ".toc1",  /* used by ppc64 */
+               ".altinstructions",
+               ".pdr",
+               "__bug_table", /* used by powerpc for BUG() */
+               ".exitcall.exit",
+               ".eh_frame",
+               ".stab",
+               NULL
+       };
+       /* Start of section names */
+       const char *namelist2[] = {
+               ".debug",
+               NULL
+       };
+       /* part of section name */
+       const char *namelist3 [] = {
+               ".unwind",  /* Sample: IA_64.unwind.exit.text */
+               NULL
+       };
+
+       for (s = namelist1; *s; s++)
+               if (strcmp(*s, name) == 0)
+                       return 1;
+       for (s = namelist2; *s; s++)
+               if (strncmp(*s, name, strlen(*s)) == 0)
+                       return 1;
+       for (s = namelist3; *s; s++)
+               if (strstr(name, *s) != NULL)
+                       return 1;
+       return 0;
+}
+
+static void read_symbols(char *modname)
 {
        const char *symname;
        char *version;
@@ -462,9 +877,7 @@ read_symbols(char *modname)
        /* When there's no vmlinux, don't print warnings about
         * unresolved symbols (since there'll be too many ;) */
        if (is_vmlinux(modname)) {
-               unsigned int fake_crc = 0;
                have_vmlinux = 1;
-               add_exported_symbol("struct_module", mod, &fake_crc);
                mod->skip = 1;
        }
 
@@ -474,6 +887,8 @@ read_symbols(char *modname)
                handle_modversions(mod, &info, sym, symname);
                handle_moddevtable(mod, &info, sym, symname);
        }
+       check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok);
+       check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok);
 
        version = get_modinfo(info.modinfo, info.modinfo_len, "version");
        if (version)
@@ -499,39 +914,33 @@ read_symbols(char *modname)
  * following helper, then compare to the file on disk and
  * only update the later if anything changed */
 
-void __attribute__((format(printf, 2, 3)))
-buf_printf(struct buffer *buf, const char *fmt, ...)
+void __attribute__((format(printf, 2, 3))) buf_printf(struct buffer *buf,
+                                                     const char *fmt, ...)
 {
        char tmp[SZ];
        int len;
        va_list ap;
-       
+
        va_start(ap, fmt);
        len = vsnprintf(tmp, SZ, fmt, ap);
-       if (buf->size - buf->pos < len + 1) {
-               buf->size += 128;
-               buf->p = realloc(buf->p, buf->size);
-       }
-       strncpy(buf->p + buf->pos, tmp, len + 1);
-       buf->pos += len;
+       buf_write(buf, tmp, len);
        va_end(ap);
 }
 
-void
-buf_write(struct buffer *buf, const char *s, int len)
+void buf_write(struct buffer *buf, const char *s, int len)
 {
        if (buf->size - buf->pos < len) {
-               buf->size += len;
+               buf->size += len + SZ;
                buf->p = realloc(buf->p, buf->size);
        }
        strncpy(buf->p + buf->pos, s, len);
        buf->pos += len;
 }
 
-/* Header for the generated file */
-
-void
-add_header(struct buffer *b, struct module *mod)
+/**
+ * Header for the generated file
+ **/
+static void add_header(struct buffer *b, struct module *mod)
 {
        buf_printf(b, "#include <linux/module.h>\n");
        buf_printf(b, "#include <linux/vermagic.h>\n");
@@ -551,10 +960,10 @@ add_header(struct buffer *b, struct module *mod)
        buf_printf(b, "};\n");
 }
 
-/* Record CRCs for unresolved symbols */
-
-void
-add_versions(struct buffer *b, struct module *mod)
+/**
+ * Record CRCs for unresolved symbols
+ **/
+static void add_versions(struct buffer *b, struct module *mod)
 {
        struct symbol *s, *exp;
 
@@ -562,8 +971,8 @@ add_versions(struct buffer *b, struct module *mod)
                exp = find_symbol(s->name);
                if (!exp || exp->module == mod) {
                        if (have_vmlinux && !s->weak)
-                               fprintf(stderr, "*** Warning: \"%s\" [%s.ko] "
-                               "undefined!\n", s->name, mod->name);
+                               warn("\"%s\" [%s.ko] undefined!\n",
+                                    s->name, mod->name);
                        continue;
                }
                s->module = exp->module;
@@ -584,8 +993,7 @@ add_versions(struct buffer *b, struct module *mod)
                        continue;
                }
                if (!s->crc_valid) {
-                       fprintf(stderr, "*** Warning: \"%s\" [%s.ko] "
-                               "has no CRC!\n",
+                       warn("\"%s\" [%s.ko] has no CRC!\n",
                                s->name, mod->name);
                        continue;
                }
@@ -595,8 +1003,8 @@ add_versions(struct buffer *b, struct module *mod)
        buf_printf(b, "};\n");
 }
 
-void
-add_depends(struct buffer *b, struct module *mod, struct module *modules)
+static void add_depends(struct buffer *b, struct module *mod,
+                       struct module *modules)
 {
        struct symbol *s;
        struct module *m;
@@ -626,8 +1034,7 @@ add_depends(struct buffer *b, struct module *mod, struct module *modules)
        buf_printf(b, "\";\n");
 }
 
-void
-add_srcversion(struct buffer *b, struct module *mod)
+static void add_srcversion(struct buffer *b, struct module *mod)
 {
        if (mod->srcversion[0]) {
                buf_printf(b, "\n");
@@ -636,8 +1043,7 @@ add_srcversion(struct buffer *b, struct module *mod)
        }
 }
 
-void
-write_if_changed(struct buffer *b, const char *fname)
+static void write_if_changed(struct buffer *b, const char *fname)
 {
        char *tmp;
        FILE *file;
@@ -681,8 +1087,7 @@ write_if_changed(struct buffer *b, const char *fname)
        fclose(file);
 }
 
-void
-read_dump(const char *fname)
+static void read_dump(const char *fname, unsigned int kernel)
 {
        unsigned long size, pos = 0;
        void *file = grab_file(fname, &size);
@@ -696,6 +1101,7 @@ read_dump(const char *fname)
                char *symname, *modname, *d;
                unsigned int crc;
                struct module *mod;
+               struct symbol *s;
 
                if (!(symname = strchr(line, '\t')))
                        goto fail;
@@ -716,15 +1122,30 @@ read_dump(const char *fname)
                        mod = new_module(NOFAIL(strdup(modname)));
                        mod->skip = 1;
                }
-               add_exported_symbol(symname, mod, &crc);
+               s = sym_add_exported(symname, mod);
+               s->kernel    = kernel;
+               s->preloaded = 1;
+               sym_update_crc(symname, mod, crc);
        }
        return;
 fail:
        fatal("parse error in symbol dump file\n");
 }
 
-void
-write_dump(const char *fname)
+/* For normal builds always dump all symbols.
+ * For external modules only dump symbols
+ * that are not read from kernel Module.symvers.
+ **/
+static int dump_sym(struct symbol *sym)
+{
+       if (!external_module)
+               return 1;
+       if (sym->vmlinux || sym->kernel)
+               return 0;
+       return 1;
+}
+
+static void write_dump(const char *fname)
 {
        struct buffer buf = { };
        struct symbol *symbol;
@@ -733,34 +1154,33 @@ write_dump(const char *fname)
        for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
                symbol = symbolhash[n];
                while (symbol) {
-                       symbol = symbol->next;
-               }
-       }
-
-       for (n = 0; n < SYMBOL_HASH_SIZE ; n++) {
-               symbol = symbolhash[n];
-               while (symbol) {
-                       buf_printf(&buf, "0x%08x\t%s\t%s\n", symbol->crc,
-                               symbol->name, symbol->module->name);
+                       if (dump_sym(symbol))
+                               buf_printf(&buf, "0x%08x\t%s\t%s\n",
+                                       symbol->crc, symbol->name,
+                                       symbol->module->name);
                        symbol = symbol->next;
                }
        }
        write_if_changed(&buf, fname);
 }
 
-int
-main(int argc, char **argv)
+int main(int argc, char **argv)
 {
        struct module *mod;
        struct buffer buf = { };
        char fname[SZ];
-       char *dump_read = NULL, *dump_write = NULL;
+       char *kernel_read = NULL, *module_read = NULL;
+       char *dump_write = NULL;
        int opt;
 
-       while ((opt = getopt(argc, argv, "i:mo:a")) != -1) {
+       while ((opt = getopt(argc, argv, "i:I:mo:a")) != -1) {
                switch(opt) {
                        case 'i':
-                               dump_read = optarg;
+                               kernel_read = optarg;
+                               break;
+                       case 'I':
+                               module_read = optarg;
+                               external_module = 1;
                                break;
                        case 'm':
                                modversions = 1;
@@ -776,8 +1196,10 @@ main(int argc, char **argv)
                }
        }
 
-       if (dump_read)
-               read_dump(dump_read);
+       if (kernel_read)
+               read_dump(kernel_read, 1);
+       if (module_read)
+               read_dump(module_read, 0);
 
        while (optind < argc) {
                read_symbols(argv[optind++]);
@@ -804,4 +1226,3 @@ main(int argc, char **argv)
 
        return 0;
 }
-