// SPDX-License-Identifier: GPL-2.0-or-later/* Copyright (C) 2002 Richard Henderson Copyright (C) 2001 Rusty Russell, 2002, 2010 Rusty Russell IBM. */ #define INCLUDE_VERMAGIC #include <linux/export.h>#include <linux/extable.h>#include <linux/moduleloader.h>#include <linux/module_signature.h>#include <linux/trace_events.h>#include <linux/init.h>#include <linux/kallsyms.h>#include <linux/file.h>#include <linux/fs.h>#include <linux/sysfs.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/elf.h>#include <linux/proc_fs.h>#include <linux/security.h>#include <linux/seq_file.h>#include <linux/syscalls.h>#include <linux/fcntl.h>#include <linux/rcupdate.h>#include <linux/capability.h>#include <linux/cpu.h>#include <linux/moduleparam.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/vermagic.h>#include <linux/notifier.h>#include <linux/sched.h>#include <linux/device.h>#include <linux/string.h>#include <linux/mutex.h>#include <linux/rculist.h>#include <linux/uaccess.h>#include <asm/cacheflush.h>#include <linux/set_memory.h>#include <asm/mmu_context.h>#include <linux/license.h>#include <asm/sections.h>#include <linux/tracepoint.h>#include <linux/ftrace.h>#include <linux/livepatch.h>#include <linux/async.h>#include <linux/percpu.h>#include <linux/kmemleak.h>#include <linux/jump_label.h>#include <linux/pfn.h>#include <linux/bsearch.h>#include <linux/dynamic_debug.h>#include <linux/audit.h>#include <uapi/linux/module.h>#include "module-internal.h" #define CREATE_TRACE_POINTS#include <trace/events/module.h> #ifndef ARCH_SHF_SMALL#define ARCH_SHF_SMALL 0#endif /* * Modules' sections will be aligned on page boundaries * to ensure complete separation of code and data, but * only when CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y */#ifdef CONFIG_ARCH_HAS_STRICT_MODULE_RWX# define debug_align(X) ALIGN(X, PAGE_SIZE)#else# define debug_align(X) (X)#endif /* If this is set, the section belongs in the init part of the module */#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) /* * Mutex protects: * 1) List of modules (also safely readable with preempt_disable), * 2) module_use links, * 3) module_addr_min/module_addr_max. * (delete and add uses RCU list operations). */DEFINE_MUTEX(module_mutex);EXPORT_SYMBOL_GPL(module_mutex);static LIST_HEAD(modules); /* Work queue for freeing init sections in success case */static struct work_struct init_free_wq;static struct llist_head init_free_list; #ifdef CONFIG_MODULES_TREE_LOOKUP /* * Use a latched RB-tree for __module_address(); this allows us to use * RCU-sched lookups of the address from any context. * * This is conditional on PERF_EVENTS || TRACING because those can really hit * __module_address() hard by doing a lot of stack unwinding; potentially from * NMI context. */ static __always_inline unsigned long __mod_tree_val(struct latch_tree_node *n){ struct module_layout *layout = container_of(n, struct module_layout, mtn.node); return (unsigned long)layout->base;} static __always_inline unsigned long __mod_tree_size(struct latch_tree_node *n){ struct module_layout *layout = container_of(n, struct module_layout, mtn.node); return (unsigned long)layout->size;} static __always_inline boolmod_tree_less(struct latch_tree_node *a, struct latch_tree_node *b){ return __mod_tree_val(a) < __mod_tree_val(b);} static __always_inline intmod_tree_comp(void *key, struct latch_tree_node *n){ unsigned long val = (unsigned long)key; unsigned long start, end; start = __mod_tree_val(n); if (val < start) return -1; end = start + __mod_tree_size(n); if (val >= end) return 1; return 0;} static const struct latch_tree_ops mod_tree_ops = { .less = mod_tree_less, .comp = mod_tree_comp,}; static struct mod_tree_root { struct latch_tree_root root; unsigned long addr_min; unsigned long addr_max;} mod_tree __cacheline_aligned = { .addr_min = -1UL,}; #define module_addr_min mod_tree.addr_min#define module_addr_max mod_tree.addr_max static noinline void __mod_tree_insert(struct mod_tree_node *node){ latch_tree_insert(&node->node, &mod_tree.root, &mod_tree_ops);} static void __mod_tree_remove(struct mod_tree_node *node){ latch_tree_erase(&node->node, &mod_tree.root, &mod_tree_ops);} /* * These modifications: insert, remove_init and remove; are serialized by the * module_mutex. */static void mod_tree_insert(struct module *mod){ mod->core_layout.mtn.mod = mod; mod->init_layout.mtn.mod = mod; __mod_tree_insert(&mod->core_layout.mtn); if (mod->init_layout.size) __mod_tree_insert(&mod->init_layout.mtn);} static void mod_tree_remove_init(struct module *mod){ if (mod->init_layout.size) __mod_tree_remove(&mod->init_layout.mtn);} static void mod_tree_remove(struct module *mod){ __mod_tree_remove(&mod->core_layout.mtn); mod_tree_remove_init(mod);} static struct module *mod_find(unsigned long addr){ struct latch_tree_node *ltn; ltn = latch_tree_find((void *)addr, &mod_tree.root, &mod_tree_ops); if (!ltn) return NULL; return container_of(ltn, struct mod_tree_node, node)->mod;} #else /* MODULES_TREE_LOOKUP */ static unsigned long module_addr_min = -1UL, module_addr_max = 0; static void mod_tree_insert(struct module *mod) { }static void mod_tree_remove_init(struct module *mod) { }static void mod_tree_remove(struct module *mod) { } static struct module *mod_find(unsigned long addr){ struct module *mod; list_for_each_entry_rcu(mod, &modules, list, lockdep_is_held(&module_mutex)) { if (within_module(addr, mod)) return mod; } return NULL;} #endif /* MODULES_TREE_LOOKUP */ /* * Bounds of module text, for speeding up __module_address. * Protected by module_mutex. */static void __mod_update_bounds(void *base, unsigned int size){ unsigned long min = (unsigned long)base; unsigned long max = min + size; if (min < module_addr_min) module_addr_min = min; if (max > module_addr_max) module_addr_max = max;} static void mod_update_bounds(struct module *mod){ __mod_update_bounds(mod->core_layout.base, mod->core_layout.size); if (mod->init_layout.size) __mod_update_bounds(mod->init_layout.base, mod->init_layout.size);} #ifdef CONFIG_KGDB_KDBstruct list_head *kdb_modules = &modules; /* kdb needs the list of modules */#endif /* CONFIG_KGDB_KDB */ static void module_assert_mutex(void){ lockdep_assert_held(&module_mutex);} static void module_assert_mutex_or_preempt(void){#ifdef CONFIG_LOCKDEP if (unlikely(!debug_locks)) return; WARN_ON_ONCE(!rcu_read_lock_sched_held() && !lockdep_is_held(&module_mutex));#endif} static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE);module_param(sig_enforce, bool_enable_only, 0644); /* * Export sig_enforce kernel cmdline parameter to allow other subsystems rely * on that instead of directly to CONFIG_MODULE_SIG_FORCE config. */bool is_module_sig_enforced(void){ return sig_enforce;}EXPORT_SYMBOL(is_module_sig_enforced); void set_module_sig_enforced(void){ sig_enforce = true;} /* Block module loading/unloading? */int modules_disabled = 0;core_param(nomodule, modules_disabled, bint, 0); /* Waiting for a module to finish initializing? */static DECLARE_WAIT_QUEUE_HEAD(module_wq); static BLOCKING_NOTIFIER_HEAD(module_notify_list); int register_module_notifier(struct notifier_block *nb){ return blocking_notifier_chain_register(&module_notify_list, nb);}EXPORT_SYMBOL(register_module_notifier); int unregister_module_notifier(struct notifier_block *nb){ return blocking_notifier_chain_unregister(&module_notify_list, nb);}EXPORT_SYMBOL(unregister_module_notifier); /* * We require a truly strong try_module_get(): 0 means success. * Otherwise an error is returned due to ongoing or failed * initialization etc. */static inline int strong_try_module_get(struct module *mod){ BUG_ON(mod && mod->state == MODULE_STATE_UNFORMED); if (mod && mod->state == MODULE_STATE_COMING) return -EBUSY; if (try_module_get(mod)) return 0; else return -ENOENT;} static inline void add_taint_module(struct module *mod, unsigned flag, enum lockdep_ok lockdep_ok){ add_taint(flag, lockdep_ok); set_bit(flag, &mod->taints);} /* * A thread that wants to hold a reference to a module only while it * is running can call this to safely exit. nfsd and lockd use this. */void __noreturn __module_put_and_exit(struct module *mod, long code){ module_put(mod); do_exit(code);}EXPORT_SYMBOL(__module_put_and_exit); /* Find a module section: 0 means not found. */static unsigned int find_sec(const struct load_info *info, const char *name){ unsigned int i; for (i = 1; i < info->hdr->e_shnum; i++) { Elf_Shdr *shdr = &info->sechdrs[i]; /* Alloc bit cleared means "ignore it." */ if ((shdr->sh_flags & SHF_ALLOC) && strcmp(info->secstrings + shdr->sh_name, name) == 0) return i; } return 0;} /* Find a module section, or NULL. */static void *section_addr(const struct load_info *info, const char *name){ /* Section 0 has sh_addr 0. */ return (void *)info->sechdrs[find_sec(info, name)].sh_addr;} /* Find a module section, or NULL. Fill in number of "objects" in section. */static void *section_objs(const struct load_info *info, const char *name, size_t object_size, unsigned int *num){ unsigned int sec = find_sec(info, name); /* Section 0 has sh_addr 0 and sh_size 0. */ *num = info->sechdrs[sec].sh_size / object_size; return (void *)info->sechdrs[sec].sh_addr;} /* Provided by the linker */extern const struct kernel_symbol __start___ksymtab[];extern const struct kernel_symbol __stop___ksymtab[];extern const struct kernel_symbol __start___ksymtab_gpl[];extern const struct kernel_symbol __stop___ksymtab_gpl[];extern const struct kernel_symbol __start___ksymtab_gpl_future[];extern const struct kernel_symbol __stop___ksymtab_gpl_future[];extern const s32 __start___kcrctab[];extern const s32 __start___kcrctab_gpl[];extern const s32 __start___kcrctab_gpl_future[];#ifdef CONFIG_UNUSED_SYMBOLSextern const struct kernel_symbol __start___ksymtab_unused[];extern const struct kernel_symbol __stop___ksymtab_unused[];extern const struct kernel_symbol __start___ksymtab_unused_gpl[];extern const struct kernel_symbol __stop___ksymtab_unused_gpl[];extern const s32 __start___kcrctab_unused[];extern const s32 __start___kcrctab_unused_gpl[];#endif #ifndef CONFIG_MODVERSIONS#define symversion(base, idx) NULL#else#define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL)#endif static bool each_symbol_in_section(const struct symsearch *arr, unsigned int arrsize, struct module *owner, bool (*fn)(const struct symsearch *syms, struct module *owner, void *data), void *data){ unsigned int j; for (j = 0; j < arrsize; j++) { if (fn(&arr[j], owner, data)) return true; } return false;} /* Returns true as soon as fn returns true, otherwise false. */static bool each_symbol_section(bool (*fn)(const struct symsearch *arr, struct module *owner, void *data), void *data){ struct module *mod; static const struct symsearch arr[] = { { __start___ksymtab, __stop___ksymtab, __start___kcrctab, NOT_GPL_ONLY, false }, { __start___ksymtab_gpl, __stop___ksymtab_gpl, __start___kcrctab_gpl, GPL_ONLY, false }, { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, __start___kcrctab_gpl_future, WILL_BE_GPL_ONLY, false },#ifdef CONFIG_UNUSED_SYMBOLS { __start___ksymtab_unused, __stop___ksymtab_unused, __start___kcrctab_unused, NOT_GPL_ONLY, true }, { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, __start___kcrctab_unused_gpl, GPL_ONLY, true },#endif }; module_assert_mutex_or_preempt(); if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data)) return true; list_for_each_entry_rcu(mod, &modules, list, lockdep_is_held(&module_mutex)) { struct symsearch arr[] = { { mod->syms, mod->syms + mod->num_syms, mod->crcs, NOT_GPL_ONLY, false }, { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, mod->gpl_crcs, GPL_ONLY, false }, { mod->gpl_future_syms, mod->gpl_future_syms + mod->num_gpl_future_syms, mod->gpl_future_crcs, WILL_BE_GPL_ONLY, false },#ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->unused_syms + mod->num_unused_syms, mod->unused_crcs, NOT_GPL_ONLY, true }, { mod->unused_gpl_syms, mod->unused_gpl_syms + mod->num_unused_gpl_syms, mod->unused_gpl_crcs, GPL_ONLY, true },#endif }; if (mod->state == MODULE_STATE_UNFORMED) continue; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data)) return true; } return false;} struct find_symbol_arg { /* Input */ const char *name; bool gplok; bool warn; /* Output */ struct module *owner; const s32 *crc; const struct kernel_symbol *sym; enum mod_license license;}; Lines 1–500 of 4590. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* Copyright (C) 2002 Richard Henderson Copyright (C) 2001 Rusty Russell, 2002, 2010 Rusty Russell IBM. */ #define INCLUDE_VERMAGIC #include <linux/export.h> #include <linux/extable.h> #include <linux/moduleloader.h> #include <linux/module_signature.h> #include <linux/trace_events.h> #include <linux/init.h> #include <linux/kallsyms.h> #include <linux/file.h> #include <linux/fs.h> #include <linux/sysfs.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/elf.h> #include <linux/proc_fs.h> #include <linux/security.h> #include <linux/seq_file.h> #include <linux/syscalls.h> #include <linux/fcntl.h> #include <linux/rcupdate.h> #include <linux/capability.h> #include <linux/cpu.h> #include <linux/moduleparam.h> #include <linux/errno.h> #include <linux/err.h> #include <linux/vermagic.h> #include <linux/notifier.h> #include <linux/sched.h> #include <linux/device.h> #include <linux/string.h> #include <linux/mutex.h> #include <linux/rculist.h> #include <linux/uaccess.h> #include <asm/cacheflush.h> #include <linux/set_memory.h> #include <asm/mmu_context.h> #include <linux/license.h> #include <asm/sections.h> #include <linux/tracepoint.h> #include <linux/ftrace.h> #include <linux/livepatch.h> #include <linux/async.h> #include <linux/percpu.h> #include <linux/kmemleak.h> #include <linux/jump_label.h> #include <linux/pfn.h> #include <linux/bsearch.h> #include <linux/dynamic_debug.h> #include <linux/audit.h> #include <uapi/linux/module.h> #include "module-internal.h" #define CREATE_TRACE_POINTS #include <trace/events/module.h> #ifndef ARCH_SHF_SMALL #define ARCH_SHF_SMALL 0 #endif /* * Modules' sections will be aligned on page boundaries * to ensure complete separation of code and data, but * only when CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y */ #ifdef CONFIG_ARCH_HAS_STRICT_MODULE_RWX # define debug_align(X) ALIGN(X, PAGE_SIZE) #else # define debug_align(X) (X) #endif /* If this is set, the section belongs in the init part of the module */ #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) /* * Mutex protects: * 1) List of modules (also safely readable with preempt_disable), * 2) module_use links, * 3) module_addr_min/module_addr_max. * (delete and add uses RCU list operations). */ DEFINE_MUTEX(module_mutex); EXPORT_SYMBOL_GPL(module_mutex); static LIST_HEAD(modules); /* Work queue for freeing init sections in success case */ static struct work_struct init_free_wq; static struct llist_head init_free_list; #ifdef CONFIG_MODULES_TREE_LOOKUP /* * Use a latched RB-tree for __module_address(); this allows us to use * RCU-sched lookups of the address from any context. * * This is conditional on PERF_EVENTS || TRACING because those can really hit * __module_address() hard by doing a lot of stack unwinding; potentially from * NMI context. */ static __always_inline unsigned long __mod_tree_val(struct latch_tree_node *n) { struct module_layout *layout = container_of(n, struct module_layout, mtn.node); return (unsigned long)layout->base; } static __always_inline unsigned long __mod_tree_size(struct latch_tree_node *n) { struct module_layout *layout = container_of(n, struct module_layout, mtn.node); return (unsigned long)layout->size; } static __always_inline bool mod_tree_less(struct latch_tree_node *a, struct latch_tree_node *b) { return __mod_tree_val(a) < __mod_tree_val(b); } static __always_inline int mod_tree_comp(void *key, struct latch_tree_node *n) { unsigned long val = (unsigned long)key; unsigned long start, end; start = __mod_tree_val(n); if (val < start) return -1; end = start + __mod_tree_size(n); if (val >= end) return 1; return 0; } static const struct latch_tree_ops mod_tree_ops = { .less = mod_tree_less, .comp = mod_tree_comp, }; static struct mod_tree_root { struct latch_tree_root root; unsigned long addr_min; unsigned long addr_max; } mod_tree __cacheline_aligned = { .addr_min = -1UL, }; #define module_addr_min mod_tree.addr_min #define module_addr_max mod_tree.addr_max static noinline void __mod_tree_insert(struct mod_tree_node *node) { latch_tree_insert(&node->node, &mod_tree.root, &mod_tree_ops); } static void __mod_tree_remove(struct mod_tree_node *node) { latch_tree_erase(&node->node, &mod_tree.root, &mod_tree_ops); } /* * These modifications: insert, remove_init and remove; are serialized by the * module_mutex. */ static void mod_tree_insert(struct module *mod) { mod->core_layout.mtn.mod = mod; mod->init_layout.mtn.mod = mod; __mod_tree_insert(&mod->core_layout.mtn); if (mod->init_layout.size) __mod_tree_insert(&mod->init_layout.mtn); } static void mod_tree_remove_init(struct module *mod) { if (mod->init_layout.size) __mod_tree_remove(&mod->init_layout.mtn); } static void mod_tree_remove(struct module *mod) { __mod_tree_remove(&mod->core_layout.mtn); mod_tree_remove_init(mod); } static struct module *mod_find(unsigned long addr) { struct latch_tree_node *ltn; ltn = latch_tree_find((void *)addr, &mod_tree.root, &mod_tree_ops); if (!ltn) return NULL; return container_of(ltn, struct mod_tree_node, node)->mod; } #else /* MODULES_TREE_LOOKUP */ static unsigned long module_addr_min = -1UL, module_addr_max = 0; static void mod_tree_insert(struct module *mod) { } static void mod_tree_remove_init(struct module *mod) { } static void mod_tree_remove(struct module *mod) { } static struct module *mod_find(unsigned long addr) { struct module *mod; list_for_each_entry_rcu(mod, &modules, list, lockdep_is_held(&module_mutex)) { if (within_module(addr, mod)) return mod; } return NULL; } #endif /* MODULES_TREE_LOOKUP */ /* * Bounds of module text, for speeding up __module_address. * Protected by module_mutex. */ static void __mod_update_bounds(void *base, unsigned int size) { unsigned long min = (unsigned long)base; unsigned long max = min + size; if (min < module_addr_min) module_addr_min = min; if (max > module_addr_max) module_addr_max = max; } static void mod_update_bounds(struct module *mod) { __mod_update_bounds(mod->core_layout.base, mod->core_layout.size); if (mod->init_layout.size) __mod_update_bounds(mod->init_layout.base, mod->init_layout.size); } #ifdef CONFIG_KGDB_KDB struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ #endif /* CONFIG_KGDB_KDB */ static void module_assert_mutex(void) { lockdep_assert_held(&module_mutex); } static void module_assert_mutex_or_preempt(void) { #ifdef CONFIG_LOCKDEP if (unlikely(!debug_locks)) return; WARN_ON_ONCE(!rcu_read_lock_sched_held() && !lockdep_is_held(&module_mutex)); #endif } static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE); module_param(sig_enforce, bool_enable_only, 0644); /* * Export sig_enforce kernel cmdline parameter to allow other subsystems rely * on that instead of directly to CONFIG_MODULE_SIG_FORCE config. */ bool is_module_sig_enforced(void) { return sig_enforce; } EXPORT_SYMBOL(is_module_sig_enforced); void set_module_sig_enforced(void) { sig_enforce = true; } /* Block module loading/unloading? */ int modules_disabled = 0; core_param(nomodule, modules_disabled, bint, 0); /* Waiting for a module to finish initializing? */ static DECLARE_WAIT_QUEUE_HEAD(module_wq); static BLOCKING_NOTIFIER_HEAD(module_notify_list); int register_module_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&module_notify_list, nb); } EXPORT_SYMBOL(register_module_notifier); int unregister_module_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&module_notify_list, nb); } EXPORT_SYMBOL(unregister_module_notifier); /* * We require a truly strong try_module_get(): 0 means success. * Otherwise an error is returned due to ongoing or failed * initialization etc. */ static inline int strong_try_module_get(struct module *mod) { BUG_ON(mod && mod->state == MODULE_STATE_UNFORMED); if (mod && mod->state == MODULE_STATE_COMING) return -EBUSY; if (try_module_get(mod)) return 0; else return -ENOENT; } static inline void add_taint_module(struct module *mod, unsigned flag, enum lockdep_ok lockdep_ok) { add_taint(flag, lockdep_ok); set_bit(flag, &mod->taints); } /* * A thread that wants to hold a reference to a module only while it * is running can call this to safely exit. nfsd and lockd use this. */ void __noreturn __module_put_and_exit(struct module *mod, long code) { module_put(mod); do_exit(code); } EXPORT_SYMBOL(__module_put_and_exit); /* Find a module section: 0 means not found. */ static unsigned int find_sec(const struct load_info *info, const char *name) { unsigned int i; for (i = 1; i < info->hdr->e_shnum; i++) { Elf_Shdr *shdr = &info->sechdrs[i]; /* Alloc bit cleared means "ignore it." */ if ((shdr->sh_flags & SHF_ALLOC) && strcmp(info->secstrings + shdr->sh_name, name) == 0) return i; } return 0; } /* Find a module section, or NULL. */ static void *section_addr(const struct load_info *info, const char *name) { /* Section 0 has sh_addr 0. */ return (void *)info->sechdrs[find_sec(info, name)].sh_addr; } /* Find a module section, or NULL. Fill in number of "objects" in section. */ static void *section_objs(const struct load_info *info, const char *name, size_t object_size, unsigned int *num) { unsigned int sec = find_sec(info, name); /* Section 0 has sh_addr 0 and sh_size 0. */ *num = info->sechdrs[sec].sh_size / object_size; return (void *)info->sechdrs[sec].sh_addr; } /* Provided by the linker */ extern const struct kernel_symbol __start___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[]; extern const struct kernel_symbol __start___ksymtab_gpl[]; extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; extern const s32 __start___kcrctab[]; extern const s32 __start___kcrctab_gpl[]; extern const s32 __start___kcrctab_gpl_future[]; #ifdef CONFIG_UNUSED_SYMBOLS extern const struct kernel_symbol __start___ksymtab_unused[]; extern const struct kernel_symbol __stop___ksymtab_unused[]; extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; extern const s32 __start___kcrctab_unused[]; extern const s32 __start___kcrctab_unused_gpl[]; #endif #ifndef CONFIG_MODVERSIONS #define symversion(base, idx) NULL #else #define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL) #endif static bool each_symbol_in_section(const struct symsearch *arr, unsigned int arrsize, struct module *owner, bool (*fn)(const struct symsearch *syms, struct module *owner, void *data), void *data) { unsigned int j; for (j = 0; j < arrsize; j++) { if (fn(&arr[j], owner, data)) return true; } return false; } /* Returns true as soon as fn returns true, otherwise false. */ static bool each_symbol_section(bool (*fn)(const struct symsearch *arr, struct module *owner, void *data), void *data) { struct module *mod; static const struct symsearch arr[] = { { __start___ksymtab, __stop___ksymtab, __start___kcrctab, NOT_GPL_ONLY, false }, { __start___ksymtab_gpl, __stop___ksymtab_gpl, __start___kcrctab_gpl, GPL_ONLY, false }, { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future, __start___kcrctab_gpl_future, WILL_BE_GPL_ONLY, false }, #ifdef CONFIG_UNUSED_SYMBOLS { __start___ksymtab_unused, __stop___ksymtab_unused, __start___kcrctab_unused, NOT_GPL_ONLY, true }, { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl, __start___kcrctab_unused_gpl, GPL_ONLY, true }, #endif }; module_assert_mutex_or_preempt(); if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data)) return true; list_for_each_entry_rcu(mod, &modules, list, lockdep_is_held(&module_mutex)) { struct symsearch arr[] = { { mod->syms, mod->syms + mod->num_syms, mod->crcs, NOT_GPL_ONLY, false }, { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms, mod->gpl_crcs, GPL_ONLY, false }, { mod->gpl_future_syms, mod->gpl_future_syms + mod->num_gpl_future_syms, mod->gpl_future_crcs, WILL_BE_GPL_ONLY, false }, #ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->unused_syms + mod->num_unused_syms, mod->unused_crcs, NOT_GPL_ONLY, true }, { mod->unused_gpl_syms, mod->unused_gpl_syms + mod->num_unused_gpl_syms, mod->unused_gpl_crcs, GPL_ONLY, true }, #endif }; if (mod->state == MODULE_STATE_UNFORMED) continue; if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data)) return true; } return false; } struct find_symbol_arg { /* Input */ const char *name; bool gplok; bool warn; /* Output */ struct module *owner; const s32 *crc; const struct kernel_symbol *sym; enum mod_license license; };