Commit 588ccd732ba2d32db8228802ef9283b583d3395f
1 parent
f5eaa323eb
Exists in
master
and in
20 other branches
kbuild: add verbose option to Section mismatch reporting in modpost
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. Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Showing 3 changed files with 229 additions and 29 deletions Side-by-side Diff
lib/Kconfig.debug
... | ... | @@ -108,6 +108,8 @@ |
108 | 108 | will tell where the mismatch happens much closer to the |
109 | 109 | source. The drawback is that we will report the same |
110 | 110 | mismatch at least twice. |
111 | + - Enable verbose reporting from modpost to help solving | |
112 | + the section mismatches reported. | |
111 | 113 | |
112 | 114 | config DEBUG_KERNEL |
113 | 115 | bool "Kernel debugging" |
scripts/Makefile.modpost
... | ... | @@ -62,6 +62,7 @@ |
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 |
scripts/mod/modpost.c
... | ... | @@ -28,6 +28,9 @@ |
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 |
34 | 37 | |
... | ... | @@ -760,9 +763,23 @@ |
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[] = { |
769 | 786 | |
770 | 787 | |
771 | 788 | |
772 | 789 | |
773 | 790 | |
774 | 791 | |
... | ... | @@ -770,33 +787,54 @@ |
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, | |
775 | 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, | |
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 | |
803 | 841 | |
... | ... | @@ -809,10 +847,10 @@ |
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 | /** |
819 | 857 | |
820 | 858 | |
821 | 859 | |
822 | 860 | |
823 | 861 | |
824 | 862 | |
825 | 863 | |
826 | 864 | |
827 | 865 | |
... | ... | @@ -989,47 +1027,197 @@ |
989 | 1027 | } |
990 | 1028 | |
991 | 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 | + | |
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) | |
1080 | + int from_is_func, | |
1081 | + const char *tosec, const char *tosym, | |
1082 | + int to_is_func) | |
1001 | 1083 | { |
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); | |
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 @@ |
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 @@ |
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 @@ |
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 | } |