kbuild: simplified warning report in modpost
[safe/jmp/linux-2.6] / scripts / mod / modpost.c
index 986513d..0a80aca 100644 (file)
@@ -605,6 +605,29 @@ static int strrcmp(const char *s, const char *sub)
        return memcmp(s + slen - sublen, sub, sublen);
 }
 
+static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
+{
+       if (sym)
+               return elf->strtab + sym->st_name;
+       else
+               return "";
+}
+
+static const char *sec_name(struct elf_info *elf, int shndx)
+{
+       Elf_Shdr *sechdrs = elf->sechdrs;
+       return (void *)elf->hdr +
+               elf->sechdrs[elf->hdr->e_shstrndx].sh_offset +
+               sechdrs[shndx].sh_name;
+}
+
+static const char *sech_name(struct elf_info *elf, Elf_Shdr *sechdr)
+{
+       return (void *)elf->hdr +
+               elf->sechdrs[elf->hdr->e_shstrndx].sh_offset +
+               sechdr->sh_name;
+}
+
 /* if sym is empty or point to a string
  * like ".[0-9]+" then return 1.
  * This is the optional prefix added by ld to some sections
@@ -670,27 +693,41 @@ int match(const char *sym, const char * const pat[])
 static const char *section_white_list[] =
        { ".debug*", ".stab*", ".note*", ".got*", ".toc*", NULL };
 
-#define INIT_DATA_SECTIONS ".init.data$"
-#define EXIT_DATA_SECTIONS ".exit.data$"
+#define ALL_INIT_DATA_SECTIONS \
+       ".init.data$", ".devinit.data$", ".cpuinit.data$", ".meminit.data$"
+#define ALL_EXIT_DATA_SECTIONS \
+       ".exit.data$", ".devexit.data$", ".cpuexit.data$", ".memexit.data$"
 
-#define INIT_TEXT_SECTIONS ".init.text$"
-#define EXIT_TEXT_SECTIONS ".exit.text$"
+#define ALL_INIT_TEXT_SECTIONS \
+       ".init.text$", ".devinit.text$", ".cpuinit.text$", ".meminit.text$"
+#define ALL_EXIT_TEXT_SECTIONS \
+       ".exit.text$", ".devexit.text$", ".cpuexit.text$", ".memexit.text$"
 
-#define INIT_SECTIONS INIT_DATA_SECTIONS, INIT_TEXT_SECTIONS
-#define EXIT_SECTIONS EXIT_DATA_SECTIONS, EXIT_TEXT_SECTIONS
+#define ALL_INIT_SECTIONS ALL_INIT_DATA_SECTIONS, ALL_INIT_TEXT_SECTIONS
+#define ALL_EXIT_SECTIONS ALL_EXIT_DATA_SECTIONS, ALL_EXIT_TEXT_SECTIONS
 
 #define DATA_SECTIONS ".data$", ".data.rel$"
 #define TEXT_SECTIONS ".text$"
 
+#define INIT_SECTIONS      ".init.data$", ".init.text$"
+#define DEV_INIT_SECTIONS  ".devinit.data$", ".devinit.text$"
+#define CPU_INIT_SECTIONS  ".cpuinit.data$", ".cpuinit.text$"
+#define MEM_INIT_SECTIONS  ".meminit.data$", ".meminit.text$"
+
+#define EXIT_SECTIONS      ".exit.data$", ".exit.text$"
+#define DEV_EXIT_SECTIONS  ".devexit.data$", ".devexit.text$"
+#define CPU_EXIT_SECTIONS  ".cpuexit.data$", ".cpuexit.text$"
+#define MEM_EXIT_SECTIONS  ".memexit.data$", ".memexit.text$"
+
 /* init data sections */
-static const char *init_data_sections[] = { INIT_DATA_SECTIONS, NULL };
+static const char *init_data_sections[] = { ALL_INIT_DATA_SECTIONS, NULL };
 
 /* all init sections */
-static const char *init_sections[] = { INIT_SECTIONS, NULL };
+static const char *init_sections[] = { ALL_INIT_SECTIONS, NULL };
 
 /* All init and exit sections (code + data) */
 static const char *init_exit_sections[] =
-       {INIT_SECTIONS, EXIT_SECTIONS, NULL };
+       {ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL };
 
 /* data section */
 static const char *data_sections[] = { DATA_SECTIONS, NULL };
