diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2008-07-22 20:24:25 -0400 |
|---|---|---|
| committer | Rusty Russell <rusty@rustcorp.com.au> | 2008-07-22 05:24:26 -0400 |
| commit | dafd0940c96fec67974a88ed8e6b8ba3160394cd (patch) | |
| tree | 616da8f0f283509ec71ff2d6e467fa4c6877edc6 | |
| parent | da39ba5e1d65e997a98f6eb93ba6e6eb505f6e3c (diff) | |
module: generic each_symbol iterator function
Introduce an each_symbol() iterator to avoid duplicating the knowledge
about the 5 different sections containing symbols. Currently only
used by find_symbol(), but will be used by symbol_put_addr() too.
(Includes NULL ptr deref fix by Jiri Kosina <jkosina@suse.cz>)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Cc: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | kernel/module.c | 244 |
1 files changed, 134 insertions, 110 deletions
diff --git a/kernel/module.c b/kernel/module.c index 705e1d5d516c..c51c089c666e 100644 --- a/kernel/module.c +++ b/kernel/module.c | |||
| @@ -152,156 +152,180 @@ extern const unsigned long __start___kcrctab_unused_gpl[]; | |||
| 152 | #define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL) | 152 | #define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL) |
| 153 | #endif | 153 | #endif |
| 154 | 154 | ||
| 155 | /* lookup symbol in given range of kernel_symbols */ | ||
| 156 | static const struct kernel_symbol *lookup_symbol(const char *name, | ||
| 157 | const struct kernel_symbol *start, | ||
| 158 | const struct kernel_symbol *stop) | ||
| 159 | { | ||
| 160 | const struct kernel_symbol *ks = start; | ||
| 161 | for (; ks < stop; ks++) | ||
| 162 | if (strcmp(ks->name, name) == 0) | ||
| 163 | return ks; | ||
| 164 | return NULL; | ||
| 165 | } | ||
| 166 | |||
| 167 | static bool always_ok(bool gplok, bool warn, const char *name) | ||
| 168 | { | ||
| 169 | return true; | ||
| 170 | } | ||
| 171 | |||
| 172 | static bool printk_unused_warning(bool gplok, bool warn, const char *name) | ||
| 173 | { | ||
| 174 | if (warn) { | ||
| 175 | printk(KERN_WARNING "Symbol %s is marked as UNUSED, " | ||
| 176 | "however this module is using it.\n", name); | ||
| 177 | printk(KERN_WARNING | ||
| 178 | "This symbol will go away in the future.\n"); | ||
| 179 | printk(KERN_WARNING | ||
| 180 | "Please evalute if this is the right api to use and if " | ||
| 181 | "it really is, submit a report the linux kernel " | ||
| 182 | "mailinglist together with submitting your code for " | ||
| 183 | "inclusion.\n"); | ||
| 184 | } | ||
| 185 | return true; | ||
| 186 | } | ||
| 187 | |||
| 188 | static bool gpl_only_unused_warning(bool gplok, bool warn, const char *name) | ||
| 189 | { | ||
| 190 | if (!gplok) | ||
| 191 | return false; | ||
| 192 | return printk_unused_warning(gplok, warn, name); | ||
| 193 | } | ||
| 194 | |||
| 195 | static bool gpl_only(bool gplok, bool warn, const char *name) | ||
| 196 | { | ||
| 197 | return gplok; | ||
| 198 | } | ||
| 199 | |||
| 200 | static bool warn_if_not_gpl(bool gplok, bool warn, const char *name) | ||
| 201 | { | ||
| 202 | if (!gplok && warn) { | ||
| 203 | printk(KERN_WARNING "Symbol %s is being used " | ||
| 204 | "by a non-GPL module, which will not " | ||
| 205 | "be allowed in the future\n", name); | ||
| 206 | printk(KERN_WARNING "Please see the file " | ||
| 207 | "Documentation/feature-removal-schedule.txt " | ||
| 208 | "in the kernel source tree for more details.\n"); | ||
| 209 | } | ||
| 210 | return true; | ||
| 211 | } | ||
| 212 | |||
| 213 | struct symsearch { | 155 | struct symsearch { |
| 214 | const struct kernel_symbol *start, *stop; | 156 | const struct kernel_symbol *start, *stop; |
| 215 | const unsigned long *crcs; | 157 | const unsigned long *crcs; |
| 216 | bool (*check)(bool gplok, bool warn, const char *name); | 158 | enum { |
| 159 | NOT_GPL_ONLY, | ||
| 160 | GPL_ONLY, | ||
| 161 | WILL_BE_GPL_ONLY, | ||
| 162 | } licence; | ||
| 163 | bool unused; | ||
| 217 | }; | 164 | }; |
| 218 | 165 | ||
| 219 | /* Look through this array of symbol tables for a symbol match which | 166 | static bool each_symbol_in_section(const struct symsearch *arr, |
| 220 | * passes the check function. */ | 167 | unsigned int arrsize, |
| 221 | static const struct kernel_symbol *search_symarrays(const struct symsearch *arr, | 168 | struct module *owner, |
| 222 | unsigned int num, | 169 | bool (*fn)(const struct symsearch *syms, |
| 223 | const char *name, | 170 | struct module *owner, |
| 224 | bool gplok, | 171 | unsigned int symnum, void *data), |
| 225 | bool warn, | 172 | void *data) |
| 226 | const unsigned long **crc) | ||
| 227 | { | 173 | { |
| 228 | unsigned int i; | 174 | unsigned int i, j; |
| 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 | 175 | ||
| 236 | if (crc) | 176 | for (j = 0; j < arrsize; j++) { |
| 237 | *crc = symversion(arr[i].crcs, ks - arr[i].start); | 177 | for (i = 0; i < arr[j].stop - arr[j].start; i++) |
| 238 | return ks; | 178 | if (fn(&arr[j], owner, i, data)) |
| 179 | return true; | ||
| 239 | } | 180 | } |
| 240 | return NULL; | 181 | |
| 182 | return false; | ||
| 241 | } | 183 | } |
| 242 | 184 | ||
| 243 | /* Find a symbol, return value, (optional) crc and (optional) module | 185 | /* Returns true as soon as fn returns true, otherwise false. */ |
| 244 | * which owns it */ | 186 | static bool each_symbol(bool (*fn)(const struct symsearch *arr, |
| 245 | static unsigned long find_symbol(const char *name, | 187 | struct module *owner, |
| 246 | struct module **owner, | 188 | unsigned int symnum, void *data), |
| 247 | const unsigned long **crc, | 189 | void *data) |
| 248 | bool gplok, | ||
| 249 | bool warn) | ||
| 250 | { | 190 | { |
| 251 | struct module *mod; | 191 | struct module *mod; |
| 252 | const struct kernel_symbol *ks; | ||
| 253 | const struct symsearch arr[] = { | 192 | const struct symsearch arr[] = { |
| 254 | { __start___ksymtab, __stop___ksymtab, __start___kcrctab, | 193 | { __start___ksymtab, __stop___ksymtab, __start___kcrctab, |
| 255 | always_ok }, | 194 | NOT_GPL_ONLY, false }, |
| 256 | { __start___ksymtab_gpl, __stop___ksymtab_gpl, | 195 | { __start___ksymtab_gpl, __stop___ksymtab_gpl, |
| 257 | __start___kcrctab_gpl, gpl_only }, | 196 | __start___kcrctab_gpl, |
| 197 | GPL_ONLY, false }, | ||
| 258 | { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, | 198 | { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, |
| 259 | __start___kcrctab_gpl_future, warn_if_not_gpl }, | 199 | __start___kcrctab_gpl_future, |
| 200 | WILL_BE_GPL_ONLY, false }, | ||
| 260 | { __start___ksymtab_unused, __stop___ksymtab_unused, | 201 | { __start___ksymtab_unused, __stop___ksymtab_unused, |
| 261 | __start___kcrctab_unused, printk_unused_warning }, | 202 | __start___kcrctab_unused, |
| 203 | NOT_GPL_ONLY, true }, | ||
| 262 | { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, | 204 | { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, |
| 263 | __start___kcrctab_unused_gpl, gpl_only_unused_warning }, | 205 | __start___kcrctab_unused_gpl, |
| 206 | GPL_ONLY, true }, | ||
| 264 | }; | 207 | }; |
| 265 | 208 | ||
| 266 | /* Core kernel first. */ | 209 | if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data)) |
| 267 | ks = search_symarrays(arr, ARRAY_SIZE(arr), name, gplok, warn, crc); | 210 | return true; |
| 268 | if (ks) { | ||
| 269 | if (owner) | ||
| 270 | *owner = NULL; | ||
| 271 | return ks->value; | ||
| 272 | } | ||
| 273 | 211 | ||
| 274 | /* Now try modules. */ | ||
| 275 | list_for_each_entry(mod, &modules, list) { | 212 | list_for_each_entry(mod, &modules, list) { |
| 276 | struct symsearch arr[] = { | 213 | struct symsearch arr[] = { |
| 277 | { mod->syms, mod->syms + mod->num_syms, mod->crcs, | 214 | { mod->syms, mod->syms + mod->num_syms, mod->crcs, |
| 278 | always_ok }, | 215 | NOT_GPL_ONLY, false }, |
| 279 | { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, | 216 | { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, |
| 280 | mod->gpl_crcs, gpl_only }, | 217 | mod->gpl_crcs, |
| 218 | GPL_ONLY, false }, | ||
| 281 | { mod->gpl_future_syms, | 219 | { mod->gpl_future_syms, |
| 282 | mod->gpl_future_syms + mod->num_gpl_future_syms, | 220 | mod->gpl_future_syms + mod->num_gpl_future_syms, |
| 283 | mod->gpl_future_crcs, warn_if_not_gpl }, | 221 | mod->gpl_future_crcs, |
| 222 | WILL_BE_GPL_ONLY, false }, | ||
| 284 | { mod->unused_syms, | 223 | { mod->unused_syms, |
| 285 | mod->unused_syms + mod->num_unused_syms, | 224 | mod->unused_syms + mod->num_unused_syms, |
| 286 | mod->unused_crcs, printk_unused_warning }, | 225 | mod->unused_crcs, |
| 226 | NOT_GPL_ONLY, true }, | ||
| 287 | { mod->unused_gpl_syms, | 227 | { mod->unused_gpl_syms, |
| 288 | mod->unused_gpl_syms + mod->num_unused_gpl_syms, | 228 | mod->unused_gpl_syms + mod->num_unused_gpl_syms, |
| 289 | mod->unused_gpl_crcs, gpl_only_unused_warning }, | 229 | mod->unused_gpl_crcs, |
| 230 | GPL_ONLY, true }, | ||
| 290 | }; | 231 | }; |
| 291 | 232 | ||
| 292 | ks = search_symarrays(arr, ARRAY_SIZE(arr), | 233 | if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data)) |
| 293 | name, gplok, warn, crc); | 234 | return true; |
| 294 | if (ks) { | 235 | } |
| 295 | if (owner) | 236 | return false; |
| 296 | *owner = mod; | 237 | } |
| 297 | return ks->value; | 238 | |
| 239 | struct find_symbol_arg { | ||
| 240 | /* Input */ | ||
| 241 | const char *name; | ||
| 242 | bool gplok; | ||
| 243 | bool warn; | ||
| 244 | |||
| 245 | /* Output */ | ||
| 246 | struct module *owner; | ||
| 247 | const unsigned long *crc; | ||
| 248 | unsigned long value; | ||
| 249 | }; | ||
| 250 | |||
| 251 | static bool find_symbol_in_section(const struct symsearch *syms, | ||
| 252 | struct module *owner, | ||
| 253 | unsigned int symnum, void *data) | ||
| 254 | { | ||
| 255 | struct find_symbol_arg *fsa = data; | ||
| 256 | |||
| 257 | if (strcmp(syms->start[symnum].name, fsa->name) != 0) | ||
| 258 | return false; | ||
| 259 | |||
| 260 | if (!fsa->gplok) { | ||
| 261 | if (syms->licence == GPL_ONLY) | ||
| 262 | return false; | ||
| 263 | if (syms->licence == WILL_BE_GPL_ONLY && fsa->warn) { | ||
| 264 | printk(KERN_WARNING "Symbol %s is being used " | ||
| 265 | "by a non-GPL module, which will not " | ||
| 266 | "be allowed in the future\n", fsa->name); | ||
| 267 | printk(KERN_WARNING "Please see the file " | ||
| 268 | "Documentation/feature-removal-schedule.txt " | ||
| 269 | "in the kernel source tree for more details.\n"); | ||
| 298 | } | 270 | } |
| 299 | } | 271 | } |
| 300 | 272 | ||
| 273 | if (syms->unused && fsa->warn) { | ||
| 274 | printk(KERN_WARNING "Symbol %s is marked as UNUSED, " | ||
| 275 | "however this module is using it.\n", fsa->name); | ||
| 276 | printk(KERN_WARNING | ||
| 277 | "This symbol will go away in the future.\n"); | ||
| 278 | printk(KERN_WARNING | ||
| 279 | "Please evalute if this is the right api to use and if " | ||
| 280 | "it really is, submit a report the linux kernel " | ||
| 281 | "mailinglist together with submitting your code for " | ||
| 282 | "inclusion.\n"); | ||
| 283 | } | ||
| 284 | |||
| 285 | fsa->owner = owner; | ||
| 286 | fsa->crc = symversion(syms->crcs, symnum); | ||
| 287 | fsa->value = syms->start[symnum].value; | ||
| 288 | return true; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* Find a symbol, return value, (optional) crc and (optional) module | ||
| 292 | * which owns it */ | ||
| 293 | static unsigned long find_symbol(const char *name, | ||
| 294 | struct module **owner, | ||
| 295 | const unsigned long **crc, | ||
| 296 | bool gplok, | ||
| 297 | bool warn) | ||
| 298 | { | ||
| 299 | struct find_symbol_arg fsa; | ||
| 300 | |||
| 301 | fsa.name = name; | ||
| 302 | fsa.gplok = gplok; | ||
| 303 | fsa.warn = warn; | ||
| 304 | |||
| 305 | if (each_symbol(find_symbol_in_section, &fsa)) { | ||
| 306 | if (owner) | ||
| 307 | *owner = fsa.owner; | ||
| 308 | if (crc) | ||
| 309 | *crc = fsa.crc; | ||
| 310 | return fsa.value; | ||
| 311 | } | ||
| 312 | |||
| 301 | DEBUGP("Failed to find symbol %s\n", name); | 313 | DEBUGP("Failed to find symbol %s\n", name); |
| 302 | return -ENOENT; | 314 | return -ENOENT; |
| 303 | } | 315 | } |
| 304 | 316 | ||
| 317 | /* lookup symbol in given range of kernel_symbols */ | ||
| 318 | static const struct kernel_symbol *lookup_symbol(const char *name, | ||
| 319 | const struct kernel_symbol *start, | ||
| 320 | const struct kernel_symbol *stop) | ||
| 321 | { | ||
| 322 | const struct kernel_symbol *ks = start; | ||
| 323 | for (; ks < stop; ks++) | ||
| 324 | if (strcmp(ks->name, name) == 0) | ||
| 325 | return ks; | ||
| 326 | return NULL; | ||
| 327 | } | ||
| 328 | |||
| 305 | /* Search for module by name: must hold module_mutex. */ | 329 | /* Search for module by name: must hold module_mutex. */ |
| 306 | static struct module *find_module(const char *name) | 330 | static struct module *find_module(const char *name) |
| 307 | { | 331 | { |
