If the config option CONFIG_SECTION_MISMATCH is not set and we see a Section mismatch present the following to the user: modpost: Found 1 section mismatch(es). To see additional details select "Enable full Section mismatch analysis" in the Kernel Hacking menu (CONFIG_SECTION_MISMATCH). If the option CONFIG_SECTION_MISMATCH is selected then be verbose in the Section mismatch reporting from mdopost. Sample outputs: WARNING: o-x86_64/vmlinux.o(.text+0x7396): Section mismatch in reference from the function discover_ebda() to the variable .init.data:ebda_addr The function discover_ebda() references the variable __initdata ebda_addr. This is often because discover_ebda lacks a __initdata annotation or the annotation of ebda_addr is wrong. WARNING: o-x86_64/vmlinux.o(.data+0x74d58): Section mismatch in reference from the variable pci_serial_quirks to the function .devexit.text:pci_plx9050_exit() The variable pci_serial_quirks references the function __devexit pci_plx9050_exit() If the reference is valid then annotate the variable with __exit* (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console, WARNING: o-x86_64/vmlinux.o(__ksymtab+0x630): Section mismatch in reference from the variable __ksymtab_arch_register_cpu to the function .cpuinit.text:arch_register_cpu() The symbol arch_register_cpu is exported and annotated __cpuinit Fix this by removing the __cpuinit annotation of arch_register_cpu or drop the export.
Showing the changes to scripts (2 of 3 files). Show all
Unified · Split
| @@ -62,6 +62,7 @@ modpost = scripts/mod/modpost \ · 61 unmodified lines | ||
| 62 | 62 | $(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile) \ |
| 63 | 63 | $(if $(KBUILD_EXTMOD),-I $(modulesymfile)) \ |
| 64 | 64 | $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ |
| 65 | + $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ | |
| 65 | 66 | $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) |
| 66 | 67 | |
| 67 | 68 | quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules |
| @@ -28,6 +28,9 @@ static int vmlinux_section_warnings = 1; · 27 unmodified lines | ||
| 28 | 28 | /* Only warn about unresolved symbols */ |
| 29 | 29 | static int warn_unresolved = 0; |
| 30 | 30 | /* How a symbol is exported */ |
| 31 | +static int sec_mismatch_count = 0; | |
| 32 | +static int sec_mismatch_verbose = 1; | |
| 33 | + | |
| 31 | 34 | enum export { |
| 32 | 35 | export_plain, export_unused, export_gpl, |
| 33 | 36 | export_unused_gpl, export_gpl_future, export_unknown |
| @@ -760,9 +763,23 @@ static const char *head_sections[] = { ".head.text*", NULL }; · 726 unmodified lines | ||
| 760 | 763 | static const char *linker_symbols[] = |
| 761 | 764 | { "__init_begin", "_sinittext", "_einittext", NULL }; |
| 762 | 765 | |
| 766 | +enum mismatch { | |
| 767 | + NO_MISMATCH, | |
| 768 | + TEXT_TO_INIT, | |
| 769 | + DATA_TO_INIT, | |
| 770 | + TEXT_TO_EXIT, | |
| 771 | + DATA_TO_EXIT, | |
| 772 | + XXXINIT_TO_INIT, | |
| 773 | + XXXEXIT_TO_EXIT, | |
| 774 | + INIT_TO_EXIT, | |
| 775 | + EXIT_TO_INIT, | |
| 776 | + EXPORT_TO_INIT_EXIT, | |
| 777 | +}; | |
| 778 | + | |
| 763 | 779 | struct sectioncheck { |
| 764 | 780 | const char *fromsec[20]; |
| 765 | 781 | const char *tosec[20]; |
| 782 | + enum mismatch mismatch; | |
| 766 | 783 | }; |
| 767 | 784 | |
| 768 | 785 | const struct sectioncheck sectioncheck[] = { |
| @@ -770,33 +787,54 @@ const struct sectioncheck sectioncheck[] = { · 1 unmodified line | ||
| 770 | 787 | * normal code and data |
| 771 | 788 | */ |
| 772 | 789 | { |
| 773 | − .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL }, | |
| 774 | − .tosec = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL } | |
| 790 | + .fromsec = { TEXT_SECTIONS, NULL }, | |
| 791 | + .tosec = { ALL_INIT_SECTIONS, NULL }, | |
| 792 | + .mismatch = TEXT_TO_INIT, | |
| 793 | +}, | |
| 794 | +{ | |
| 795 | + .fromsec = { DATA_SECTIONS, NULL }, | |
| 796 | + .tosec = { ALL_INIT_SECTIONS, NULL }, | |
| 797 | + .mismatch = DATA_TO_INIT, | |
| 798 | +}, | |
| 799 | +{ | |
| 800 | + .fromsec = { TEXT_SECTIONS, NULL }, | |
| 801 | + .tosec = { ALL_EXIT_SECTIONS, NULL }, | |
| 802 | + .mismatch = TEXT_TO_EXIT, | |
| 803 | +}, | |
| 804 | +{ | |
| 805 | + .fromsec = { DATA_SECTIONS, NULL }, | |
| 806 | + .tosec = { ALL_EXIT_SECTIONS, NULL }, | |
| 807 | + .mismatch = DATA_TO_EXIT, | |
| 775 | 808 | }, |
| 776 | 809 | /* Do not reference init code/data from devinit/cpuinit/meminit code/data */ |
| 777 | 810 | { |
| 778 | 811 | .fromsec = { DEV_INIT_SECTIONS, CPU_INIT_SECTIONS, MEM_INIT_SECTIONS, NULL }, |
| 779 | − .tosec = { INIT_SECTIONS, NULL } | |
| 812 | + .tosec = { INIT_SECTIONS, NULL }, | |
| 813 | + .mismatch = XXXINIT_TO_INIT, | |
| 780 | 814 | }, |
| 781 | 815 | /* Do not reference exit code/data from devexit/cpuexit/memexit code/data */ |
| 782 | 816 | { |
| 783 | 817 | .fromsec = { DEV_EXIT_SECTIONS, CPU_EXIT_SECTIONS, MEM_EXIT_SECTIONS, NULL }, |
| 784 | − .tosec = { EXIT_SECTIONS, NULL } | |
| 818 | + .tosec = { EXIT_SECTIONS, NULL }, | |
| 819 | + .mismatch = XXXEXIT_TO_EXIT, | |
| 785 | 820 | }, |
| 786 | 821 | /* Do not use exit code/data from init code */ |
| 787 | 822 | { |
| 788 | 823 | .fromsec = { ALL_INIT_SECTIONS, NULL }, |
| 789 | 824 | .tosec = { ALL_EXIT_SECTIONS, NULL }, |
| 825 | + .mismatch = INIT_TO_EXIT, | |
| 790 | 826 | }, |
| 791 | 827 | /* Do not use init code/data from exit code */ |
| 792 | 828 | { |
| 793 | 829 | .fromsec = { ALL_EXIT_SECTIONS, NULL }, |
| 794 | − .tosec = { ALL_INIT_SECTIONS, NULL } | |
| 830 | + .tosec = { ALL_INIT_SECTIONS, NULL }, | |
| 831 | + .mismatch = EXIT_TO_INIT, | |
| 795 | 832 | }, |
| 796 | 833 | /* Do not export init/exit functions or data */ |
| 797 | 834 | { |
| 798 | 835 | .fromsec = { "__ksymtab*", NULL }, |
| 799 | − .tosec = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL } | |
| 836 | + .tosec = { ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL }, | |
| 837 | + .mismatch = EXPORT_TO_INIT_EXIT | |
| 800 | 838 | } |
| 801 | 839 | }; |
| 802 | 840 | |
| @@ -809,10 +847,10 @@ static int section_mismatch(const char *fromsec, const char *tosec) · 6 unmodified lines | ||
| 809 | 847 | for (i = 0; i < elems; i++) { |
| 810 | 848 | if (match(fromsec, check->fromsec) && |
| 811 | 849 | match(tosec, check->tosec)) |
| 812 | − return 1; | |
| 850 | + return check->mismatch; | |
| 813 | 851 | check++; |
| 814 | 852 | } |
| 815 | − return 0; | |
| 853 | + return NO_MISMATCH; | |
| 816 | 854 | } |
| 817 | 855 | |
| 818 | 856 | /** |
| @@ -988,48 +1026,198 @@ static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr, · 169 unmodified lines | ||
| 988 | 1026 | return near; |
| 989 | 1027 | } |
| 990 | 1028 | |
| 1029 | +/* | |
| 1030 | + * Convert a section name to the function/data attribute | |
| 1031 | + * .init.text => __init | |
| 1032 | + * .cpuinit.data => __cpudata | |
| 1033 | + * .memexitconst => __memconst | |
| 1034 | + * etc. | |
| 1035 | +*/ | |
| 1036 | +static char *sec2annotation(const char *s) | |
| 1037 | +{ | |
| 1038 | + if (match(s, init_exit_sections)) { | |
| 1039 | + char *p = malloc(20); | |
| 1040 | + char *r = p; | |
| 1041 | + | |
| 1042 | + *p++ = '_'; | |
| 1043 | + *p++ = '_'; | |
| 1044 | + if (*s == '.') | |
| 1045 | + s++; | |
| 1046 | + while (*s && *s != '.') | |
| 1047 | + *p++ = *s++; | |
| 1048 | + *p = '\0'; | |
| 1049 | + if (*s == '.') | |
| 1050 | + s++; | |
| 1051 | + if (strstr(s, "rodata") != NULL) | |
| 1052 | + strcat(p, "const "); | |
| 1053 | + else if (strstr(s, "data") != NULL) | |
| 1054 | + strcat(p, "data "); | |
| 1055 | + else | |
| 1056 | + strcat(p, " "); | |
| 1057 | + return r; /* we leak her but we do not care */ | |
| 1058 | + } else { | |
| 1059 | + return ""; | |
| 1060 | + } | |
| 1061 | +} | |
| 1062 | + | |
| 1063 | +static int is_function(Elf_Sym *sym) | |
| 1064 | +{ | |
| 1065 | + if (sym) | |
| 1066 | + return ELF_ST_TYPE(sym->st_info) == STT_FUNC; | |
| 1067 | + else | |
| 1068 | + return 0; | |
| 1069 | +} | |
| 1070 | + | |
| 991 | 1071 | /* |
| 992 | 1072 | * Print a warning about a section mismatch. |
| 993 | 1073 | * Try to find symbols near it so user can find it. |
| 994 | 1074 | * Check whitelist before warning - it may be a false positive. |
| 995 | 1075 | */ |
| 996 | −static void report_sec_mismatch(const char *modname, | |
| 1076 | +static void report_sec_mismatch(const char *modname, enum mismatch mismatch, | |
| 997 | 1077 | const char *fromsec, |
| 998 | 1078 | unsigned long long fromaddr, |
| 999 | 1079 | const char *fromsym, |
| 1000 | − const char *tosec, const char *tosym) | |
| 1001 | −{ | |
| 1002 | − if (strlen(tosym)) { | |
| 1003 | − warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s " | |
| 1004 | − "in '%s'\n", | |
| 1005 | − modname, fromsec, fromaddr, | |
| 1006 | − tosec, tosym, fromsym); | |
| 1007 | − } else { | |
| 1008 | − warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s\n", | |
| 1009 | − modname, fromsec, fromaddr, | |
| 1010 | − tosec, tosym); | |
| 1080 | + int from_is_func, | |
| 1081 | + const char *tosec, const char *tosym, | |
| 1082 | + int to_is_func) | |
| 1083 | +{ | |
| 1084 | + const char *from, *from_p; | |
| 1085 | + const char *to, *to_p; | |
| 1086 | + from = from_is_func ? "function" : "variable"; | |
| 1087 | + from_p = from_is_func ? "()" : ""; | |
| 1088 | + to = to_is_func ? "function" : "variable"; | |
| 1089 | + to_p = to_is_func ? "()" : ""; | |
| 1090 | + | |
| 1091 | + fprintf(stderr, "WARNING: %s(%s+0x%llx): Section mismatch in" | |
| 1092 | + " reference from the %s %s%s to the %s %s:%s%s\n", | |
| 1093 | + modname, fromsec, fromaddr, from, fromsym, from_p, | |
| 1094 | + to, tosec, tosym, to_p); | |
| 1095 | + | |
| 1096 | + sec_mismatch_count++; | |
| 1097 | + if (!sec_mismatch_verbose) | |
| 1098 | + return; | |
| 1099 | + | |
| 1100 | + switch (mismatch) { | |
| 1101 | + case TEXT_TO_INIT: | |
| 1102 | + fprintf(stderr, | |
| 1103 | + "The function %s %s() references\n" | |
| 1104 | + "the %s %s%s%s.\n" | |
| 1105 | + "This is often because %s lacks a %s\n" | |
| 1106 | + "annotation or the annotation of %s is wrong.\n", | |
| 1107 | + sec2annotation(fromsec), fromsym, | |
| 1108 | + to, sec2annotation(tosec), tosym, to_p, | |
| 1109 | + fromsym, sec2annotation(tosec), tosym); | |
| 1110 | + break; | |
| 1111 | + case DATA_TO_INIT: { | |
| 1112 | + const char **s = symbol_white_list; | |
| 1113 | + fprintf(stderr, | |
| 1114 | + "The variable %s references\n" | |
| 1115 | + "the %s %s%s%s\n" | |
| 1116 | + "If the reference is valid then annotate the\n" | |
| 1117 | + "variable with __init* (see linux/init.h) " | |
| 1118 | + "or name the variable:\n", | |
| 1119 | + fromsym, to, sec2annotation(tosec), tosym, to_p); | |
| 1120 | + while (*s) | |
| 1121 | + fprintf(stderr, "%s, ", *s++); | |
| 1122 | + fprintf(stderr, "\n"); | |
| 1123 | + break; | |
| 1011 | 1124 | } |
| 1125 | + case TEXT_TO_EXIT: | |
| 1126 | + fprintf(stderr, | |
| 1127 | + "The function %s() references a %s in an exit section.\n" | |
| 1128 | + "Often the %s %s%s has valid usage outside the exit section\n" | |
| 1129 | + "and the fix is to remove the %sannotation of %s.\n", | |
| 1130 | + fromsym, to, to, tosym, to_p, sec2annotation(tosec), tosym); | |
| 1131 | + break; | |
| 1132 | + case DATA_TO_EXIT: { | |
| 1133 | + const char **s = symbol_white_list; | |
| 1134 | + fprintf(stderr, | |
| 1135 | + "The variable %s references\n" | |
| 1136 | + "the %s %s%s%s\n" | |
| 1137 | + "If the reference is valid then annotate the\n" | |
| 1138 | + "variable with __exit* (see linux/init.h) or " | |
| 1139 | + "name the variable:\n", | |
| 1140 | + fromsym, to, sec2annotation(tosec), tosym, to_p); | |
| 1141 | + while (*s) | |
| 1142 | + fprintf(stderr, "%s, ", *s++); | |
| 1143 | + fprintf(stderr, "\n"); | |
| 1144 | + break; | |
| 1145 | + } | |
| 1146 | + case XXXINIT_TO_INIT: | |
| 1147 | + case XXXEXIT_TO_EXIT: | |
| 1148 | + fprintf(stderr, | |
| 1149 | + "The %s %s%s%s references\n" | |
| 1150 | + "a %s %s%s%s.\n" | |
| 1151 | + "If %s is only used by %s then\n" | |
| 1152 | + "annotate %s with a matching annotation.\n", | |
| 1153 | + from, sec2annotation(fromsec), fromsym, from_p, | |
| 1154 | + to, sec2annotation(tosec), tosym, to_p, | |
| 1155 | + fromsym, tosym, fromsym); | |
| 1156 | + break; | |
| 1157 | + case INIT_TO_EXIT: | |
| 1158 | + fprintf(stderr, | |
| 1159 | + "The %s %s%s%s references\n" | |
| 1160 | + "a %s %s%s%s.\n" | |
| 1161 | + "This is often seen when error handling " | |
| 1162 | + "in the init function\n" | |
| 1163 | + "uses functionality in the exit path.\n" | |
| 1164 | + "The fix is often to remove the %sannotation of\n" | |
| 1165 | + "%s%s so it may be used outside an exit section.\n", | |
| 1166 | + from, sec2annotation(fromsec), fromsym, from_p, | |
| 1167 | + to, sec2annotation(tosec), tosym, to_p, | |
| 1168 | + sec2annotation(tosec), tosym, to_p); | |
| 1169 | + break; | |
| 1170 | + case EXIT_TO_INIT: | |
| 1171 | + fprintf(stderr, | |
| 1172 | + "The %s %s%s%s references\n" | |
| 1173 | + "a %s %s%s%s.\n" | |
| 1174 | + "This is often seen when error handling " | |
| 1175 | + "in the exit function\n" | |
| 1176 | + "uses functionality in the init path.\n" | |
| 1177 | + "The fix is often to remove the %sannotation of\n" | |
| 1178 | + "%s%s so it may be used outside an init section.\n", | |
| 1179 | + from, sec2annotation(fromsec), fromsym, from_p, | |
| 1180 | + to, sec2annotation(tosec), tosym, to_p, | |
| 1181 | + sec2annotation(tosec), tosym, to_p); | |
| 1182 | + break; | |
| 1183 | + case EXPORT_TO_INIT_EXIT: | |
| 1184 | + fprintf(stderr, | |
| 1185 | + "The symbol %s is exported and annotated %s\n" | |
| 1186 | + "Fix this by removing the %sannotation of %s " | |
| 1187 | + "or drop the export.\n", | |
| 1188 | + tosym, sec2annotation(tosec), sec2annotation(tosec), tosym); | |
| 1189 | + case NO_MISMATCH: | |
| 1190 | + /* To get warnings on missing members */ | |
| 1191 | + break; | |
| 1192 | + } | |
| 1193 | + fprintf(stderr, "\n"); | |
| 1012 | 1194 | } |
| 1013 | 1195 | |
| 1014 | 1196 | static void check_section_mismatch(const char *modname, struct elf_info *elf, |
| 1015 | 1197 | Elf_Rela *r, Elf_Sym *sym, const char *fromsec) |
| 1016 | 1198 | { |
| 1017 | 1199 | const char *tosec; |
| 1200 | + enum mismatch mismatch; | |
| 1018 | 1201 | |
| 1019 | 1202 | tosec = sec_name(elf, sym->st_shndx); |
| 1020 | − if (section_mismatch(fromsec, tosec)) { | |
| 1021 | − const char *fromsym; | |
| 1203 | + mismatch = section_mismatch(fromsec, tosec); | |
| 1204 | + if (mismatch != NO_MISMATCH) { | |
| 1205 | + Elf_Sym *to; | |
| 1206 | + Elf_Sym *from; | |
| 1022 | 1207 | const char *tosym; |
| 1208 | + const char *fromsym; | |
| 1023 | 1209 | |
| 1024 | − fromsym = sym_name(elf, | |
| 1025 | − find_elf_symbol2(elf, r->r_offset, fromsec)); | |
| 1026 | − tosym = sym_name(elf, | |
| 1027 | − find_elf_symbol(elf, r->r_addend, sym)); | |
| 1210 | + from = find_elf_symbol2(elf, r->r_offset, fromsec); | |
| 1211 | + fromsym = sym_name(elf, from); | |
| 1212 | + to = find_elf_symbol(elf, r->r_addend, sym); | |
| 1213 | + tosym = sym_name(elf, to); | |
| 1028 | 1214 | |
| 1029 | 1215 | /* check whitelist - we may ignore it */ |
| 1030 | 1216 | if (secref_whitelist(fromsec, fromsym, tosec, tosym)) { |
| 1031 | − report_sec_mismatch(modname, fromsec, r->r_offset, | |
| 1032 | − fromsym, tosec, tosym); | |
| 1217 | + report_sec_mismatch(modname, mismatch, | |
| 1218 | + fromsec, r->r_offset, fromsym, | |
| 1219 | + is_function(from), tosec, tosym, | |
| 1220 | + is_function(to)); | |
| 1033 | 1221 | } |
| 1034 | 1222 | } |
| 1035 | 1223 | } |
| @@ -1643,7 +1831,7 @@ int main(int argc, char **argv) · 607 unmodified lines | ||
| 1643 | 1831 | int opt; |
| 1644 | 1832 | int err; |
| 1645 | 1833 | |
| 1646 | − while ((opt = getopt(argc, argv, "i:I:mso:aw")) != -1) { | |
| 1834 | + while ((opt = getopt(argc, argv, "i:I:msSo:aw")) != -1) { | |
| 1647 | 1835 | switch (opt) { |
| 1648 | 1836 | case 'i': |
| 1649 | 1837 | kernel_read = optarg; |
| @@ -1664,6 +1852,9 @@ int main(int argc, char **argv) · 14 unmodified lines | ||
| 1664 | 1852 | case 's': |
| 1665 | 1853 | vmlinux_section_warnings = 0; |
| 1666 | 1854 | break; |
| 1855 | + case 'S': | |
| 1856 | + sec_mismatch_verbose = 0; | |
| 1857 | + break; | |
| 1667 | 1858 | case 'w': |
| 1668 | 1859 | warn_unresolved = 1; |
| 1669 | 1860 | break; |
| @@ -1708,6 +1899,12 @@ int main(int argc, char **argv) · 38 unmodified lines | ||
| 1708 | 1899 | |
| 1709 | 1900 | if (dump_write) |
| 1710 | 1901 | write_dump(dump_write); |
| 1902 | + if (sec_mismatch_count && !sec_mismatch_verbose) | |
| 1903 | + fprintf(stderr, "modpost: Found %d section mismatch(es).\n" | |
| 1904 | + "To see additional details select \"Enable full " | |
| 1905 | + "Section mismatch analysis\"\n" | |
| 1906 | + "in the Kernel Hacking menu " | |
| 1907 | + "(CONFIG_SECTION_MISMATCH).\n", sec_mismatch_count); | |
| 1711 | 1908 | |
| 1712 | 1909 | return err; |
| 1713 | 1910 | } |