@@ -734,22 +771,32 @@ const struct sectioncheck sectioncheck[] = {
  */
 {
        .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL },
-       .tosec   = { INIT_SECTIONS, EXIT_SECTIONS, NULL }
+       .tosec   = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }
+},
+/* Do not reference init code/data from devinit/cpuinit/meminit code/data */
+{
+       .fromsec = { DEV_INIT_SECTIONS, CPU_INIT_SECTIONS, MEM_INIT_SECTIONS, NULL },
+       .tosec   = { INIT_SECTIONS, NULL }
+},
+/* Do not reference exit code/data from devexit/cpuexit/memexit code/data */
+{
+       .fromsec = { DEV_EXIT_SECTIONS, CPU_EXIT_SECTIONS, MEM_EXIT_SECTIONS, NULL },
+       .tosec   = { EXIT_SECTIONS, NULL }
 },
 /* Do not use exit code/data from init code */
 {
-       .fromsec = { INIT_SECTIONS, NULL },
-       .tosec   = { EXIT_SECTIONS, NULL },
+       .fromsec = { ALL_INIT_SECTIONS, NULL },
+       .tosec   = { ALL_EXIT_SECTIONS, NULL },
 },
 /* Do not use init code/data from exit code */
 {
-       .fromsec = { EXIT_SECTIONS, NULL },
-       .tosec   = { INIT_SECTIONS, NULL }
+       .fromsec = { ALL_EXIT_SECTIONS, NULL },
+       .tosec   = { ALL_INIT_SECTIONS, NULL }
 },
 /* Do not export init/exit functions or data */
 {
        .fromsec = { "__ksymtab*", NULL },
-       .tosec   = { INIT_SECTIONS, EXIT_SECTIONS, NULL }
+       .tosec   = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }
 }
 };
 
@@ -768,7 +815,6 @@ static int section_mismatch(const char *fromsec, const char *tosec)
        return 0;
 }
 
-
 /**
  * Whitelist to allow certain references to pass with no warning.
  *
@@ -812,36 +858,35 @@ static int section_mismatch(const char *fromsec, const char *tosec)
  *   refsymname = __init_begin, _sinittext, _einittext
  *
  **/
-static int secref_whitelist(const char *modname, const char *tosec,
-                           const char *fromsec, const char *atsym,
-                           const char *refsymname)
+static int secref_whitelist(const char *fromsec, const char *fromsym,
+                           const char *tosec, const char *tosym)
 {
        /* Check for pattern 0 */
        if (match(fromsec, initref_sections))
-               return 1;
+               return 0;
 
        /* Check for pattern 1 */
        if (match(tosec, init_data_sections) &&
            match(fromsec, data_sections) &&
-           (strncmp(atsym, "__param", strlen("__param")) == 0))
-               return 1;
+           (strncmp(fromsym, "__param", strlen("__param")) == 0))
+               return 0;
 
        /* Check for pattern 2 */
        if (match(tosec, init_exit_sections) &&
            match(fromsec, data_sections) &&
-           match(atsym, symbol_white_list))
-               return 1;
+           match(fromsym, symbol_white_list))
+               return 0;
 
        /* Check for pattern 3 */
        if (match(fromsec, head_sections) &&
            match(tosec, init_sections))
-       return 1;
+               return 0;
 
        /* Check for pattern 4 */
-       if (match(refsymname, linker_symbols))
-               return 1;
+       if (match(tosym, linker_symbols))
+               return 0;
 
