diff options
Diffstat (limited to 'scripts/mod/modpost.c')
-rw-r--r-- | scripts/mod/modpost.c | 201 |
1 files changed, 49 insertions, 152 deletions
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index ea72af229d2b..973b5e5ae2dd 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -22,7 +22,6 @@ #include <errno.h> #include "modpost.h" #include "../../include/linux/license.h" -#include "../../include/linux/module_symbol.h" static bool module_enabled; /* Are we using CONFIG_MODVERSIONS? */ @@ -577,11 +576,14 @@ static int parse_elf(struct elf_info *info, const char *filename) *p = TO_NATIVE(*p); } + symsearch_init(info); + return 1; } static void parse_elf_finish(struct elf_info *info) { + symsearch_finish(info); release_file(info->hdr, info->size); } @@ -792,24 +794,16 @@ static void check_section(const char *modname, struct elf_info *elf, #define ALL_INIT_DATA_SECTIONS \ ".init.setup", ".init.rodata", ".meminit.rodata", \ ".init.data", ".meminit.data" -#define ALL_EXIT_DATA_SECTIONS \ - ".exit.data", ".memexit.data" - -#define ALL_INIT_TEXT_SECTIONS \ - ".init.text", ".meminit.text" -#define ALL_EXIT_TEXT_SECTIONS \ - ".exit.text", ".memexit.text" #define ALL_PCI_INIT_SECTIONS \ ".pci_fixup_early", ".pci_fixup_header", ".pci_fixup_final", \ ".pci_fixup_enable", ".pci_fixup_resume", \ ".pci_fixup_resume_early", ".pci_fixup_suspend" -#define ALL_XXXINIT_SECTIONS MEM_INIT_SECTIONS -#define ALL_XXXEXIT_SECTIONS MEM_EXIT_SECTIONS +#define ALL_XXXINIT_SECTIONS ".meminit.*" #define ALL_INIT_SECTIONS INIT_SECTIONS, ALL_XXXINIT_SECTIONS -#define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS +#define ALL_EXIT_SECTIONS ".exit.*" #define DATA_SECTIONS ".data", ".data.rel" #define TEXT_SECTIONS ".text", ".text.*", ".sched.text", \ @@ -819,20 +813,13 @@ static void check_section(const char *modname, struct elf_info *elf, ".coldtext", ".softirqentry.text" #define INIT_SECTIONS ".init.*" -#define MEM_INIT_SECTIONS ".meminit.*" -#define EXIT_SECTIONS ".exit.*" -#define MEM_EXIT_SECTIONS ".memexit.*" - -#define ALL_TEXT_SECTIONS ALL_INIT_TEXT_SECTIONS, ALL_EXIT_TEXT_SECTIONS, \ +#define ALL_TEXT_SECTIONS ".init.text", ".meminit.text", ".exit.text", \ TEXT_SECTIONS, OTHER_TEXT_SECTIONS enum mismatch { - TEXT_TO_ANY_INIT, - DATA_TO_ANY_INIT, - TEXTDATA_TO_ANY_EXIT, + TEXTDATA_TO_ANY_INIT_EXIT, XXXINIT_TO_SOME_INIT, - XXXEXIT_TO_SOME_EXIT, ANY_INIT_TO_ANY_EXIT, ANY_EXIT_TO_ANY_INIT, EXTABLE_TO_NON_TEXT, @@ -863,19 +850,9 @@ static const struct sectioncheck sectioncheck[] = { * normal code and data */ { - .fromsec = { TEXT_SECTIONS, NULL }, - .bad_tosec = { ALL_INIT_SECTIONS, NULL }, - .mismatch = TEXT_TO_ANY_INIT, -}, -{ - .fromsec = { DATA_SECTIONS, NULL }, - .bad_tosec = { ALL_XXXINIT_SECTIONS, INIT_SECTIONS, NULL }, - .mismatch = DATA_TO_ANY_INIT, -}, -{ .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL }, - .bad_tosec = { ALL_EXIT_SECTIONS, NULL }, - .mismatch = TEXTDATA_TO_ANY_EXIT, + .bad_tosec = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }, + .mismatch = TEXTDATA_TO_ANY_INIT_EXIT, }, /* Do not reference init code/data from meminit code/data */ { @@ -883,12 +860,6 @@ static const struct sectioncheck sectioncheck[] = { .bad_tosec = { INIT_SECTIONS, NULL }, .mismatch = XXXINIT_TO_SOME_INIT, }, -/* Do not reference exit code/data from memexit code/data */ -{ - .fromsec = { ALL_XXXEXIT_SECTIONS, NULL }, - .bad_tosec = { EXIT_SECTIONS, NULL }, - .mismatch = XXXEXIT_TO_SOME_EXIT, -}, /* Do not use exit code/data from init code */ { .fromsec = { ALL_INIT_SECTIONS, NULL }, @@ -1006,19 +977,7 @@ static int secref_whitelist(const char *fromsec, const char *fromsym, /* symbols in data sections that may refer to any init/exit sections */ if (match(fromsec, PATTERNS(DATA_SECTIONS)) && match(tosec, PATTERNS(ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS)) && - match(fromsym, PATTERNS("*_template", // scsi uses *_template a lot - "*_timer", // arm uses ops structures named _timer a lot - "*_sht", // scsi also used *_sht to some extent - "*_ops", - "*_probe", - "*_probe_one", - "*_console"))) - return 0; - - /* symbols in data sections that may refer to meminit sections */ - if (match(fromsec, PATTERNS(DATA_SECTIONS)) && - match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS, ALL_XXXEXIT_SECTIONS)) && - match(fromsym, PATTERNS("*driver"))) + match(fromsym, PATTERNS("*_ops", "*_probe", "*_console"))) return 0; /* @@ -1028,7 +987,7 @@ static int secref_whitelist(const char *fromsec, const char *fromsym, */ if (!extra_warn && match(fromsec, PATTERNS(DATA_SECTIONS)) && - match(tosec, PATTERNS(EXIT_SECTIONS)) && + match(tosec, PATTERNS(ALL_EXIT_SECTIONS)) && match(fromsym, PATTERNS("*driver"))) return 0; @@ -1050,71 +1009,10 @@ static int secref_whitelist(const char *fromsec, const char *fromsym, return 1; } -/* - * If there's no name there, ignore it; likewise, ignore it if it's - * one of the magic symbols emitted used by current tools. - * - * Otherwise if find_symbols_between() returns those symbols, they'll - * fail the whitelist tests and cause lots of false alarms ... fixable - * only by merging __exit and __init sections into __text, bloating - * the kernel (which is especially evil on embedded platforms). - */ -static inline bool is_valid_name(struct elf_info *elf, Elf_Sym *sym) -{ - const char *name = elf->strtab + sym->st_name; - - if (!name || !strlen(name)) - return false; - return !is_mapping_symbol(name); -} - -/* Look up the nearest symbol based on the section and the address */ -static Elf_Sym *find_nearest_sym(struct elf_info *elf, Elf_Addr addr, - unsigned int secndx, bool allow_negative, - Elf_Addr min_distance) -{ - Elf_Sym *sym; - Elf_Sym *near = NULL; - Elf_Addr sym_addr, distance; - bool is_arm = (elf->hdr->e_machine == EM_ARM); - - for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) { - if (get_secindex(elf, sym) != secndx) - continue; - if (!is_valid_name(elf, sym)) - continue; - - sym_addr = sym->st_value; - - /* - * For ARM Thumb instruction, the bit 0 of st_value is set - * if the symbol is STT_FUNC type. Mask it to get the address. - */ - if (is_arm && ELF_ST_TYPE(sym->st_info) == STT_FUNC) - sym_addr &= ~1; - - if (addr >= sym_addr) - distance = addr - sym_addr; - else if (allow_negative) - distance = sym_addr - addr; - else - continue; - - if (distance <= min_distance) { - min_distance = distance; - near = sym; - } - - if (min_distance == 0) - break; - } - return near; -} - static Elf_Sym *find_fromsym(struct elf_info *elf, Elf_Addr addr, unsigned int secndx) { - return find_nearest_sym(elf, addr, secndx, false, ~0); + return symsearch_find_nearest(elf, addr, secndx, false, ~0); } static Elf_Sym *find_tosym(struct elf_info *elf, Elf_Addr addr, Elf_Sym *sym) @@ -1127,7 +1025,8 @@ static Elf_Sym *find_tosym(struct elf_info *elf, Elf_Addr addr, Elf_Sym *sym) * Strive to find a better symbol name, but the resulting name may not * match the symbol referenced in the original code. */ - return find_nearest_sym(elf, addr, get_secindex(elf, sym), true, 20); + return symsearch_find_nearest(elf, addr, get_secindex(elf, sym), + true, 20); } static bool is_executable_section(struct elf_info *elf, unsigned int secndx) @@ -1248,10 +1147,10 @@ static void check_export_symbol(struct module *mod, struct elf_info *elf, ELF_ST_TYPE(sym->st_info) == STT_LOPROC) s->is_func = true; - if (match(secname, PATTERNS(INIT_SECTIONS))) + if (match(secname, PATTERNS(ALL_INIT_SECTIONS))) warn("%s: %s: EXPORT_SYMBOL used for init symbol. Remove __init or EXPORT_SYMBOL.\n", mod->name, name); - else if (match(secname, PATTERNS(EXIT_SECTIONS))) + else if (match(secname, PATTERNS(ALL_EXIT_SECTIONS))) warn("%s: %s: EXPORT_SYMBOL used for exit symbol. Remove __exit or EXPORT_SYMBOL.\n", mod->name, name); } @@ -1468,32 +1367,20 @@ static void get_rel_type_and_sym(struct elf_info *elf, uint64_t r_info, return; } - if (is_64bit) { - Elf64_Xword r_info64 = r_info; - - r_info = TO_NATIVE(r_info64); - } else { - Elf32_Word r_info32 = r_info; - - r_info = TO_NATIVE(r_info32); - } + if (is_64bit) + r_info = TO_NATIVE((Elf64_Xword)r_info); + else + r_info = TO_NATIVE((Elf32_Word)r_info); *r_type = ELF_R_TYPE(r_info); *r_sym = ELF_R_SYM(r_info); } static void section_rela(struct module *mod, struct elf_info *elf, - Elf_Shdr *sechdr) + unsigned int fsecndx, const char *fromsec, + const Elf_Rela *start, const Elf_Rela *stop) { - Elf_Rela *rela; - unsigned int fsecndx = sechdr->sh_info; - const char *fromsec = sec_name(elf, fsecndx); - Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset; - Elf_Rela *stop = (void *)start + sechdr->sh_size; - - /* if from section (name) is know good then skip it */ - if (match(fromsec, section_white_list)) - return; + const Elf_Rela *rela; for (rela = start; rela < stop; rela++) { Elf_Addr taddr, r_offset; @@ -1523,17 +1410,10 @@ static void section_rela(struct module *mod, struct elf_info *elf, } static void section_rel(struct module *mod, struct elf_info *elf, - Elf_Shdr *sechdr) + unsigned int fsecndx, const char *fromsec, + const Elf_Rel *start, const Elf_Rel *stop) { - Elf_Rel *rel; - unsigned int fsecndx = sechdr->sh_info; - const char *fromsec = sec_name(elf, fsecndx); - Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset; - Elf_Rel *stop = (void *)start + sechdr->sh_size; - - /* if from section (name) is know good then skip it */ - if (match(fromsec, section_white_list)) - return; + const Elf_Rel *rel; for (rel = start; rel < stop; rel++) { Elf_Sym *tsym; @@ -1581,16 +1461,33 @@ static void section_rel(struct module *mod, struct elf_info *elf, static void check_sec_ref(struct module *mod, struct elf_info *elf) { int i; - Elf_Shdr *sechdrs = elf->sechdrs; /* Walk through all sections */ for (i = 0; i < elf->num_sections; i++) { - check_section(mod->name, elf, &elf->sechdrs[i]); + Elf_Shdr *sechdr = &elf->sechdrs[i]; + + check_section(mod->name, elf, sechdr); /* We want to process only relocation sections and not .init */ - if (sechdrs[i].sh_type == SHT_RELA) - section_rela(mod, elf, &elf->sechdrs[i]); - else if (sechdrs[i].sh_type == SHT_REL) - section_rel(mod, elf, &elf->sechdrs[i]); + if (sechdr->sh_type == SHT_REL || sechdr->sh_type == SHT_RELA) { + /* section to which the relocation applies */ + unsigned int secndx = sechdr->sh_info; + const char *secname = sec_name(elf, secndx); + const void *start, *stop; + + /* If the section is known good, skip it */ + if (match(secname, section_white_list)) + continue; + + start = sym_get_data_by_offset(elf, i, 0); + stop = start + sechdr->sh_size; + + if (sechdr->sh_type == SHT_RELA) + section_rela(mod, elf, secndx, secname, + start, stop); + else + section_rel(mod, elf, secndx, secname, + start, stop); + } } } |