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
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 };
*/
{
.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 }
}
};
return 0;
}
-
/**
* 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;
}
/**
* 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);
+ }
}
}
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);
/* 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);
}
}
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))
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);
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;
/* 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);
}
}
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]);