-       return 0;
+       return 1;
 }
 
 /**
@@ -914,100 +959,78 @@ static inline int is_valid_name(struct elf_info *elf, Elf_Sym *sym)
  * 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)
+static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr,
+                                const char *sec)
 {
        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;
+       Elf_Sym *near = NULL;
+       Elf_Addr distance = ~0;
 
        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;
+               symsec = sec_name(elf, sym->st_shndx);
                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) {
-                               *before = sym;
+                       if ((addr - sym->st_value) < distance) {
+                               distance = addr - sym->st_value;
+                               near = sym;
+                       } else if ((addr - sym->st_value) == distance) {
+                               near = sym;
                        }
-               } else {
-                       if ((sym->st_value - addr) < afterdiff) {
-                               afterdiff = sym->st_value - addr;
-                               *after = sym;
-                       } else if ((sym->st_value - addr) == afterdiff)
-                               *after = sym;
                }
        }
+       return near;
 }
 
-/**
+/*
  * 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)
+ */
+static void report_sec_mismatch(const char *modname,
+                                const char *fromsec,
+                                unsigned long long fromaddr,
+                                const char *fromsym,
+                                const char *tosec, const char *tosym)
 {
-       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 (secref_whitelist(modname, secname, fromsec,
-                            before ? elf->strtab + before->st_name : "",
-                            refsymname))
-               return;
-
-       if (before && after) {
-               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,
-                    elf->strtab + after->st_name);
-       } else if (before) {
+       if (strlen(tosym)) {
                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(%s+0x%llx): Section mismatch: reference to %s:%s "
-                    "before '%s' (at offset -0x%llx)\n",
-                    modname, fromsec, (unsigned long long)r.r_offset,
-                    secname, refsymname,
-                    elf->strtab + after->st_name,
-                    (unsigned long long)r.r_offset);
+                    "in '%s'\n",
+                    modname, fromsec, fromaddr,
+                    tosec, tosym, fromsym);
        } else {
                warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s\n",
-                    modname, fromsec, (unsigned long long)r.r_offset,
-                    secname, refsymname);
+                    modname, fromsec, fromaddr,
+                    tosec, tosym);
+       }
+}
+
+static void check_section_mismatch(const char *modname, struct elf_info *elf,
+                                   Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
+{
+       const char *tosec;
+
+       tosec = sec_name(elf, sym->st_shndx);
+       if (section_mismatch(fromsec, tosec)) {
+               const char *fromsym;
+               const char *tosym;
+
+               fromsym = sym_name(elf,
+                         find_elf_symbol2(elf, r->r_offset, fromsec));
+               tosym = sym_name(elf,
+                       find_elf_symbol(elf, r->r_addend, sym));
+
+               /* check whitelist - we may ignore it */
+               if (secref_whitelist(fromsec, fromsym, tosec, tosym)) {
+                       report_sec_mismatch(modname, fromsec, r->r_offset,
+                                           fromsym, tosec, tosym);
+               }
        }
 }
 
@@ -1093,25 +1116,19 @@ static void section_rela(const char *modname, struct elf_info *elf,
        Elf_Rela r;
        unsigned int r_sym;
        const char *fromsec;
-       const char * tosec;
 
-       Elf_Ehdr *hdr = elf->hdr;
-       Elf_Rela *start = (void *)hdr + sechdr->sh_offset;
+       Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset;
        Elf_Rela *stop  = (void *)start + sechdr->sh_size;
 
-       const char *secstrings = (void *)hdr +
-                                elf->sechdrs[hdr->e_shstrndx].sh_offset;
-
-       fromsec = secstrings + sechdr->sh_name;
+       fromsec = sech_name(elf, sechdr);
        fromsec += strlen(".rela");
        /* if from section (name) is know good then skip it */
        if (match(fromsec, section_white_list))
                return;
-
        for (rela = start; rela < stop; rela++) {
                r.r_offset = TO_NATIVE(rela->r_offset);
 #if KERNEL_ELFCLASS == ELFCLASS64
-               if (hdr->e_machine == EM_MIPS) {
+               if (elf->hdr->e_machine == EM_MIPS) {
                        unsigned int r_typ;
                        r_sym = ELF64_MIPS_R_SYM(rela->r_info);
                        r_sym = TO_NATIVE(r_sym);
@@ -1130,11 +1147,7 @@ static void section_rela(const char *modname, struct elf_info *elf,
                /* Skip special sections */
                if (sym->st_shndx >= SHN_LORESERVE)
                        continue;
-
-               tosec = secstrings +
-                       elf->sechdrs[sym->st_shndx].sh_name;
-               if (section_mismatch(fromsec, tosec))
-                       warn_sec_mismatch(modname, fromsec, elf, sym, r);
+               check_section_mismatch(modname, elf, &r, sym, fromsec);
        }
 }
 
@@ -1146,16 +1159,11 @@ static void section_rel(const char *modname, struct elf_info *elf,
        Elf_Rela r;
        unsigned int r_sym;
        const char *fromsec;
-       const char * tosec;
 
-       Elf_Ehdr *hdr = elf->hdr;
-       Elf_Rel *start = (void *)hdr + sechdr->sh_offset;
+       Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset;
        Elf_Rel *stop  = (void *)start + sechdr->sh_size;
 
-       const char *secstrings = (void *)hdr +
-                                elf->sechdrs[hdr->e_shstrndx].sh_offset;
-
-       fromsec = secstrings + sechdr->sh_name;
+       fromsec = sech_name(elf, sechdr);
        fromsec += strlen(".rel");
        /* if from section (name) is know good then skip it */
        if (match(fromsec, section_white_list))
@@ -1164,7 +1172,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
        for (rel = start; rel < stop; rel++) {
                r.r_offset = TO_NATIVE(rel->r_offset);
 #if KERNEL_ELFCLASS == ELFCLASS64
-               if (hdr->e_machine == EM_MIPS) {
+               if (elf->hdr->e_machine == EM_MIPS) {
                        unsigned int r_typ;
                        r_sym = ELF64_MIPS_R_SYM(rel->r_info);
                        r_sym = TO_NATIVE(r_sym);
@@ -1179,7 +1187,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
                r_sym = ELF_R_SYM(r.r_info);
 #endif
                r.r_addend = 0;
-               switch (hdr->e_machine) {
+               switch (elf->hdr->e_machine) {
                case EM_386:
                        if (addend_386_rel(elf, sechdr, &r))
                                continue;
@@ -1197,11 +1205,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
                /* Skip special sections */
                if (sym->st_shndx >= SHN_LORESERVE)
                        continue;
-
-               tosec = secstrings +
-                       elf->sechdrs[sym->st_shndx].sh_name;
-               if (section_mismatch(fromsec, tosec))
-                       warn_sec_mismatch(modname, fromsec, elf, sym, r);
+               check_section_mismatch(modname, elf, &r, sym, fromsec);
        }
 }
 
@@ -1221,11 +1225,10 @@ static void check_sec_ref(struct module *mod, const char *modname,
                           struct elf_info *elf)
 {
        int i;
-       Elf_Ehdr *hdr = elf->hdr;
        Elf_Shdr *sechdrs = elf->sechdrs;
 
        /* Walk through all sections */
-       for (i = 0; i < hdr->e_shnum; i++) {
+       for (i = 0; i < elf->hdr->e_shnum; i++) {
                /* We want to process only relocation sections and not .init */
                if (sechdrs[i].sh_type == SHT_RELA)
                        section_rela(modname, elf, &elf->sechdrs[i]);