diff options
-rw-r--r-- | lib/Kconfig.debug | 2 | ||||
-rw-r--r-- | scripts/Makefile.modpost | 1 | ||||
-rw-r--r-- | scripts/mod/modpost.c | 255 |
3 files changed, 229 insertions, 29 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 748e72be6e68..c4ecb2994ba3 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -108,6 +108,8 @@ config DEBUG_SECTION_MISMATCH | |||
108 | will tell where the mismatch happens much closer to the | 108 | will tell where the mismatch happens much closer to the |
109 | source. The drawback is that we will report the same | 109 | source. The drawback is that we will report the same |
110 | mismatch at least twice. | 110 | mismatch at least twice. |
111 | - Enable verbose reporting from modpost to help solving | ||
112 | the section mismatches reported. | ||
111 | 113 | ||
112 | config DEBUG_KERNEL | 114 | config DEBUG_KERNEL |
113 | bool "Kernel debugging" | 115 | bool "Kernel debugging" |
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index d988f5d21e3d..65e707e1ffc3 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost | |||
@@ -62,6 +62,7 @@ modpost = scripts/mod/modpost \ | |||
62 | $(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile) \ | 62 | $(if $(KBUILD_EXTMOD),-i,-o) $(kernelsymfile) \ |
63 | $(if $(KBUILD_EXTMOD),-I $(modulesymfile)) \ | 63 | $(if $(KBUILD_EXTMOD),-I $(modulesymfile)) \ |
64 | $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ | 64 | $(if $(KBUILD_EXTMOD),-o $(modulesymfile)) \ |
65 | $(if $(CONFIG_DEBUG_SECTION_MISMATCH),,-S) \ | ||
65 | $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) | 66 | $(if $(KBUILD_EXTMOD)$(KBUILD_MODPOST_WARN),-w) |
66 | 67 | ||
67 | quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules | 68 | quiet_cmd_modpost = MODPOST $(words $(filter-out vmlinux FORCE, $^)) modules |
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index e75739ec9c03..3cf1ba8220d2 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c | |||
@@ -28,6 +28,9 @@ static int vmlinux_section_warnings = 1; | |||
28 | /* Only warn about unresolved symbols */ | 28 | /* Only warn about unresolved symbols */ |
29 | static int warn_unresolved = 0; | 29 | static int warn_unresolved = 0; |
30 | /* How a symbol is exported */ | 30 | /* How a symbol is exported */ |
31 | static int sec_mismatch_count = 0; | ||
32 | static int sec_mismatch_verbose = 1; | ||
33 | |||
31 | enum export { | 34 | enum export { |
32 | export_plain, export_unused, export_gpl, | 35 | export_plain, export_unused, export_gpl, |
33 | export_unused_gpl, export_gpl_future, export_unknown | 36 | export_unused_gpl, export_gpl_future, export_unknown |
@@ -760,9 +763,23 @@ static const char *head_sections[] = { ".head.text*", NULL }; | |||
760 | static const char *linker_symbols[] = | 763 | static const char *linker_symbols[] = |
761 | { "__init_begin", "_sinittext", "_einittext", NULL }; | 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 | struct sectioncheck { | 779 | struct sectioncheck { |
764 | const char *fromsec[20]; | 780 | const char *fromsec[20]; |
765 | const char *tosec[20]; | 781 | const char *tosec[20]; |
782 | enum mismatch mismatch; | ||
766 | }; | 783 | }; |
767 | 784 | ||
768 | const struct sectioncheck sectioncheck[] = { | 785 | const struct sectioncheck sectioncheck[] = { |
@@ -770,33 +787,54 @@ const struct sectioncheck sectioncheck[] = { | |||
770 | * normal code and data | 787 | * normal code and data |
771 | */ | 788 | */ |
772 | { | 789 | { |
773 | .fromsec = { TEXT_SECTIONS, DATA_SECTIONS, NULL }, | 790 | .fromsec = { TEXT_SECTIONS, NULL }, |
774 | .tosec = { ALL_INIT_SECTIONS, ALL_EXIT_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 | /* Do not reference init code/data from devinit/cpuinit/meminit code/data */ | 809 | /* Do not reference init code/data from devinit/cpuinit/meminit code/data */ |
777 | { | 810 | { |
778 | .fromsec = { DEV_INIT_SECTIONS, CPU_INIT_SECTIONS, MEM_INIT_SECTIONS, NULL }, | 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 | /* Do not reference exit code/data from devexit/cpuexit/memexit code/data */ | 815 | /* Do not reference exit code/data from devexit/cpuexit/memexit code/data */ |
782 | { | 816 | { |
783 | .fromsec = { DEV_EXIT_SECTIONS, CPU_EXIT_SECTIONS, MEM_EXIT_SECTIONS, NULL }, | 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 | /* Do not use exit code/data from init code */ | 821 | /* Do not use exit code/data from init code */ |
787 | { | 822 | { |
788 | .fromsec = { ALL_INIT_SECTIONS, NULL }, | 823 | .fromsec = { ALL_INIT_SECTIONS, NULL }, |
789 | .tosec = { ALL_EXIT_SECTIONS, NULL }, | 824 | .tosec = { ALL_EXIT_SECTIONS, NULL }, |
825 | .mismatch = INIT_TO_EXIT, | ||
790 | }, | 826 | }, |
791 | /* Do not use init code/data from exit code */ | 827 | /* Do not use init code/data from exit code */ |
792 | { | 828 | { |
793 | .fromsec = { ALL_EXIT_SECTIONS, NULL }, | 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 | /* Do not export init/exit functions or data */ | 833 | /* Do not export init/exit functions or data */ |
797 | { | 834 | { |
798 | .fromsec = { "__ksymtab*", NULL }, | 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) | |||
809 | for (i = 0; i < elems; i++) { | 847 | for (i = 0; i < elems; i++) { |
810 | if (match(fromsec, check->fromsec) && | 848 | if (match(fromsec, check->fromsec) && |
811 | match(tosec, check->tosec)) | 849 | match(tosec, check->tosec)) |
812 | return 1; | 850 | return check->mismatch; |
813 | check++; | 851 | check++; |
814 | } | 852 | } |
815 | return 0; | 853 | return NO_MISMATCH; |
816 | } | 854 | } |
817 | 855 | ||
818 | /** | 856 | /** |
@@ -989,47 +1027,197 @@ static Elf_Sym *find_elf_symbol2(struct elf_info *elf, Elf_Addr addr, | |||
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 | * Print a warning about a section mismatch. | 1072 | * Print a warning about a section mismatch. |
993 | * Try to find symbols near it so user can find it. | 1073 | * Try to find symbols near it so user can find it. |
994 | * Check whitelist before warning - it may be a false positive. | 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 | const char *fromsec, | 1077 | const char *fromsec, |
998 | unsigned long long fromaddr, | 1078 | unsigned long long fromaddr, |
999 | const char *fromsym, | 1079 | const char *fromsym, |
1000 | const char *tosec, const char *tosym) | 1080 | int from_is_func, |
1001 | { | 1081 | const char *tosec, const char *tosym, |
1002 | if (strlen(tosym)) { | 1082 | int to_is_func) |
1003 | warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s " | 1083 | { |
1004 | "in '%s'\n", | 1084 | const char *from, *from_p; |
1005 | modname, fromsec, fromaddr, | 1085 | const char *to, *to_p; |
1006 | tosec, tosym, fromsym); | 1086 | from = from_is_func ? "function" : "variable"; |
1007 | } else { | 1087 | from_p = from_is_func ? "()" : ""; |
1008 | warn("%s(%s+0x%llx): Section mismatch: reference to %s:%s\n", | 1088 | to = to_is_func ? "function" : "variable"; |
1009 | modname, fromsec, fromaddr, | 1089 | to_p = to_is_func ? "()" : ""; |
1010 | tosec, tosym); | 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 | static void check_section_mismatch(const char *modname, struct elf_info *elf, | 1196 | static void check_section_mismatch(const char *modname, struct elf_info *elf, |
1015 | Elf_Rela *r, Elf_Sym *sym, const char *fromsec) | 1197 | Elf_Rela *r, Elf_Sym *sym, const char *fromsec) |
1016 | { | 1198 | { |
1017 | const char *tosec; | 1199 | const char *tosec; |
1200 | enum mismatch mismatch; | ||
1018 | 1201 | ||
1019 | tosec = sec_name(elf, sym->st_shndx); | 1202 | tosec = sec_name(elf, sym->st_shndx); |
1020 | if (section_mismatch(fromsec, tosec)) { | 1203 | mismatch = section_mismatch(fromsec, tosec); |
1021 | const char *fromsym; | 1204 | if (mismatch != NO_MISMATCH) { |
1205 | Elf_Sym *to; | ||
1206 | Elf_Sym *from; | ||
1022 | const char *tosym; | 1207 | const char *tosym; |
1208 | const char *fromsym; | ||
1023 | 1209 | ||
1024 | fromsym = sym_name(elf, | 1210 | from = find_elf_symbol2(elf, r->r_offset, fromsec); |
1025 | find_elf_symbol2(elf, r->r_offset, fromsec)); | 1211 | fromsym = sym_name(elf, from); |
1026 | tosym = sym_name(elf, | 1212 | to = find_elf_symbol(elf, r->r_addend, sym); |
1027 | find_elf_symbol(elf, r->r_addend, sym)); | 1213 | tosym = sym_name(elf, to); |
1028 | 1214 | ||
1029 | /* check whitelist - we may ignore it */ | 1215 | /* check whitelist - we may ignore it */ |
1030 | if (secref_whitelist(fromsec, fromsym, tosec, tosym)) { | 1216 | if (secref_whitelist(fromsec, fromsym, tosec, tosym)) { |
1031 | report_sec_mismatch(modname, fromsec, r->r_offset, | 1217 | report_sec_mismatch(modname, mismatch, |
1032 | fromsym, tosec, tosym); | 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) | |||
1643 | int opt; | 1831 | int opt; |
1644 | int err; | 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 | switch (opt) { | 1835 | switch (opt) { |
1648 | case 'i': | 1836 | case 'i': |
1649 | kernel_read = optarg; | 1837 | kernel_read = optarg; |
@@ -1664,6 +1852,9 @@ int main(int argc, char **argv) | |||
1664 | case 's': | 1852 | case 's': |
1665 | vmlinux_section_warnings = 0; | 1853 | vmlinux_section_warnings = 0; |
1666 | break; | 1854 | break; |
1855 | case 'S': | ||
1856 | sec_mismatch_verbose = 0; | ||
1857 | break; | ||
1667 | case 'w': | 1858 | case 'w': |
1668 | warn_unresolved = 1; | 1859 | warn_unresolved = 1; |
1669 | break; | 1860 | break; |
@@ -1708,6 +1899,12 @@ int main(int argc, char **argv) | |||
1708 | 1899 | ||
1709 | if (dump_write) | 1900 | if (dump_write) |
1710 | write_dump(dump_write); | 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 | return err; | 1909 | return err; |
1713 | } | 1910 | } |