|
@@ -605,6 +605,61 @@ static int strrcmp(const char *s, const char *sub)
|
|
|
return memcmp(s + slen - sublen, sub, sublen);
|
|
|
}
|
|
|
|
|
|
+/* 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
|
|
|
+ */
|
|
|
+static int number_prefix(const char *sym)
|
|
|
+{
|
|
|
+ if (*sym++ == '\0')
|
|
|
+ return 1;
|
|
|
+ if (*sym != '.')
|
|
|
+ return 0;
|
|
|
+ do {
|
|
|
+ char c = *sym++;
|
|
|
+ if (c < '0' || c > '9')
|
|
|
+ return 0;
|
|
|
+ } while (*sym);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/* The pattern is an array of simple patterns.
|
|
|
+ * "foo" will match an exact string equal to "foo"
|
|
|
+ * "foo*" will match a string that begins with "foo"
|
|
|
+ * "foo$" will match a string equal to "foo" or "foo.1"
|
|
|
+ * where the '1' can be any number including several digits.
|
|
|
+ * The $ syntax is for sections where ld append a dot number
|
|
|
+ * to make section name unique.
|
|
|
+ */
|
|
|
+int match(const char *sym, const char * const pat[])
|
|
|
+{
|
|
|
+ const char *p;
|
|
|
+ while (*pat) {
|
|
|
+ p = *pat++;
|
|
|
+ const char *endp = p + strlen(p) - 1;
|
|
|
+
|
|
|
+ /* "foo*" */
|
|
|
+ if (*endp == '*') {
|
|
|
+ if (strncmp(sym, p, strlen(p) - 1) == 0)
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ /* "foo$" */
|
|
|
+ else if (*endp == '$') {
|
|
|
+ if (strncmp(sym, p, strlen(p) - 1) == 0) {
|
|
|
+ if (number_prefix(sym + strlen(p) - 1))
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* no wildcards */
|
|
|
+ else {
|
|
|
+ if (strcmp(p, sym) == 0)
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* no match */
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* 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
|
|
@@ -653,6 +708,68 @@ static int data_section(const char *name)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* sections that we do not want to do full section mismatch check on */
|
|
|
+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 INIT_TEXT_SECTIONS ".init.text$"
|
|
|
+#define EXIT_TEXT_SECTIONS ".exit.text$"
|
|
|
+
|
|
|
+#define INIT_SECTIONS INIT_DATA_SECTIONS, INIT_TEXT_SECTIONS
|
|
|
+#define EXIT_SECTIONS EXIT_DATA_SECTIONS, EXIT_TEXT_SECTIONS
|
|
|
+
|
|
|
+#define DATA_SECTIONS ".data$"
|
|
|
+#define TEXT_SECTIONS ".text$"
|
|
|
+
|
|
|
+struct sectioncheck {
|
|
|
+ const char *fromsec[20];
|
|
|
+ const char *tosec[20];
|
|
|
+};
|
|
|
+
|
|
|
+const struct sectioncheck sectioncheck[] = {
|
|
|
+/* Do not reference init/exit code/data from
|
|
|
+ * normal code and data
|
|
|
+ */
|
|
|
+{
|
|
|
+ .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL },
|
|
|
+ .tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }
|
|
|
+},
|
|
|
+/* Do not use exit code/data from init code */
|
|
|
+{
|
|
|
+ .fromsec = { INIT_SECTIONS, NULL },
|
|
|
+ .tosec = { EXIT_SECTIONS, NULL },
|
|
|
+},
|
|
|
+/* Do not use init code/data from exit code */
|
|
|
+{
|
|
|
+ .fromsec = { EXIT_SECTIONS, NULL },
|
|
|
+ .tosec = { INIT_SECTIONS, NULL }
|
|
|
+},
|
|
|
+/* Do not export init/exit functions or data */
|
|
|
+{
|
|
|
+ .fromsec = { "__ksymtab*", NULL },
|
|
|
+ .tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL }
|
|
|
+}
|
|
|
+};
|
|
|
+
|
|
|
+static int section_mismatch(const char *fromsec, const char *tosec)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ int elems = sizeof(sectioncheck) / sizeof(struct sectioncheck);
|
|
|
+ const struct sectioncheck *check = §ioncheck[0];
|
|
|
+
|
|
|
+ for (i = 0; i < elems; i++) {
|
|
|
+ if (match(fromsec, check->fromsec) &&
|
|
|
+ match(tosec, check->tosec))
|
|
|
+ return 1;
|
|
|
+ check++;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* Whitelist to allow certain references to pass with no warning.
|
|
|
*
|
|
@@ -695,18 +812,11 @@ static int data_section(const char *name)
|
|
|
* This pattern is identified by
|
|
|
* refsymname = __init_begin, _sinittext, _einittext
|
|
|
*
|
|
|
- * Pattern 5:
|
|
|
- * Xtensa uses literal sections for constants that are accessed PC-relative.
|
|
|
- * Literal sections may safely reference their text sections.
|
|
|
- * (Note that the name for the literal section omits any trailing '.text')
|
|
|
- * tosec = <section>[.text]
|
|
|
- * fromsec = <section>.literal
|
|
|
**/
|
|
|
static int secref_whitelist(const char *modname, const char *tosec,
|
|
|
const char *fromsec, const char *atsym,
|
|
|
const char *refsymname)
|
|
|
{
|
|
|
- int len;
|
|
|
const char **s;
|
|
|
const char *pat2sym[] = {
|
|
|
"driver",
|
|
@@ -757,15 +867,6 @@ static int secref_whitelist(const char *modname, const char *tosec,
|
|
|
if (strcmp(refsymname, *s) == 0)
|
|
|
return 1;
|
|
|
|
|
|
- /* Check for pattern 5 */
|
|
|
- if (strrcmp(tosec, ".text") == 0)
|
|
|
- len = strlen(tosec) - strlen(".text");
|
|
|
- else
|
|
|
- len = strlen(tosec);
|
|
|
- if ((strncmp(tosec, fromsec, len) == 0) && (strlen(fromsec) > len) &&
|
|
|
- (strcmp(fromsec + len, ".literal") == 0))
|
|
|
- return 1;
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1011,8 +1112,7 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r)
|
|
|
}
|
|
|
|
|
|
static void section_rela(const char *modname, struct elf_info *elf,
|
|
|
- Elf_Shdr *sechdr, int section(const char *),
|
|
|
- int section_ref_ok(const char *))
|
|
|
+ Elf_Shdr *sechdr)
|
|
|
{
|
|
|
Elf_Sym *sym;
|
|
|
Elf_Rela *rela;
|
|
@@ -1031,7 +1131,7 @@ static void section_rela(const char *modname, struct elf_info *elf,
|
|
|
fromsec = secstrings + sechdr->sh_name;
|
|
|
fromsec += strlen(".rela");
|
|
|
/* if from section (name) is know good then skip it */
|
|
|
- if (section_ref_ok(fromsec))
|
|
|
+ if (match(fromsec, section_white_list))
|
|
|
return;
|
|
|
|
|
|
for (rela = start; rela < stop; rela++) {
|
|
@@ -1059,14 +1159,13 @@ static void section_rela(const char *modname, struct elf_info *elf,
|
|
|
|
|
|
tosec = secstrings +
|
|
|
elf->sechdrs[sym->st_shndx].sh_name;
|
|
|
- if (section(tosec))
|
|
|
+ if (section_mismatch(fromsec, tosec))
|
|
|
warn_sec_mismatch(modname, fromsec, elf, sym, r);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void section_rel(const char *modname, struct elf_info *elf,
|
|
|
- Elf_Shdr *sechdr, int section(const char *),
|
|
|
- int section_ref_ok(const char *))
|
|
|
+ Elf_Shdr *sechdr)
|
|
|
{
|
|
|
Elf_Sym *sym;
|
|
|
Elf_Rel *rel;
|
|
@@ -1085,7 +1184,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
|
|
|
fromsec = secstrings + sechdr->sh_name;
|
|
|
fromsec += strlen(".rel");
|
|
|
/* if from section (name) is know good then skip it */
|
|
|
- if (section_ref_ok(fromsec))
|
|
|
+ if (match(fromsec, section_white_list))
|
|
|
return;
|
|
|
|
|
|
for (rel = start; rel < stop; rel++) {
|
|
@@ -1127,7 +1226,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
|
|
|
|
|
|
tosec = secstrings +
|
|
|
elf->sechdrs[sym->st_shndx].sh_name;
|
|
|
- if (section(tosec))
|
|
|
+ if (section_mismatch(fromsec, tosec))
|
|
|
warn_sec_mismatch(modname, fromsec, elf, sym, r);
|
|
|
}
|
|
|
}
|
|
@@ -1145,9 +1244,7 @@ static void section_rel(const char *modname, struct elf_info *elf,
|
|
|
* 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 *))
|
|
|
+ struct elf_info *elf)
|
|
|
{
|
|
|
int i;
|
|
|
Elf_Ehdr *hdr = elf->hdr;
|
|
@@ -1157,156 +1254,12 @@ static void check_sec_ref(struct module *mod, const char *modname,
|
|
|
for (i = 0; i < 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],
|
|
|
- section, section_ref_ok);
|
|
|
+ section_rela(modname, elf, &elf->sechdrs[i]);
|
|
|
else if (sechdrs[i].sh_type == SHT_REL)
|
|
|
- section_rel(modname, elf, &elf->sechdrs[i],
|
|
|
- section, section_ref_ok);
|
|
|
+ section_rel(modname, elf, &elf->sechdrs[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Identify sections from which references to either a
|
|
|
- * .init or 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.
|
|
|
- * [.rodata] like for .init.text we ignore .rodata references -same reason
|
|
|
- */
|
|
|
-static int initexit_section_ref_ok(const char *name)
|
|
|
-{
|
|
|
- const char **s;
|
|
|
- /* Absolute section names */
|
|
|
- const char *namelist1[] = {
|
|
|
- "__bug_table", /* used by powerpc for BUG() */
|
|
|
- "__ex_table",
|
|
|
- ".altinstructions",
|
|
|
- ".cranges", /* used by sh64 */
|
|
|
- ".fixup",
|
|
|
- ".machvec", /* ia64 + powerpc uses these */
|
|
|
- ".machine.desc",
|
|
|
- ".opd", /* See comment [OPD] */
|
|
|
- "__dbe_table",
|
|
|
- ".parainstructions",
|
|
|
- ".pdr",
|
|
|
- ".plt", /* seen on ARCH=um build on x86_64. Harmless */
|
|
|
- ".smp_locks",
|
|
|
- ".stab",
|
|
|
- ".m68k_fixup",
|
|
|
- ".xt.prop", /* xtensa informational section */
|
|
|
- ".xt.lit", /* xtensa informational section */
|
|
|
- NULL
|
|
|
- };
|
|
|
- /* Start of section names */
|
|
|
- const char *namelist2[] = {
|
|
|
- ".debug",
|
|
|
- ".eh_frame",
|
|
|
- ".note", /* ignore ELF notes - may contain anything */
|
|
|
- ".got", /* powerpc - global offset table */
|
|
|
- ".toc", /* powerpc - table of contents */
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
- * 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[] = {
|
|
|
- "__dbe_table", /* MIPS generate these */
|
|
|
- "__ftr_fixup", /* powerpc cpu feature fixup */
|
|
|
- "__fw_ftr_fixup", /* powerpc firmware feature fixup */
|
|
|
- "__param",
|
|
|
- ".data.rel.ro", /* used by parisc64 */
|
|
|
- ".init",
|
|
|
- ".text.lock",
|
|
|
- NULL
|
|
|
- };
|
|
|
- /* Start of section names */
|
|
|
- const char *namelist2[] = {
|
|
|
- ".init.",
|
|
|
- ".pci_fixup",
|
|
|
- ".rodata",
|
|
|
- NULL
|
|
|
- };
|
|
|
-
|
|
|
- if (initexit_section_ref_ok(name))
|
|
|
- return 1;
|
|
|
-
|
|
|
- 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;
|
|
|
-
|
|
|
- /* If section name ends with ".init" we allow references
|
|
|
- * as is the case with .initcallN.init, .early_param.init,
|
|
|
- * .taglist.init etc
|
|
|
- */
|
|
|
- if (strrcmp(name, ".init") == 0)
|
|
|
- return 1;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * Identify sections from which references to a .exit section is OK.
|
|
|
- */
|
|
|
-static int exit_section_ref_ok(const char *name)
|
|
|
-{
|
|
|
- const char **s;
|
|
|
- /* Absolute section names */
|
|
|
- const char *namelist1[] = {
|
|
|
- ".exit.data",
|
|
|
- ".exit.text",
|
|
|
- ".exitcall.exit",
|
|
|
- ".rodata",
|
|
|
- NULL
|
|
|
- };
|
|
|
-
|
|
|
- if (initexit_section_ref_ok(name))
|
|
|
- return 1;
|
|
|
-
|
|
|
- for (s = namelist1; *s; s++)
|
|
|
- if (strcmp(*s, name) == 0)
|
|
|
- return 1;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
static void read_symbols(char *modname)
|
|
|
{
|
|
|
const char *symname;
|
|
@@ -1347,10 +1300,8 @@ static void read_symbols(char *modname)
|
|
|
handle_moddevtable(mod, &info, sym, symname);
|
|
|
}
|
|
|
if (!is_vmlinux(modname) ||
|
|
|
- (is_vmlinux(modname) && vmlinux_section_warnings)) {
|
|
|
- check_sec_ref(mod, modname, &info, init_section, init_section_ref_ok);
|
|
|
- check_sec_ref(mod, modname, &info, exit_section, exit_section_ref_ok);
|
|
|
- }
|
|
|
+ (is_vmlinux(modname) && vmlinux_section_warnings))
|
|
|
+ check_sec_ref(mod, modname, &info);
|
|
|
|
|
|
version = get_modinfo(info.modinfo, info.modinfo_len, "version");
|
|
|
if (version)
|