diff options
Diffstat (limited to 'kernel/module.c')
| -rw-r--r-- | kernel/module.c | 246 |
1 files changed, 125 insertions, 121 deletions
diff --git a/kernel/module.c b/kernel/module.c index 031bf26af8ea..679e4c88ed9e 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
| @@ -164,131 +164,140 @@ static const struct kernel_symbol *lookup_symbol(const char *name, | |||
| 164 | return NULL; | 164 | return NULL; |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | static void printk_unused_warning(const char *name) | 167 | static bool always_ok(bool gplok, bool warn, const char *name) |
| 168 | { | 168 | { |
| 169 | printk(KERN_WARNING "Symbol %s is marked as UNUSED, " | 169 | return true; |
| 170 | "however this module is using it.\n", name); | ||
| 171 | printk(KERN_WARNING "This symbol will go away in the future.\n"); | ||
| 172 | printk(KERN_WARNING "Please evalute if this is the right api to use, " | ||
| 173 | "and if it really is, submit a report the linux kernel " | ||
| 174 | "mailinglist together with submitting your code for " | ||
| 175 | "inclusion.\n"); | ||
| 176 | } | 170 | } |
| 177 | 171 | ||
| 178 | /* Find a symbol, return value, crc and module which owns it */ | 172 | static bool printk_unused_warning(bool gplok, bool warn, const char *name) |
| 179 | static unsigned long __find_symbol(const char *name, | ||
| 180 | struct module **owner, | ||
| 181 | const unsigned long **crc, | ||
| 182 | int gplok) | ||
| 183 | { | 173 | { |
| 184 | struct module *mod; | 174 | if (warn) { |
| 185 | const struct kernel_symbol *ks; | 175 | printk(KERN_WARNING "Symbol %s is marked as UNUSED, " |
| 186 | 176 | "however this module is using it.\n", name); | |
| 187 | /* Core kernel first. */ | 177 | printk(KERN_WARNING |
| 188 | *owner = NULL; | 178 | "This symbol will go away in the future.\n"); |
| 189 | ks = lookup_symbol(name, __start___ksymtab, __stop___ksymtab); | 179 | printk(KERN_WARNING |
| 190 | if (ks) { | 180 | "Please evalute if this is the right api to use and if " |
| 191 | *crc = symversion(__start___kcrctab, (ks - __start___ksymtab)); | 181 | "it really is, submit a report the linux kernel " |
| 192 | return ks->value; | 182 | "mailinglist together with submitting your code for " |
| 183 | "inclusion.\n"); | ||
| 193 | } | 184 | } |
| 194 | if (gplok) { | 185 | return true; |
| 195 | ks = lookup_symbol(name, __start___ksymtab_gpl, | 186 | } |
| 196 | __stop___ksymtab_gpl); | 187 | |
| 197 | if (ks) { | 188 | static bool gpl_only_unused_warning(bool gplok, bool warn, const char *name) |
| 198 | *crc = symversion(__start___kcrctab_gpl, | 189 | { |
| 199 | (ks - __start___ksymtab_gpl)); | 190 | if (!gplok) |
| 200 | return ks->value; | 191 | return false; |
| 201 | } | 192 | return printk_unused_warning(gplok, warn, name); |
| 202 | } | 193 | } |
| 203 | ks = lookup_symbol(name, __start___ksymtab_gpl_future, | 194 | |
| 204 | __stop___ksymtab_gpl_future); | 195 | static bool gpl_only(bool gplok, bool warn, const char *name) |
| 205 | if (ks) { | 196 | { |
| 206 | if (!gplok) { | 197 | return gplok; |
| 207 | printk(KERN_WARNING "Symbol %s is being used " | 198 | } |
| 208 | "by a non-GPL module, which will not " | 199 | |
| 209 | "be allowed in the future\n", name); | 200 | static bool warn_if_not_gpl(bool gplok, bool warn, const char *name) |
| 210 | printk(KERN_WARNING "Please see the file " | 201 | { |
| 211 | "Documentation/feature-removal-schedule.txt " | 202 | if (!gplok && warn) { |
| 212 | "in the kernel source tree for more " | 203 | printk(KERN_WARNING "Symbol %s is being used " |
| 213 | "details.\n"); | 204 | "by a non-GPL module, which will not " |
| 214 | } | 205 | "be allowed in the future\n", name); |
| 215 | *crc = symversion(__start___kcrctab_gpl_future, | 206 | printk(KERN_WARNING "Please see the file " |
| 216 | (ks - __start___ksymtab_gpl_future)); | 207 | "Documentation/feature-removal-schedule.txt " |
| 217 | return ks->value; | 208 | "in the kernel source tree for more details.\n"); |
| 218 | } | 209 | } |
| 210 | return true; | ||
| 211 | } | ||
| 219 | 212 | ||
| 220 | ks = lookup_symbol(name, __start___ksymtab_unused, | 213 | struct symsearch { |
| 221 | __stop___ksymtab_unused); | 214 | const struct kernel_symbol *start, *stop; |
| 222 | if (ks) { | 215 | const unsigned long *crcs; |
| 223 | printk_unused_warning(name); | 216 | bool (*check)(bool gplok, bool warn, const char *name); |
| 224 | *crc = symversion(__start___kcrctab_unused, | 217 | }; |
| 225 | (ks - __start___ksymtab_unused)); | 218 | |
| 226 | return ks->value; | 219 | /* Look through this array of symbol tables for a symbol match which |
| 220 | * passes the check function. */ | ||
| 221 | static const struct kernel_symbol *search_symarrays(const struct symsearch *arr, | ||
| 222 | unsigned int num, | ||
| 223 | const char *name, | ||
| 224 | bool gplok, | ||
| 225 | bool warn, | ||
| 226 | const unsigned long **crc) | ||
| 227 | { | ||
| 228 | unsigned int i; | ||
| 229 | const struct kernel_symbol *ks; | ||
| 230 | |||
| 231 | for (i = 0; i < num; i++) { | ||
| 232 | ks = lookup_symbol(name, arr[i].start, arr[i].stop); | ||
| 233 | if (!ks || !arr[i].check(gplok, warn, name)) | ||
| 234 | continue; | ||
| 235 | |||
| 236 | if (crc) | ||
| 237 | *crc = symversion(arr[i].crcs, ks - arr[i].start); | ||
| 238 | return ks; | ||
| 227 | } | 239 | } |
| 240 | return NULL; | ||
| 241 | } | ||
| 228 | 242 | ||
| 229 | if (gplok) | 243 | /* Find a symbol, return value, (optional) crc and (optional) module |
| 230 | ks = lookup_symbol(name, __start___ksymtab_unused_gpl, | 244 | * which owns it */ |
| 231 | __stop___ksymtab_unused_gpl); | 245 | static unsigned long find_symbol(const char *name, |
| 246 | struct module **owner, | ||
| 247 | const unsigned long **crc, | ||
| 248 | bool gplok, | ||
| 249 | bool warn) | ||
| 250 | { | ||
| 251 | struct module *mod; | ||
| 252 | const struct kernel_symbol *ks; | ||
| 253 | const struct symsearch arr[] = { | ||
| 254 | { __start___ksymtab, __stop___ksymtab, __start___kcrctab, | ||
| 255 | always_ok }, | ||
| 256 | { __start___ksymtab_gpl, __stop___ksymtab_gpl, | ||
| 257 | __start___kcrctab_gpl, gpl_only }, | ||
| 258 | { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, | ||
| 259 | __start___kcrctab_gpl_future, warn_if_not_gpl }, | ||
| 260 | { __start___ksymtab_unused, __stop___ksymtab_unused, | ||
| 261 | __start___kcrctab_unused, printk_unused_warning }, | ||
| 262 | { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, | ||
| 263 | __start___kcrctab_unused_gpl, gpl_only_unused_warning }, | ||
| 264 | }; | ||
| 265 | |||
| 266 | /* Core kernel first. */ | ||
| 267 | ks = search_symarrays(arr, ARRAY_SIZE(arr), name, gplok, warn, crc); | ||
| 232 | if (ks) { | 268 | if (ks) { |
| 233 | printk_unused_warning(name); | 269 | if (owner) |
| 234 | *crc = symversion(__start___kcrctab_unused_gpl, | 270 | *owner = NULL; |
| 235 | (ks - __start___ksymtab_unused_gpl)); | ||
| 236 | return ks->value; | 271 | return ks->value; |
| 237 | } | 272 | } |
| 238 | 273 | ||
| 239 | /* Now try modules. */ | 274 | /* Now try modules. */ |
| 240 | list_for_each_entry(mod, &modules, list) { | 275 | list_for_each_entry(mod, &modules, list) { |
| 241 | *owner = mod; | 276 | struct symsearch arr[] = { |
| 242 | ks = lookup_symbol(name, mod->syms, mod->syms + mod->num_syms); | 277 | { mod->syms, mod->syms + mod->num_syms, mod->crcs, |
| 278 | always_ok }, | ||
| 279 | { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, | ||
| 280 | mod->gpl_crcs, gpl_only }, | ||
| 281 | { mod->gpl_future_syms, | ||
| 282 | mod->gpl_future_syms + mod->num_gpl_future_syms, | ||
| 283 | mod->gpl_future_crcs, warn_if_not_gpl }, | ||
| 284 | { mod->unused_syms, | ||
| 285 | mod->unused_syms + mod->num_unused_syms, | ||
| 286 | mod->unused_crcs, printk_unused_warning }, | ||
| 287 | { mod->unused_gpl_syms, | ||
| 288 | mod->unused_gpl_syms + mod->num_unused_gpl_syms, | ||
| 289 | mod->unused_gpl_crcs, gpl_only_unused_warning }, | ||
| 290 | }; | ||
| 291 | |||
| 292 | ks = search_symarrays(arr, ARRAY_SIZE(arr), | ||
| 293 | name, gplok, warn, crc); | ||
| 243 | if (ks) { | 294 | if (ks) { |
| 244 | *crc = symversion(mod->crcs, (ks - mod->syms)); | 295 | if (owner) |
| 245 | return ks->value; | 296 | *owner = mod; |
| 246 | } | ||
| 247 | |||
| 248 | if (gplok) { | ||
| 249 | ks = lookup_symbol(name, mod->gpl_syms, | ||
| 250 | mod->gpl_syms + mod->num_gpl_syms); | ||
| 251 | if (ks) { | ||
| 252 | *crc = symversion(mod->gpl_crcs, | ||
| 253 | (ks - mod->gpl_syms)); | ||
| 254 | return ks->value; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | ks = lookup_symbol(name, mod->unused_syms, mod->unused_syms + mod->num_unused_syms); | ||
| 258 | if (ks) { | ||
| 259 | printk_unused_warning(name); | ||
| 260 | *crc = symversion(mod->unused_crcs, (ks - mod->unused_syms)); | ||
| 261 | return ks->value; | ||
| 262 | } | ||
| 263 | |||
| 264 | if (gplok) { | ||
| 265 | ks = lookup_symbol(name, mod->unused_gpl_syms, | ||
| 266 | mod->unused_gpl_syms + mod->num_unused_gpl_syms); | ||
| 267 | if (ks) { | ||
| 268 | printk_unused_warning(name); | ||
| 269 | *crc = symversion(mod->unused_gpl_crcs, | ||
| 270 | (ks - mod->unused_gpl_syms)); | ||
| 271 | return ks->value; | ||
| 272 | } | ||
| 273 | } | ||
| 274 | ks = lookup_symbol(name, mod->gpl_future_syms, | ||
| 275 | (mod->gpl_future_syms + | ||
| 276 | mod->num_gpl_future_syms)); | ||
| 277 | if (ks) { | ||
| 278 | if (!gplok) { | ||
| 279 | printk(KERN_WARNING "Symbol %s is being used " | ||
| 280 | "by a non-GPL module, which will not " | ||
| 281 | "be allowed in the future\n", name); | ||
| 282 | printk(KERN_WARNING "Please see the file " | ||
| 283 | "Documentation/feature-removal-schedule.txt " | ||
| 284 | "in the kernel source tree for more " | ||
| 285 | "details.\n"); | ||
| 286 | } | ||
| 287 | *crc = symversion(mod->gpl_future_crcs, | ||
| 288 | (ks - mod->gpl_future_syms)); | ||
| 289 | return ks->value; | 297 | return ks->value; |
| 290 | } | 298 | } |
| 291 | } | 299 | } |
| 300 | |||
| 292 | DEBUGP("Failed to find symbol %s\n", name); | 301 | DEBUGP("Failed to find symbol %s\n", name); |
| 293 | return -ENOENT; | 302 | return -ENOENT; |
| 294 | } | 303 | } |
| @@ -777,10 +786,9 @@ static void print_unload_info(struct seq_file *m, struct module *mod) | |||
| 777 | void __symbol_put(const char *symbol) | 786 | void __symbol_put(const char *symbol) |
| 778 | { | 787 | { |
| 779 | struct module *owner; | 788 | struct module *owner; |
| 780 | const unsigned long *crc; | ||
| 781 | 789 | ||
| 782 | preempt_disable(); | 790 | preempt_disable(); |
| 783 | if (IS_ERR_VALUE(__find_symbol(symbol, &owner, &crc, 1))) | 791 | if (IS_ERR_VALUE(find_symbol(symbol, &owner, NULL, true, false))) |
| 784 | BUG(); | 792 | BUG(); |
| 785 | module_put(owner); | 793 | module_put(owner); |
| 786 | preempt_enable(); | 794 | preempt_enable(); |
| @@ -924,13 +932,10 @@ static inline int check_modstruct_version(Elf_Shdr *sechdrs, | |||
| 924 | struct module *mod) | 932 | struct module *mod) |
| 925 | { | 933 | { |
| 926 | const unsigned long *crc; | 934 | const unsigned long *crc; |
| 927 | struct module *owner; | ||
| 928 | 935 | ||
| 929 | if (IS_ERR_VALUE(__find_symbol("struct_module", | 936 | if (IS_ERR_VALUE(find_symbol("struct_module", NULL, &crc, true, false))) |
| 930 | &owner, &crc, 1))) | ||
| 931 | BUG(); | 937 | BUG(); |
| 932 | return check_version(sechdrs, versindex, "struct_module", mod, | 938 | return check_version(sechdrs, versindex, "struct_module", mod, crc); |
| 933 | crc); | ||
| 934 | } | 939 | } |
| 935 | 940 | ||
| 936 | /* First part is kernel version, which we ignore. */ | 941 | /* First part is kernel version, which we ignore. */ |
| @@ -974,8 +979,8 @@ static unsigned long resolve_symbol(Elf_Shdr *sechdrs, | |||
| 974 | unsigned long ret; | 979 | unsigned long ret; |
| 975 | const unsigned long *crc; | 980 | const unsigned long *crc; |
| 976 | 981 | ||
| 977 | ret = __find_symbol(name, &owner, &crc, | 982 | ret = find_symbol(name, &owner, &crc, |
| 978 | !(mod->taints & TAINT_PROPRIETARY_MODULE)); | 983 | !(mod->taints & TAINT_PROPRIETARY_MODULE), true); |
| 979 | if (!IS_ERR_VALUE(ret)) { | 984 | if (!IS_ERR_VALUE(ret)) { |
| 980 | /* use_module can fail due to OOM, | 985 | /* use_module can fail due to OOM, |
| 981 | or module initialization or unloading */ | 986 | or module initialization or unloading */ |
| @@ -1376,10 +1381,9 @@ void *__symbol_get(const char *symbol) | |||
| 1376 | { | 1381 | { |
| 1377 | struct module *owner; | 1382 | struct module *owner; |
| 1378 | unsigned long value; | 1383 | unsigned long value; |
| 1379 | const unsigned long *crc; | ||
| 1380 | 1384 | ||
| 1381 | preempt_disable(); | 1385 | preempt_disable(); |
| 1382 | value = __find_symbol(symbol, &owner, &crc, 1); | 1386 | value = find_symbol(symbol, &owner, NULL, true, true); |
| 1383 | if (IS_ERR_VALUE(value)) | 1387 | if (IS_ERR_VALUE(value)) |
| 1384 | value = 0; | 1388 | value = 0; |
| 1385 | else if (strong_try_module_get(owner)) | 1389 | else if (strong_try_module_get(owner)) |
| @@ -1402,16 +1406,16 @@ static int verify_export_symbols(struct module *mod) | |||
| 1402 | const unsigned long *crc; | 1406 | const unsigned long *crc; |
| 1403 | 1407 | ||
| 1404 | for (i = 0; i < mod->num_syms; i++) | 1408 | for (i = 0; i < mod->num_syms; i++) |
| 1405 | if (!IS_ERR_VALUE(__find_symbol(mod->syms[i].name, | 1409 | if (!IS_ERR_VALUE(find_symbol(mod->syms[i].name, |
| 1406 | &owner, &crc, 1))) { | 1410 | &owner, &crc, true, false))) { |
| 1407 | name = mod->syms[i].name; | 1411 | name = mod->syms[i].name; |
| 1408 | ret = -ENOEXEC; | 1412 | ret = -ENOEXEC; |
| 1409 | goto dup; | 1413 | goto dup; |
| 1410 | } | 1414 | } |
| 1411 | 1415 | ||
| 1412 | for (i = 0; i < mod->num_gpl_syms; i++) | 1416 | for (i = 0; i < mod->num_gpl_syms; i++) |
| 1413 | if (!IS_ERR_VALUE(__find_symbol(mod->gpl_syms[i].name, | 1417 | if (!IS_ERR_VALUE(find_symbol(mod->gpl_syms[i].name, |
| 1414 | &owner, &crc, 1))) { | 1418 | &owner, &crc, true, false))) { |
| 1415 | name = mod->gpl_syms[i].name; | 1419 | name = mod->gpl_syms[i].name; |
| 1416 | ret = -ENOEXEC; | 1420 | ret = -ENOEXEC; |
| 1417 | goto dup; | 1421 | goto dup; |
