diff options
Diffstat (limited to 'kernel')
-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 | { |