/* Only warn about unresolved symbols */
static int warn_unresolved = 0;
/* How a symbol is exported */
+static int sec_mismatch_count = 0;
+static int sec_mismatch_verbose = 1;
+
enum export {
export_plain, export_unused, export_gpl,
export_unused_gpl, export_gpl_future, export_unknown
static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
{
- return elf->strtab + sym->st_name;
+ if (sym)
+ return elf->strtab + sym->st_name;
+ else
+ return "";
}
static const char *sec_name(struct elf_info *elf, int shndx)
static const char *section_white_list[] =
{ ".debug*", ".stab*", ".note*", ".got*", ".toc*", NULL };
+/*
+ * Is this section one we do not want to check?
+ * This is often debug sections.
+ * If we are going to check this section then
+ * test if section name ends with a dot and a number.
+ * This is used to find sections where the linker have
+ * appended a dot-number to make the name unique.
+ * The cause of this is often a section specified in assembler
+ * without "ax" / "aw" and the same section used in .c
+ * code where gcc add these.
+ */
+static int check_section(const char *modname, const char *sec)
+{
+ const char *e = sec + strlen(sec) - 1;
+ if (match(sec, section_white_list))
+ return 1;
+
+ if (*e && isdigit(*e)) {
+ /* consume all digits */
+ while (*e && e != sec && isdigit(*e))
+ e--;
+ if (*e == '.') {
+ warn("%s (%s): unexpected section name.\n"
+ "The (.[number]+) following section name are "
+ "ld generated and not expected.\n"
+ "Did you forget to use \"ax\"/\"aw\" "
+ "in a .S file?\n"
+ "Note that for example <linux/init.h> contains\n"
+ "section definitions for use in .S files.\n\n",
+ modname, sec);
+ }
+ }
+ return 0;
+}
+
+
+
#define ALL_INIT_DATA_SECTIONS \
".init.data$", ".devinit.data$", ".cpuinit.data$", ".meminit.data$"
#define ALL_EXIT_DATA_SECTIONS \
static const char *linker_symbols[] =
{ "__init_begin", "_sinittext", "_einittext", NULL };
+enum mismatch {
+ NO_MISMATCH,
+ TEXT_TO_INIT,
+ DATA_TO_INIT,
+ TEXT_TO_EXIT,
+ DATA_TO_EXIT,
+ XXXINIT_TO_INIT,
+ XXXEXIT_TO_EXIT,
+ INIT_TO_EXIT,
+ EXIT_TO_INIT,
+ EXPORT_TO_INIT_EXIT,
+};
+
struct sectioncheck {
const char *fromsec[20];
const char *tosec[20];
+ enum mismatch mismatch;
};
const struct sectioncheck sectioncheck[] = {
* normal code and data
*/
{
- .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL },
- .tosec = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }
+ .fromsec = { TEXT_SECTIONS, NULL },
+ .tosec = { ALL_INIT_SECTIONS, NULL },
+ .mismatch = TEXT_TO_INIT,
+},
+{
+ .fromsec = { DATA_SECTIONS, NULL },
+ .tosec = { ALL_INIT_SECTIONS, NULL },
+ .mismatch = DATA_TO_INIT,
+},
+{
+ .fromsec = { TEXT_SECTIONS, NULL },
+ .tosec = { ALL_EXIT_SECTIONS, NULL },
+ .mismatch = TEXT_TO_EXIT,
+},
+{
+ .fromsec = { DATA_SECTIONS, NULL },
+ .tosec = { ALL_EXIT_SECTIONS, NULL },
+ .mismatch = DATA_TO_EXIT,
},
/* 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 }
+ .tosec = { INIT_SECTIONS, NULL },
+ .mismatch = XXXINIT_TO_INIT,
},
/* 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 }
+ .tosec = { EXIT_SECTIONS, NULL },
+ .mismatch = XXXEXIT_TO_EXIT,
},
/* Do not use exit code/data from init code */
{
.fromsec = { ALL_INIT_SECTIONS, NULL },
.tosec = { ALL_EXIT_SECTIONS, NULL },
+ .mismatch = INIT_TO_EXIT,
},
/* Do not use init code/data from exit code */
{
.fromsec = { ALL_EXIT_SECTIONS, NULL },
- .tosec = { ALL_INIT_SECTIONS, NULL }
+ .tosec = { ALL_INIT_SECTIONS, NULL },
+ .mismatch = EXIT_TO_INIT,
},
/* Do not export init/exit functions or data */
{
.fromsec = { "__ksymtab*", NULL },
- .tosec = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }
+ .tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL },
+ .mismatch = EXPORT_TO_INIT_EXIT
}
};
for (i = 0; i < elems; i++) {
if (match(fromsec, check->fromsec) &&
match(tosec, check->tosec))
- return 1;
+ return check->mismatch;
check++;
}
- return 0;
+ return NO_MISMATCH;
}
-
/**
* Whitelist to allow certain references to pass with no warning.
*
* 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;
}
/**
return near;
}
-/**
+/*
+ * Convert a section name to the function/data attribute
+ * .init.text => __init
+ * .cpuinit.data => __cpudata
+ * .memexitconst => __memconst
+ * etc.
+*/
+static char *sec2annotation(const char *s)
+{
+ if (match(s, init_exit_sections)) {
+ char *p = malloc(20);
+ char *r = p;
+
+ *p++ = '_';
+ *p++ = '_';
+ if (*s == '.')
+ s++;
+ while (*s && *s != '.')
+ *p++ = *s++;
+ *p = '\0';
+ if (*s == '.')
+ s++;
+ if (strstr(s, "rodata") != NULL)
+ strcat(p, "const ");
+ else if (strstr(s, "data") != NULL)
+ strcat(p, "data ");
+ else
+ strcat(p, " ");
+ return r; /* we leak her but we do not care */
+ } else {
+ return "";
+ }
+}
+
+static int is_function(Elf_Sym *sym)
+{
+ if (sym)
+ return ELF_ST_TYPE(sym->st_info) == STT_FUNC;
+ else
+ return 0;
+}
+
+/*
* 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)
-{
- Elf_Sym *where;
- Elf_Sym *refsym;
- const char *refsymname = "";
- const char *secname;
-
- secname = sec_name(elf, sym->st_shndx);
- where = find_elf_symbol2(elf, r.r_offset, fromsec);
-
- refsym = find_elf_symbol(elf, r.r_addend, sym);
- if (refsym && strlen(sym_name(elf, refsym)))
- refsymname = sym_name(elf, refsym);
-
- /* check whitelist - we may ignore it */
- if (secref_whitelist(modname, secname, fromsec,
- where ? sym_name(elf, where) : "",
- refsymname))
+ */
+static void report_sec_mismatch(const char *modname, enum mismatch mismatch,
+ const char *fromsec,
+ unsigned long long fromaddr,
+ const char *fromsym,
+ int from_is_func,
+ const char *tosec, const char *tosym,
+ int to_is_func)
+{
+ const char *from, *from_p;
+ const char *to, *to_p;
+ from = from_is_func ? "function" : "variable";
+ from_p = from_is_func ? "()" : "";
+ to = to_is_func ? "function" : "variable";
+ to_p = to_is_func ? "()" : "";
+
+ sec_mismatch_count++;
+ if (!sec_mismatch_verbose)
return;
- if (where) {
- warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s "
- "in '%s'\n",
- modname, fromsec, (unsigned long long)r.r_offset,
- secname, refsymname, sym_name(elf, where));
- } else {
- warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s\n",
- modname, fromsec, (unsigned long long)r.r_offset,
- secname, refsymname);
+ fprintf(stderr, "WARNING: %s(%s+0x%llx): Section mismatch in"
+ " reference from the %s %s%s to the %s %s:%s%s\n",
+ modname, fromsec, fromaddr, from, fromsym, from_p,
+ to, tosec, tosym, to_p);
+
+ switch (mismatch) {
+ case TEXT_TO_INIT:
+ fprintf(stderr,
+ "The function %s %s() references\n"
+ "the %s %s%s%s.\n"
+ "This is often because %s lacks a %s\n"
+ "annotation or the annotation of %s is wrong.\n",
+ sec2annotation(fromsec), fromsym,
+ to, sec2annotation(tosec), tosym, to_p,
+ fromsym, sec2annotation(tosec), tosym);
+ break;
+ case DATA_TO_INIT: {
+ const char **s = symbol_white_list;
+ fprintf(stderr,
+ "The variable %s references\n"
+ "the %s %s%s%s\n"
+ "If the reference is valid then annotate the\n"
+ "variable with __init* (see linux/init.h) "
+ "or name the variable:\n",
+ fromsym, to, sec2annotation(tosec), tosym, to_p);
+ while (*s)
+ fprintf(stderr, "%s, ", *s++);
+ fprintf(stderr, "\n");
+ break;
+ }
+ case TEXT_TO_EXIT:
+ fprintf(stderr,
+ "The function %s() references a %s in an exit section.\n"
+ "Often the %s %s%s has valid usage outside the exit section\n"
+ "and the fix is to remove the %sannotation of %s.\n",
+ fromsym, to, to, tosym, to_p, sec2annotation(tosec), tosym);
+ break;
+ case DATA_TO_EXIT: {
+ const char **s = symbol_white_list;
+ fprintf(stderr,
+ "The variable %s references\n"
+ "the %s %s%s%s\n"
+ "If the reference is valid then annotate the\n"
+ "variable with __exit* (see linux/init.h) or "
+ "name the variable:\n",
+ fromsym, to, sec2annotation(tosec), tosym, to_p);
+ while (*s)
+ fprintf(stderr, "%s, ", *s++);
+ fprintf(stderr, "\n");
+ break;
+ }
+ case XXXINIT_TO_INIT:
+ case XXXEXIT_TO_EXIT:
+ fprintf(stderr,
+ "The %s %s%s%s references\n"
+ "a %s %s%s%s.\n"
+ "If %s is only used by %s then\n"
+ "annotate %s with a matching annotation.\n",
+ from, sec2annotation(fromsec), fromsym, from_p,
+ to, sec2annotation(tosec), tosym, to_p,
+ fromsym, tosym, fromsym);
+ break;
+ case INIT_TO_EXIT:
+ fprintf(stderr,
+ "The %s %s%s%s references\n"
+ "a %s %s%s%s.\n"
+ "This is often seen when error handling "
+ "in the init function\n"
+ "uses functionality in the exit path.\n"
+ "The fix is often to remove the %sannotation of\n"
+ "%s%s so it may be used outside an exit section.\n",
+ from, sec2annotation(fromsec), fromsym, from_p,
+ to, sec2annotation(tosec), tosym, to_p,
+ sec2annotation(tosec), tosym, to_p);
+ break;
+ case EXIT_TO_INIT:
+ fprintf(stderr,
+ "The %s %s%s%s references\n"
+ "a %s %s%s%s.\n"
+ "This is often seen when error handling "
+ "in the exit function\n"
+ "uses functionality in the init path.\n"
+ "The fix is often to remove the %sannotation of\n"
+ "%s%s so it may be used outside an init section.\n",
+ from, sec2annotation(fromsec), fromsym, from_p,
+ to, sec2annotation(tosec), tosym, to_p,
+ sec2annotation(tosec), tosym, to_p);
+ break;
+ case EXPORT_TO_INIT_EXIT:
+ fprintf(stderr,
+ "The symbol %s is exported and annotated %s\n"
+ "Fix this by removing the %sannotation of %s "
+ "or drop the export.\n",
+ tosym, sec2annotation(tosec), sec2annotation(tosec), tosym);
+ case NO_MISMATCH:
+ /* To get warnings on missing members */
+ break;
+ }
+ fprintf(stderr, "\n");
+}
+
+static void check_section_mismatch(const char *modname, struct elf_info *elf,
+ Elf_Rela *r, Elf_Sym *sym, const char *fromsec)
+{
+ const char *tosec;
+ enum mismatch mismatch;
+
+ tosec = sec_name(elf, sym->st_shndx);
+ mismatch = section_mismatch(fromsec, tosec);
+ if (mismatch != NO_MISMATCH) {
+ Elf_Sym *to;
+ Elf_Sym *from;
+ const char *tosym;
+ const char *fromsym;
+
+ from = find_elf_symbol2(elf, r->r_offset, fromsec);
+ fromsym = sym_name(elf, from);
+ to = find_elf_symbol(elf, r->r_addend, sym);
+ tosym = sym_name(elf, to);
+
+ /* check whitelist - we may ignore it */
+ if (secref_whitelist(fromsec, fromsym, tosec, tosym)) {
+ report_sec_mismatch(modname, mismatch,
+ fromsec, r->r_offset, fromsym,
+ is_function(from), tosec, tosym,
+ is_function(to));
+ }
}
}
Elf_Rela r;
unsigned int r_sym;
const char *fromsec;
- const char * tosec;
Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset;
Elf_Rela *stop = (void *)start + sechdr->sh_size;
fromsec = sech_name(elf, sechdr);
fromsec += strlen(".rela");
/* if from section (name) is know good then skip it */
- if (match(fromsec, section_white_list))
+ if (check_section(modname, fromsec))
return;
for (rela = start; rela < stop; rela++) {
/* Skip special sections */
if (sym->st_shndx >= SHN_LORESERVE)
continue;
-
- tosec = sec_name(elf, sym->st_shndx);
- if (section_mismatch(fromsec, tosec))
- warn_sec_mismatch(modname, fromsec, elf, sym, r);
+ check_section_mismatch(modname, elf, &r, sym, fromsec);
}
}
Elf_Rela r;
unsigned int r_sym;
const char *fromsec;
- const char * tosec;
Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset;
Elf_Rel *stop = (void *)start + sechdr->sh_size;
fromsec = sech_name(elf, sechdr);
fromsec += strlen(".rel");
/* if from section (name) is know good then skip it */
- if (match(fromsec, section_white_list))
+ if (check_section(modname, fromsec))
return;
for (rel = start; rel < stop; rel++) {
/* Skip special sections */
if (sym->st_shndx >= SHN_LORESERVE)
continue;
-
- tosec = sec_name(elf, sym->st_shndx);
- if (section_mismatch(fromsec, tosec))
- warn_sec_mismatch(modname, fromsec, elf, sym, r);
+ check_section_mismatch(modname, elf, &r, sym, fromsec);
}
}
buf_printf(b, "\n");
buf_printf(b, "static const struct modversion_info ____versions[]\n");
- buf_printf(b, "__attribute_used__\n");
+ buf_printf(b, "__used\n");
buf_printf(b, "__attribute__((section(\"__versions\"))) = {\n");
for (s = mod->unres; s; s = s->next) {
buf_printf(b, "\n");
buf_printf(b, "static const char __module_depends[]\n");
- buf_printf(b, "__attribute_used__\n");
+ buf_printf(b, "__used\n");
buf_printf(b, "__attribute__((section(\".modinfo\"))) =\n");
buf_printf(b, "\"depends=");
for (s = mod->unres; s; s = s->next) {
int opt;
int err;
- while ((opt = getopt(argc, argv, "i:I:mso:aw")) != -1) {
+ while ((opt = getopt(argc, argv, "i:I:msSo:aw")) != -1) {
switch (opt) {
case 'i':
kernel_read = optarg;
case 's':
vmlinux_section_warnings = 0;
break;
+ case 'S':
+ sec_mismatch_verbose = 0;
+ break;
case 'w':
warn_unresolved = 1;
break;
if (dump_write)
write_dump(dump_write);
+ if (sec_mismatch_count && !sec_mismatch_verbose)
+ fprintf(stderr, "modpost: Found %d section mismatch(es).\n"
+ "To see full details build your kernel with:\n"
+ "'make CONFIG_DEBUG_SECTION_MISMATCH=y'\n",
+ sec_mismatch_count);
return err;
}