Modules updates for v5.12

Summary of modules changes for the 5.12 merge window:
 
 - Retire EXPORT_UNUSED_SYMBOL() and EXPORT_SYMBOL_GPL_FUTURE(). These export
   types were introduced between 2006 - 2008. All the of the unused symbols have
   been long removed and gpl future symbols were converted to gpl quite a long
   time ago, and I don't believe these export types have been used ever since.
   So, I think it should be safe to retire those export types now. (Christoph Hellwig)
 
 - Refactor and clean up some aged code cruft in the module loader (Christoph Hellwig)
 
 - Build {,module_}kallsyms_on_each_symbol only when livepatching is enabled, as
   it is the only caller (Christoph Hellwig)
 
 - Unexport find_module() and module_mutex and fix the last module
   callers to not rely on these anymore. Make module_mutex internal to
   the module loader. (Christoph Hellwig)
 
 - Harden ELF checks on module load and validate ELF structures before checking
   the module signature (Frank van der Linden)
 
 - Fix undefined symbol warning for clang (Fangrui Song)
 
 - Fix smatch warning (Dan Carpenter)
 
 Signed-off-by: Jessica Yu <jeyu@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCAAuFiEEVrp26glSWYuDNrCUwEV+OM47wXIFAmA0/KMQHGpleXVAa2Vy
 bmVsLm9yZwAKCRDARX44zjvBcu0uD/4nmRp18EKAtdUZivsZHat0aEWGlkmrVueY
 5huYw6iwM8b/wIAl3xwLki1Iv0/l0a83WXZhLG4ekl0/Nj8kgllA+jtBrZWpoLMH
 CZusN5dS9YwwyD2vu3ak83ARcehcDEPeA9thvc3uRFGis6Hi4bt1rkzGdrzsgqR4
 tybfN4qaQx4ZAKFxA8bnS58l7QTFwUzTxJfM6WWzl1Q+mLZDr/WP+loJ/f1/oFFg
 ufN31KrqqFpdQY5UKq5P4H8FVq/eXE1Mwl8vo3HsnAj598fznyPUmA3D/j+N4GuR
 sTGBVZ9CSehUj7uZRs+Qgg6Bd+y3o44N29BrdZWA6K3ieTeQQpA+VgPUNrDBjGhP
 J/9Y4ms4PnuNEWWRaa73m9qsVqAsjh9+T2xp9PYn9uWLCM8BvQFtWcY7tw4/nB0/
 INmyiP/tIRpwWkkBl47u1TPR09FzBBGDZjBiSn3lm3VX+zCYtHoma5jWyejG11cf
 ybDrTsci9ANyHNP2zFQsUOQJkph78PIal0i3k4ODqGJvaC0iEIH3Xjv+0dmE14rq
 kGRrG/HN6HhMZPjashudVUktyTZ63+PJpfFlQbcUzdvjQQIkzW0vrCHMWx9vD1xl
 Na7vZLl4Nb03WSJp6saY6j2YSRKL0poGETzGqrsUAHEhpEOPHduaiCVlAr/EmeMk
 p6SrWv8+UQ==
 =T29Q
 -----END PGP SIGNATURE-----

Merge tag 'modules-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux

Pull module updates from Jessica Yu:

 - Retire EXPORT_UNUSED_SYMBOL() and EXPORT_SYMBOL_GPL_FUTURE(). These
   export types were introduced between 2006 - 2008. All the of the
   unused symbols have been long removed and gpl future symbols were
   converted to gpl quite a long time ago, and I don't believe these
   export types have been used ever since. So, I think it should be safe
   to retire those export types now (Christoph Hellwig)

 - Refactor and clean up some aged code cruft in the module loader
   (Christoph Hellwig)

 - Build {,module_}kallsyms_on_each_symbol only when livepatching is
   enabled, as it is the only caller (Christoph Hellwig)

 - Unexport find_module() and module_mutex and fix the last module
   callers to not rely on these anymore. Make module_mutex internal to
   the module loader (Christoph Hellwig)

 - Harden ELF checks on module load and validate ELF structures before
   checking the module signature (Frank van der Linden)

 - Fix undefined symbol warning for clang (Fangrui Song)

 - Fix smatch warning (Dan Carpenter)

* tag 'modules-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux:
  module: potential uninitialized return in module_kallsyms_on_each_symbol()
  module: remove EXPORT_UNUSED_SYMBOL*
  module: remove EXPORT_SYMBOL_GPL_FUTURE
  module: move struct symsearch to module.c
  module: pass struct find_symbol_args to find_symbol
  module: merge each_symbol_section into find_symbol
  module: remove each_symbol_in_section
  module: mark module_mutex static
  kallsyms: only build {,module_}kallsyms_on_each_symbol when required
  kallsyms: refactor {,module_}kallsyms_on_each_symbol
  module: use RCU to synchronize find_module
  module: unexport find_module and module_mutex
  drm: remove drm_fb_helper_modinit
  powerpc/powernv: remove get_cxl_module
  module: harden ELF info handling
  module: Ignore _GLOBAL_OFFSET_TABLE_ when warning for undefined symbols
This commit is contained in:
Linus Torvalds 2021-02-23 10:15:33 -08:00
commit 21a6ab2131
35 changed files with 282 additions and 511 deletions

View File

@ -176,7 +176,6 @@ CONFIG_BOOT_PRINTK_DELAY=y
CONFIG_DYNAMIC_DEBUG=y CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO=y
# CONFIG_ENABLE_MUST_CHECK is not set # CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_UNUSED_SYMBOLS=y
CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_LOCKUP_DETECTOR=y CONFIG_LOCKUP_DETECTOR=y
CONFIG_SCHED_TRACER=y CONFIG_SCHED_TRACER=y

View File

@ -164,7 +164,6 @@ CONFIG_FONTS=y
CONFIG_PRINTK_TIME=y CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO=y
CONFIG_FRAME_WARN=2048 CONFIG_FRAME_WARN=2048
CONFIG_UNUSED_SYMBOLS=y
CONFIG_MAGIC_SYSRQ=y CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_KERNEL=y
CONFIG_SOFTLOCKUP_DETECTOR=y CONFIG_SOFTLOCKUP_DETECTOR=y

View File

@ -549,7 +549,6 @@ CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO=y
# CONFIG_ENABLE_MUST_CHECK is not set # CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_FRAME_WARN=1024 CONFIG_FRAME_WARN=1024
CONFIG_UNUSED_SYMBOLS=y
CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_DETECT_HUNG_TASK=y CONFIG_DETECT_HUNG_TASK=y
CONFIG_SCHEDSTATS=y CONFIG_SCHEDSTATS=y

View File

@ -500,7 +500,6 @@ CONFIG_CRC7=m
CONFIG_PRINTK_TIME=y CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO=y
# CONFIG_ENABLE_MUST_CHECK is not set # CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_UNUSED_SYMBOLS=y
CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_MEMORY_INIT=y
CONFIG_DETECT_HUNG_TASK=y CONFIG_DETECT_HUNG_TASK=y
CONFIG_SCHEDSTATS=y CONFIG_SCHEDSTATS=y

View File

@ -22,7 +22,6 @@ CONFIG_PCI_LBA=y
CONFIG_MODULES=y CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_UNUSED_SYMBOLS=y
# CONFIG_BLK_DEV_BSG is not set # CONFIG_BLK_DEV_BSG is not set
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_BINFMT_MISC=m CONFIG_BINFMT_MISC=m

View File

@ -31,7 +31,6 @@ CONFIG_MODULE_FORCE_LOAD=y
CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y CONFIG_MODVERSIONS=y
CONFIG_UNUSED_SYMBOLS=y
CONFIG_BLK_DEV_INTEGRITY=y CONFIG_BLK_DEV_INTEGRITY=y
CONFIG_BINFMT_MISC=m CONFIG_BINFMT_MISC=m
# CONFIG_COMPACTION is not set # CONFIG_COMPACTION is not set

View File

@ -1071,7 +1071,6 @@ CONFIG_NLS_ISO8859_15=m
CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_R=m
CONFIG_NLS_KOI8_U=m CONFIG_NLS_KOI8_U=m
CONFIG_DEBUG_INFO=y CONFIG_DEBUG_INFO=y
CONFIG_UNUSED_SYMBOLS=y
CONFIG_HEADERS_INSTALL=y CONFIG_HEADERS_INSTALL=y
CONFIG_MAGIC_SYSRQ=y CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_KERNEL=y

View File

@ -150,25 +150,3 @@ int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq,
return 0; return 0;
} }
EXPORT_SYMBOL(pnv_cxl_ioda_msi_setup); EXPORT_SYMBOL(pnv_cxl_ioda_msi_setup);
#if IS_MODULE(CONFIG_CXL)
static inline int get_cxl_module(void)
{
struct module *cxl_module;
mutex_lock(&module_mutex);
cxl_module = find_module("cxl");
if (cxl_module)
__module_get(cxl_module);
mutex_unlock(&module_mutex);
if (!cxl_module)
return -ENODEV;
return 0;
}
#else
static inline int get_cxl_module(void) { return 0; }
#endif

View File

@ -71,7 +71,6 @@ CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_MODULE_SIG_SHA256=y CONFIG_MODULE_SIG_SHA256=y
CONFIG_UNUSED_SYMBOLS=y
CONFIG_BLK_DEV_INTEGRITY=y CONFIG_BLK_DEV_INTEGRITY=y
CONFIG_BLK_DEV_THROTTLING=y CONFIG_BLK_DEV_THROTTLING=y
CONFIG_BLK_WBT=y CONFIG_BLK_WBT=y

View File

@ -66,7 +66,6 @@ CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_MODULE_SIG_SHA256=y CONFIG_MODULE_SIG_SHA256=y
CONFIG_UNUSED_SYMBOLS=y
CONFIG_BLK_DEV_THROTTLING=y CONFIG_BLK_DEV_THROTTLING=y
CONFIG_BLK_WBT=y CONFIG_BLK_WBT=y
CONFIG_BLK_CGROUP_IOLATENCY=y CONFIG_BLK_CGROUP_IOLATENCY=y

View File

@ -102,7 +102,6 @@ CONFIG_NLS_UTF8=y
CONFIG_PRINTK_TIME=y CONFIG_PRINTK_TIME=y
# CONFIG_ENABLE_MUST_CHECK is not set # CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_MAGIC_SYSRQ=y CONFIG_MAGIC_SYSRQ=y
CONFIG_UNUSED_SYMBOLS=y
CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_SHIRQ=y CONFIG_DEBUG_SHIRQ=y
CONFIG_DETECT_HUNG_TASK=y CONFIG_DETECT_HUNG_TASK=y

View File

@ -128,7 +128,6 @@ CONFIG_NLS_ISO8859_15=y
CONFIG_NLS_UTF8=y CONFIG_NLS_UTF8=y
# CONFIG_ENABLE_MUST_CHECK is not set # CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_MAGIC_SYSRQ=y CONFIG_MAGIC_SYSRQ=y
CONFIG_UNUSED_SYMBOLS=y
CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_KERNEL=y
CONFIG_DETECT_HUNG_TASK=y CONFIG_DETECT_HUNG_TASK=y
# CONFIG_SCHED_DEBUG is not set # CONFIG_SCHED_DEBUG is not set

View File

@ -50,7 +50,6 @@ CONFIG_JUMP_LABEL=y
CONFIG_MODULES=y CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_BINFMT_MISC=y CONFIG_BINFMT_MISC=y
CONFIG_NET=y CONFIG_NET=y
CONFIG_PACKET=y CONFIG_PACKET=y

View File

@ -48,7 +48,6 @@ CONFIG_JUMP_LABEL=y
CONFIG_MODULES=y CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_BINFMT_MISC=y CONFIG_BINFMT_MISC=y
CONFIG_NET=y CONFIG_NET=y
CONFIG_PACKET=y CONFIG_PACKET=y

View File

@ -61,8 +61,8 @@ static const char * const sym_regex_kernel[S_NSYMTYPES] = {
"(__iommu_table|__apicdrivers|__smp_locks)(|_end)|" "(__iommu_table|__apicdrivers|__smp_locks)(|_end)|"
"__(start|end)_pci_.*|" "__(start|end)_pci_.*|"
"__(start|end)_builtin_fw|" "__(start|end)_builtin_fw|"
"__(start|stop)___ksymtab(|_gpl|_unused|_unused_gpl|_gpl_future)|" "__(start|stop)___ksymtab(|_gpl)|"
"__(start|stop)___kcrctab(|_gpl|_unused|_unused_gpl|_gpl_future)|" "__(start|stop)___kcrctab(|_gpl)|"
"__(start|stop)___param|" "__(start|stop)___param|"
"__(start|stop)___modver|" "__(start|stop)___modver|"
"__(start|stop)___bug_table|" "__(start|stop)___bug_table|"

View File

@ -32,16 +32,6 @@
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>
#include <drm/drm_modes.h> #include <drm/drm_modes.h>
/* drm_fb_helper.c */
#ifdef CONFIG_DRM_FBDEV_EMULATION
int drm_fb_helper_modinit(void);
#else
static inline int drm_fb_helper_modinit(void)
{
return 0;
}
#endif
/* drm_dp_aux_dev.c */ /* drm_dp_aux_dev.c */
#ifdef CONFIG_DRM_DP_AUX_CHARDEV #ifdef CONFIG_DRM_DP_AUX_CHARDEV
int drm_dp_aux_dev_init(void); int drm_dp_aux_dev_init(void);

View File

@ -2514,24 +2514,3 @@ void drm_fbdev_generic_setup(struct drm_device *dev,
drm_client_register(&fb_helper->client); drm_client_register(&fb_helper->client);
} }
EXPORT_SYMBOL(drm_fbdev_generic_setup); EXPORT_SYMBOL(drm_fbdev_generic_setup);
/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
* but the module doesn't depend on any fb console symbols. At least
* attempt to load fbcon to avoid leaving the system without a usable console.
*/
int __init drm_fb_helper_modinit(void)
{
#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
const char name[] = "fbcon";
struct module *fbcon;
mutex_lock(&module_mutex);
fbcon = find_module(name);
mutex_unlock(&module_mutex);
if (!fbcon)
request_module_nowait(name);
#endif
return 0;
}
EXPORT_SYMBOL(drm_fb_helper_modinit);

View File

@ -64,19 +64,18 @@ MODULE_PARM_DESC(edid_firmware,
static int __init drm_kms_helper_init(void) static int __init drm_kms_helper_init(void)
{ {
int ret; /*
* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
* but the module doesn't depend on any fb console symbols. At least
* attempt to load fbcon to avoid leaving the system without a usable
* console.
*/
if (IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) &&
IS_MODULE(CONFIG_FRAMEBUFFER_CONSOLE) &&
!IS_ENABLED(CONFIG_EXPERT))
request_module_nowait("fbcon");
/* Call init functions from specific kms helpers here */ return drm_dp_aux_dev_init();
ret = drm_fb_helper_modinit();
if (ret < 0)
goto out;
ret = drm_dp_aux_dev_init();
if (ret < 0)
goto out;
out:
return ret;
} }
static void __exit drm_kms_helper_exit(void) static void __exit drm_kms_helper_exit(void)

View File

@ -497,27 +497,6 @@
__stop___ksymtab_gpl = .; \ __stop___ksymtab_gpl = .; \
} \ } \
\ \
/* Kernel symbol table: Normal unused symbols */ \
__ksymtab_unused : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) { \
__start___ksymtab_unused = .; \
KEEP(*(SORT(___ksymtab_unused+*))) \
__stop___ksymtab_unused = .; \
} \
\
/* Kernel symbol table: GPL-only unused symbols */ \
__ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \
__start___ksymtab_unused_gpl = .; \
KEEP(*(SORT(___ksymtab_unused_gpl+*))) \
__stop___ksymtab_unused_gpl = .; \
} \
\
/* Kernel symbol table: GPL-future-only symbols */ \
__ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \
__start___ksymtab_gpl_future = .; \
KEEP(*(SORT(___ksymtab_gpl_future+*))) \
__stop___ksymtab_gpl_future = .; \
} \
\
/* Kernel symbol table: Normal symbols */ \ /* Kernel symbol table: Normal symbols */ \
__kcrctab : AT(ADDR(__kcrctab) - LOAD_OFFSET) { \ __kcrctab : AT(ADDR(__kcrctab) - LOAD_OFFSET) { \
__start___kcrctab = .; \ __start___kcrctab = .; \
@ -532,27 +511,6 @@
__stop___kcrctab_gpl = .; \ __stop___kcrctab_gpl = .; \
} \ } \
\ \
/* Kernel symbol table: Normal unused symbols */ \
__kcrctab_unused : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) { \
__start___kcrctab_unused = .; \
KEEP(*(SORT(___kcrctab_unused+*))) \
__stop___kcrctab_unused = .; \
} \
\
/* Kernel symbol table: GPL-only unused symbols */ \
__kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \
__start___kcrctab_unused_gpl = .; \
KEEP(*(SORT(___kcrctab_unused_gpl+*))) \
__stop___kcrctab_unused_gpl = .; \
} \
\
/* Kernel symbol table: GPL-future-only symbols */ \
__kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \
__start___kcrctab_gpl_future = .; \
KEEP(*(SORT(___kcrctab_gpl_future+*))) \
__stop___kcrctab_gpl_future = .; \
} \
\
/* Kernel symbol table: strings */ \ /* Kernel symbol table: strings */ \
__ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \ __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \
*(__ksymtab_strings) \ *(__ksymtab_strings) \

View File

@ -157,18 +157,9 @@ struct kernel_symbol {
#define EXPORT_SYMBOL(sym) _EXPORT_SYMBOL(sym, "") #define EXPORT_SYMBOL(sym) _EXPORT_SYMBOL(sym, "")
#define EXPORT_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "_gpl") #define EXPORT_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "_gpl")
#define EXPORT_SYMBOL_GPL_FUTURE(sym) _EXPORT_SYMBOL(sym, "_gpl_future")
#define EXPORT_SYMBOL_NS(sym, ns) __EXPORT_SYMBOL(sym, "", #ns) #define EXPORT_SYMBOL_NS(sym, ns) __EXPORT_SYMBOL(sym, "", #ns)
#define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL(sym, "_gpl", #ns) #define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL(sym, "_gpl", #ns)
#ifdef CONFIG_UNUSED_SYMBOLS
#define EXPORT_UNUSED_SYMBOL(sym) _EXPORT_SYMBOL(sym, "_unused")
#define EXPORT_UNUSED_SYMBOL_GPL(sym) _EXPORT_SYMBOL(sym, "_unused_gpl")
#else
#define EXPORT_UNUSED_SYMBOL(sym)
#define EXPORT_UNUSED_SYMBOL_GPL(sym)
#endif
#endif /* !__ASSEMBLY__ */ #endif /* !__ASSEMBLY__ */
#endif /* _LINUX_EXPORT_H */ #endif /* _LINUX_EXPORT_H */

View File

@ -71,15 +71,14 @@ static inline void *dereference_symbol_descriptor(void *ptr)
return ptr; return ptr;
} }
#ifdef CONFIG_KALLSYMS
/* Lookup the address for a symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name);
/* Call a function on each kallsyms symbol in the core kernel */
int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
unsigned long), unsigned long),
void *data); void *data);
#ifdef CONFIG_KALLSYMS
/* Lookup the address for a symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name);
extern int kallsyms_lookup_size_offset(unsigned long addr, extern int kallsyms_lookup_size_offset(unsigned long addr,
unsigned long *symbolsize, unsigned long *symbolsize,
unsigned long *offset); unsigned long *offset);
@ -108,14 +107,6 @@ static inline unsigned long kallsyms_lookup_name(const char *name)
return 0; return 0;
} }
static inline int kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *,
unsigned long),
void *data)
{
return 0;
}
static inline int kallsyms_lookup_size_offset(unsigned long addr, static inline int kallsyms_lookup_size_offset(unsigned long addr,
unsigned long *symbolsize, unsigned long *symbolsize,
unsigned long *offset) unsigned long *offset)

View File

@ -392,18 +392,6 @@ struct module {
const s32 *gpl_crcs; const s32 *gpl_crcs;
bool using_gplonly_symbols; bool using_gplonly_symbols;
#ifdef CONFIG_UNUSED_SYMBOLS
/* unused exported symbols. */
const struct kernel_symbol *unused_syms;
const s32 *unused_crcs;
unsigned int num_unused_syms;
/* GPL-only, unused exported symbols. */
unsigned int num_unused_gpl_syms;
const struct kernel_symbol *unused_gpl_syms;
const s32 *unused_gpl_crcs;
#endif
#ifdef CONFIG_MODULE_SIG #ifdef CONFIG_MODULE_SIG
/* Signature was verified. */ /* Signature was verified. */
bool sig_ok; bool sig_ok;
@ -411,11 +399,6 @@ struct module {
bool async_probe_requested; bool async_probe_requested;
/* symbols that will be GPL-only in the near future. */
const struct kernel_symbol *gpl_future_syms;
const s32 *gpl_future_crcs;
unsigned int num_gpl_future_syms;
/* Exception table */ /* Exception table */
unsigned int num_exentries; unsigned int num_exentries;
struct exception_table_entry *extable; struct exception_table_entry *extable;
@ -550,8 +533,6 @@ static inline unsigned long kallsyms_symbol_value(const Elf_Sym *sym)
} }
#endif #endif
extern struct mutex module_mutex;
/* FIXME: It'd be nice to isolate modules during init, too, so they /* FIXME: It'd be nice to isolate modules during init, too, so they
aren't used before they (may) fail. But presently too much code aren't used before they (may) fail. But presently too much code
(IDE & SCSI) require entry into the module during init.*/ (IDE & SCSI) require entry into the module during init.*/
@ -586,20 +567,9 @@ static inline bool within_module(unsigned long addr, const struct module *mod)
return within_module_init(addr, mod) || within_module_core(addr, mod); return within_module_init(addr, mod) || within_module_core(addr, mod);
} }
/* Search for module by name: must hold module_mutex. */ /* Search for module by name: must be in a RCU-sched critical section. */
struct module *find_module(const char *name); struct module *find_module(const char *name);
struct symsearch {
const struct kernel_symbol *start, *stop;
const s32 *crcs;
enum mod_license {
NOT_GPL_ONLY,
GPL_ONLY,
WILL_BE_GPL_ONLY,
} license;
bool unused;
};
/* Returns 0 and fills in value, defined and namebuf, or -ERANGE if /* Returns 0 and fills in value, defined and namebuf, or -ERANGE if
symnum out of range. */ symnum out of range. */
int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type, int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
@ -608,10 +578,6 @@ int module_get_kallsym(unsigned int symnum, unsigned long *value, char *type,
/* Look for this name: can be of form module:name. */ /* Look for this name: can be of form module:name. */
unsigned long module_kallsyms_lookup_name(const char *name); unsigned long module_kallsyms_lookup_name(const char *name);
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *, unsigned long),
void *data);
extern void __noreturn __module_put_and_exit(struct module *mod, extern void __noreturn __module_put_and_exit(struct module *mod,
long code); long code);
#define module_put_and_exit(code) __module_put_and_exit(THIS_MODULE, code) #define module_put_and_exit(code) __module_put_and_exit(THIS_MODULE, code)
@ -795,14 +761,6 @@ static inline unsigned long module_kallsyms_lookup_name(const char *name)
return 0; return 0;
} }
static inline int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *,
unsigned long),
void *data)
{
return 0;
}
static inline int register_module_notifier(struct notifier_block *nb) static inline int register_module_notifier(struct notifier_block *nb)
{ {
/* no events will happen anyway, so this can always succeed */ /* no events will happen anyway, so this can always succeed */
@ -891,4 +849,8 @@ static inline bool module_sig_ok(struct module *module)
} }
#endif /* CONFIG_MODULE_SIG */ #endif /* CONFIG_MODULE_SIG */
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *, unsigned long),
void *data);
#endif /* _LINUX_MODULE_H */ #endif /* _LINUX_MODULE_H */

View File

@ -2272,25 +2272,8 @@ config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
If unsure, say N. If unsure, say N.
config UNUSED_SYMBOLS
bool "Enable unused/obsolete exported symbols"
default y if X86
help
Unused but exported symbols make the kernel needlessly bigger. For
that reason most of these unused exports will soon be removed. This
option is provided temporarily to provide a transition period in case
some external kernel module needs one of these symbols anyway. If you
encounter such a case in your module, consider if you are actually
using the right API. (rationale: since nobody in the kernel is using
this in a module, there is a pretty good chance it's actually the
wrong interface to use). If you really need the symbol, please send a
mail to the linux kernel mailing list mentioning the symbol and why
you really need it, and what the merge plan to the mainline kernel for
your module is.
config TRIM_UNUSED_KSYMS config TRIM_UNUSED_KSYMS
bool "Trim unused exported kernel symbols" bool "Trim unused exported kernel symbols"
depends on !UNUSED_SYMBOLS
help help
The kernel and some modules make many symbols available for The kernel and some modules make many symbols available for
other modules to use via EXPORT_SYMBOL() and variants. Depending other modules to use via EXPORT_SYMBOL() and variants. Depending

View File

@ -177,6 +177,11 @@ unsigned long kallsyms_lookup_name(const char *name)
return module_kallsyms_lookup_name(name); return module_kallsyms_lookup_name(name);
} }
#ifdef CONFIG_LIVEPATCH
/*
* Iterate over all symbols in vmlinux. For symbols from modules use
* module_kallsyms_on_each_symbol instead.
*/
int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *, int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
unsigned long), unsigned long),
void *data) void *data)
@ -192,8 +197,9 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
if (ret != 0) if (ret != 0)
return ret; return ret;
} }
return module_kallsyms_on_each_symbol(fn, data); return 0;
} }
#endif /* CONFIG_LIVEPATCH */
static unsigned long get_symbol_pos(unsigned long addr, static unsigned long get_symbol_pos(unsigned long addr,
unsigned long *symbolsize, unsigned long *symbolsize,

View File

@ -19,6 +19,7 @@
#include <linux/moduleloader.h> #include <linux/moduleloader.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/memory.h> #include <linux/memory.h>
#include <linux/rcupdate.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include "core.h" #include "core.h"
#include "patch.h" #include "patch.h"
@ -57,7 +58,7 @@ static void klp_find_object_module(struct klp_object *obj)
if (!klp_is_module(obj)) if (!klp_is_module(obj))
return; return;
mutex_lock(&module_mutex); rcu_read_lock_sched();
/* /*
* We do not want to block removal of patched modules and therefore * We do not want to block removal of patched modules and therefore
* we do not take a reference here. The patches are removed by * we do not take a reference here. The patches are removed by
@ -74,7 +75,7 @@ static void klp_find_object_module(struct klp_object *obj)
if (mod && mod->klp_alive) if (mod && mod->klp_alive)
obj->mod = mod; obj->mod = mod;
mutex_unlock(&module_mutex); rcu_read_unlock_sched();
} }
static bool klp_initialized(void) static bool klp_initialized(void)
@ -163,12 +164,10 @@ static int klp_find_object_symbol(const char *objname, const char *name,
.pos = sympos, .pos = sympos,
}; };
mutex_lock(&module_mutex);
if (objname) if (objname)
module_kallsyms_on_each_symbol(klp_find_callback, &args); module_kallsyms_on_each_symbol(klp_find_callback, &args);
else else
kallsyms_on_each_symbol(klp_find_callback, &args); kallsyms_on_each_symbol(klp_find_callback, &args);
mutex_unlock(&module_mutex);
/* /*
* Ensure an address was found. If sympos is 0, ensure symbol is unique; * Ensure an address was found. If sympos is 0, ensure symbol is unique;

View File

@ -87,8 +87,7 @@
* 3) module_addr_min/module_addr_max. * 3) module_addr_min/module_addr_max.
* (delete and add uses RCU list operations). * (delete and add uses RCU list operations).
*/ */
DEFINE_MUTEX(module_mutex); static DEFINE_MUTEX(module_mutex);
EXPORT_SYMBOL_GPL(module_mutex);
static LIST_HEAD(modules); static LIST_HEAD(modules);
/* Work queue for freeing init sections in success case */ /* Work queue for freeing init sections in success case */
@ -256,11 +255,6 @@ static void mod_update_bounds(struct module *mod)
struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */
#endif /* CONFIG_KGDB_KDB */ #endif /* CONFIG_KGDB_KDB */
static void module_assert_mutex(void)
{
lockdep_assert_held(&module_mutex);
}
static void module_assert_mutex_or_preempt(void) static void module_assert_mutex_or_preempt(void)
{ {
#ifdef CONFIG_LOCKDEP #ifdef CONFIG_LOCKDEP
@ -414,19 +408,8 @@ extern const struct kernel_symbol __start___ksymtab[];
extern const struct kernel_symbol __stop___ksymtab[]; extern const struct kernel_symbol __stop___ksymtab[];
extern const struct kernel_symbol __start___ksymtab_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl[];
extern const struct kernel_symbol __stop___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[];
extern const s32 __start___kcrctab_gpl[]; 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 #ifndef CONFIG_MODVERSIONS
#define symversion(base, idx) NULL #define symversion(base, idx) NULL
@ -434,87 +417,14 @@ extern const s32 __start___kcrctab_unused_gpl[];
#define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL) #define symversion(base, idx) ((base != NULL) ? ((base) + (idx)) : NULL)
#endif #endif
static bool each_symbol_in_section(const struct symsearch *arr, struct symsearch {
unsigned int arrsize, const struct kernel_symbol *start, *stop;
struct module *owner, const s32 *crcs;
bool (*fn)(const struct symsearch *syms, enum mod_license {
struct module *owner, NOT_GPL_ONLY,
void *data), GPL_ONLY,
void *data) } license;
{ };
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 { struct find_symbol_arg {
/* Input */ /* Input */
@ -535,28 +445,8 @@ static bool check_exported_symbol(const struct symsearch *syms,
{ {
struct find_symbol_arg *fsa = data; struct find_symbol_arg *fsa = data;
if (!fsa->gplok) { if (!fsa->gplok && syms->license == GPL_ONLY)
if (syms->license == GPL_ONLY) return false;
return false;
if (syms->license == WILL_BE_GPL_ONLY && fsa->warn) {
pr_warn("Symbol %s is being used by a non-GPL module, "
"which will not be allowed in the future\n",
fsa->name);
}
}
#ifdef CONFIG_UNUSED_SYMBOLS
if (syms->unused && fsa->warn) {
pr_warn("Symbol %s is marked as UNUSED, however this module is "
"using it.\n", fsa->name);
pr_warn("This symbol will go away in the future.\n");
pr_warn("Please evaluate if this is the right api to use and "
"if it really is, submit a report to the linux kernel "
"mailing list together with submitting your code for "
"inclusion.\n");
}
#endif
fsa->owner = owner; fsa->owner = owner;
fsa->crc = symversion(syms->crcs, symnum); fsa->crc = symversion(syms->crcs, symnum);
fsa->sym = &syms->start[symnum]; fsa->sym = &syms->start[symnum];
@ -619,31 +509,44 @@ static bool find_exported_symbol_in_section(const struct symsearch *syms,
* Find an exported symbol and return it, along with, (optional) crc and * Find an exported symbol and return it, along with, (optional) crc and
* (optional) module which owns it. Needs preempt disabled or module_mutex. * (optional) module which owns it. Needs preempt disabled or module_mutex.
*/ */
static const struct kernel_symbol *find_symbol(const char *name, static bool find_symbol(struct find_symbol_arg *fsa)
struct module **owner,
const s32 **crc,
enum mod_license *license,
bool gplok,
bool warn)
{ {
struct find_symbol_arg fsa; static const struct symsearch arr[] = {
{ __start___ksymtab, __stop___ksymtab, __start___kcrctab,
NOT_GPL_ONLY },
{ __start___ksymtab_gpl, __stop___ksymtab_gpl,
__start___kcrctab_gpl,
GPL_ONLY },
};
struct module *mod;
unsigned int i;
fsa.name = name; module_assert_mutex_or_preempt();
fsa.gplok = gplok;
fsa.warn = warn;
if (each_symbol_section(find_exported_symbol_in_section, &fsa)) { for (i = 0; i < ARRAY_SIZE(arr); i++)
if (owner) if (find_exported_symbol_in_section(&arr[i], NULL, fsa))
*owner = fsa.owner; return true;
if (crc)
*crc = fsa.crc; list_for_each_entry_rcu(mod, &modules, list,
if (license) lockdep_is_held(&module_mutex)) {
*license = fsa.license; struct symsearch arr[] = {
return fsa.sym; { mod->syms, mod->syms + mod->num_syms, mod->crcs,
NOT_GPL_ONLY },
{ mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms,
mod->gpl_crcs,
GPL_ONLY },
};
if (mod->state == MODULE_STATE_UNFORMED)
continue;
for (i = 0; i < ARRAY_SIZE(arr); i++)
if (find_exported_symbol_in_section(&arr[i], mod, fsa))
return true;
} }
pr_debug("Failed to find symbol %s\n", name); pr_debug("Failed to find symbol %s\n", fsa->name);
return NULL; return false;
} }
/* /*
@ -669,10 +572,8 @@ static struct module *find_module_all(const char *name, size_t len,
struct module *find_module(const char *name) struct module *find_module(const char *name)
{ {
module_assert_mutex();
return find_module_all(name, strlen(name), false); return find_module_all(name, strlen(name), false);
} }
EXPORT_SYMBOL_GPL(find_module);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
@ -1107,12 +1008,15 @@ static inline void print_unload_info(struct seq_file *m, struct module *mod)
void __symbol_put(const char *symbol) void __symbol_put(const char *symbol)
{ {
struct module *owner; struct find_symbol_arg fsa = {
.name = symbol,
.gplok = true,
};
preempt_disable(); preempt_disable();
if (!find_symbol(symbol, &owner, NULL, NULL, true, false)) if (!find_symbol(&fsa))
BUG(); BUG();
module_put(owner); module_put(fsa.owner);
preempt_enable(); preempt_enable();
} }
EXPORT_SYMBOL(__symbol_put); EXPORT_SYMBOL(__symbol_put);
@ -1381,19 +1285,22 @@ bad_version:
static inline int check_modstruct_version(const struct load_info *info, static inline int check_modstruct_version(const struct load_info *info,
struct module *mod) struct module *mod)
{ {
const s32 *crc; struct find_symbol_arg fsa = {
.name = "module_layout",
.gplok = true,
};
/* /*
* Since this should be found in kernel (which can't be removed), no * Since this should be found in kernel (which can't be removed), no
* locking is necessary -- use preempt_disable() to placate lockdep. * locking is necessary -- use preempt_disable() to placate lockdep.
*/ */
preempt_disable(); preempt_disable();
if (!find_symbol("module_layout", NULL, &crc, NULL, true, false)) { if (!find_symbol(&fsa)) {
preempt_enable(); preempt_enable();
BUG(); BUG();
} }
preempt_enable(); preempt_enable();
return check_version(info, "module_layout", mod, crc); return check_version(info, "module_layout", mod, fsa.crc);
} }
/* First part is kernel version, which we ignore if module has crcs. */ /* First part is kernel version, which we ignore if module has crcs. */
@ -1487,10 +1394,11 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
const char *name, const char *name,
char ownername[]) char ownername[])
{ {
struct module *owner; struct find_symbol_arg fsa = {
const struct kernel_symbol *sym; .name = name,
const s32 *crc; .gplok = !(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)),
enum mod_license license; .warn = true,
};
int err; int err;
/* /*
@ -1500,42 +1408,40 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod,
*/ */
sched_annotate_sleep(); sched_annotate_sleep();
mutex_lock(&module_mutex); mutex_lock(&module_mutex);
sym = find_symbol(name, &owner, &crc, &license, if (!find_symbol(&fsa))
!(mod->taints & (1 << TAINT_PROPRIETARY_MODULE)), true);
if (!sym)
goto unlock; goto unlock;
if (license == GPL_ONLY) if (fsa.license == GPL_ONLY)
mod->using_gplonly_symbols = true; mod->using_gplonly_symbols = true;
if (!inherit_taint(mod, owner)) { if (!inherit_taint(mod, fsa.owner)) {
sym = NULL; fsa.sym = NULL;
goto getname; goto getname;
} }
if (!check_version(info, name, mod, crc)) { if (!check_version(info, name, mod, fsa.crc)) {
sym = ERR_PTR(-EINVAL); fsa.sym = ERR_PTR(-EINVAL);
goto getname; goto getname;
} }
err = verify_namespace_is_imported(info, sym, mod); err = verify_namespace_is_imported(info, fsa.sym, mod);
if (err) { if (err) {
sym = ERR_PTR(err); fsa.sym = ERR_PTR(err);
goto getname; goto getname;
} }
err = ref_module(mod, owner); err = ref_module(mod, fsa.owner);
if (err) { if (err) {
sym = ERR_PTR(err); fsa.sym = ERR_PTR(err);
goto getname; goto getname;
} }
getname: getname:
/* We must make copy under the lock if we failed to get ref. */ /* We must make copy under the lock if we failed to get ref. */
strncpy(ownername, module_name(owner), MODULE_NAME_LEN); strncpy(ownername, module_name(fsa.owner), MODULE_NAME_LEN);
unlock: unlock:
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
return sym; return fsa.sym;
} }
static const struct kernel_symbol * static const struct kernel_symbol *
@ -2296,16 +2202,19 @@ static void free_module(struct module *mod)
void *__symbol_get(const char *symbol) void *__symbol_get(const char *symbol)
{ {
struct module *owner; struct find_symbol_arg fsa = {
const struct kernel_symbol *sym; .name = symbol,
.gplok = true,
.warn = true,
};
preempt_disable(); preempt_disable();
sym = find_symbol(symbol, &owner, NULL, NULL, true, true); if (!find_symbol(&fsa) || strong_try_module_get(fsa.owner)) {
if (sym && strong_try_module_get(owner)) preempt_enable();
sym = NULL; return NULL;
}
preempt_enable(); preempt_enable();
return (void *)kernel_symbol_value(fsa.sym);
return sym ? (void *)kernel_symbol_value(sym) : NULL;
} }
EXPORT_SYMBOL_GPL(__symbol_get); EXPORT_SYMBOL_GPL(__symbol_get);
@ -2318,7 +2227,6 @@ EXPORT_SYMBOL_GPL(__symbol_get);
static int verify_exported_symbols(struct module *mod) static int verify_exported_symbols(struct module *mod)
{ {
unsigned int i; unsigned int i;
struct module *owner;
const struct kernel_symbol *s; const struct kernel_symbol *s;
struct { struct {
const struct kernel_symbol *sym; const struct kernel_symbol *sym;
@ -2326,21 +2234,19 @@ static int verify_exported_symbols(struct module *mod)
} arr[] = { } arr[] = {
{ mod->syms, mod->num_syms }, { mod->syms, mod->num_syms },
{ mod->gpl_syms, mod->num_gpl_syms }, { mod->gpl_syms, mod->num_gpl_syms },
{ mod->gpl_future_syms, mod->num_gpl_future_syms },
#ifdef CONFIG_UNUSED_SYMBOLS
{ mod->unused_syms, mod->num_unused_syms },
{ mod->unused_gpl_syms, mod->num_unused_gpl_syms },
#endif
}; };
for (i = 0; i < ARRAY_SIZE(arr); i++) { for (i = 0; i < ARRAY_SIZE(arr); i++) {
for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
if (find_symbol(kernel_symbol_name(s), &owner, NULL, struct find_symbol_arg fsa = {
NULL, true, false)) { .name = kernel_symbol_name(s),
.gplok = true,
};
if (find_symbol(&fsa)) {
pr_err("%s: exports duplicate symbol %s" pr_err("%s: exports duplicate symbol %s"
" (owned by %s)\n", " (owned by %s)\n",
mod->name, kernel_symbol_name(s), mod->name, kernel_symbol_name(s),
module_name(owner)); module_name(fsa.owner));
return -ENOEXEC; return -ENOEXEC;
} }
} }
@ -2348,6 +2254,21 @@ static int verify_exported_symbols(struct module *mod)
return 0; return 0;
} }
static bool ignore_undef_symbol(Elf_Half emachine, const char *name)
{
/*
* On x86, PIC code and Clang non-PIC code may have call foo@PLT. GNU as
* before 2.37 produces an unreferenced _GLOBAL_OFFSET_TABLE_ on x86-64.
* i386 has a similar problem but may not deserve a fix.
*
* If we ever have to ignore many symbols, consider refactoring the code to
* only warn if referenced by a relocation.
*/
if (emachine == EM_386 || emachine == EM_X86_64)
return !strcmp(name, "_GLOBAL_OFFSET_TABLE_");
return false;
}
/* Change all symbols so that st_value encodes the pointer directly. */ /* Change all symbols so that st_value encodes the pointer directly. */
static int simplify_symbols(struct module *mod, const struct load_info *info) static int simplify_symbols(struct module *mod, const struct load_info *info)
{ {
@ -2395,8 +2316,10 @@ static int simplify_symbols(struct module *mod, const struct load_info *info)
break; break;
} }
/* Ok if weak. */ /* Ok if weak or ignored. */
if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) if (!ksym &&
(ELF_ST_BIND(sym[i].st_info) == STB_WEAK ||
ignore_undef_symbol(info->hdr->e_machine, name)))
break; break;
ret = PTR_ERR(ksym) ?: -ENOENT; ret = PTR_ERR(ksym) ?: -ENOENT;
@ -2964,7 +2887,7 @@ static int module_sig_check(struct load_info *info, int flags)
} }
if (is_module_sig_enforced()) { if (is_module_sig_enforced()) {
pr_notice("%s: loading of %s is rejected\n", info->name, reason); pr_notice("Loading of %s is rejected\n", reason);
return -EKEYREJECTED; return -EKEYREJECTED;
} }
@ -2977,9 +2900,33 @@ static int module_sig_check(struct load_info *info, int flags)
} }
#endif /* !CONFIG_MODULE_SIG */ #endif /* !CONFIG_MODULE_SIG */
/* Sanity checks against invalid binaries, wrong arch, weird elf version. */ static int validate_section_offset(struct load_info *info, Elf_Shdr *shdr)
static int elf_header_check(struct load_info *info)
{ {
unsigned long secend;
/*
* Check for both overflow and offset/size being
* too large.
*/
secend = shdr->sh_offset + shdr->sh_size;
if (secend < shdr->sh_offset || secend > info->len)
return -ENOEXEC;
return 0;
}
/*
* Sanity checks against invalid binaries, wrong arch, weird elf version.
*
* Also do basic validity checks against section offsets and sizes, the
* section name string table, and the indices used for it (sh_name).
*/
static int elf_validity_check(struct load_info *info)
{
unsigned int i;
Elf_Shdr *shdr, *strhdr;
int err;
if (info->len < sizeof(*(info->hdr))) if (info->len < sizeof(*(info->hdr)))
return -ENOEXEC; return -ENOEXEC;
@ -2989,11 +2936,78 @@ static int elf_header_check(struct load_info *info)
|| info->hdr->e_shentsize != sizeof(Elf_Shdr)) || info->hdr->e_shentsize != sizeof(Elf_Shdr))
return -ENOEXEC; return -ENOEXEC;
/*
* e_shnum is 16 bits, and sizeof(Elf_Shdr) is
* known and small. So e_shnum * sizeof(Elf_Shdr)
* will not overflow unsigned long on any platform.
*/
if (info->hdr->e_shoff >= info->len if (info->hdr->e_shoff >= info->len
|| (info->hdr->e_shnum * sizeof(Elf_Shdr) > || (info->hdr->e_shnum * sizeof(Elf_Shdr) >
info->len - info->hdr->e_shoff)) info->len - info->hdr->e_shoff))
return -ENOEXEC; return -ENOEXEC;
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
/*
* Verify if the section name table index is valid.
*/
if (info->hdr->e_shstrndx == SHN_UNDEF
|| info->hdr->e_shstrndx >= info->hdr->e_shnum)
return -ENOEXEC;
strhdr = &info->sechdrs[info->hdr->e_shstrndx];
err = validate_section_offset(info, strhdr);
if (err < 0)
return err;
/*
* The section name table must be NUL-terminated, as required
* by the spec. This makes strcmp and pr_* calls that access
* strings in the section safe.
*/
info->secstrings = (void *)info->hdr + strhdr->sh_offset;
if (info->secstrings[strhdr->sh_size - 1] != '\0')
return -ENOEXEC;
/*
* The code assumes that section 0 has a length of zero and
* an addr of zero, so check for it.
*/
if (info->sechdrs[0].sh_type != SHT_NULL
|| info->sechdrs[0].sh_size != 0
|| info->sechdrs[0].sh_addr != 0)
return -ENOEXEC;
for (i = 1; i < info->hdr->e_shnum; i++) {
shdr = &info->sechdrs[i];
switch (shdr->sh_type) {
case SHT_NULL:
case SHT_NOBITS:
continue;
case SHT_SYMTAB:
if (shdr->sh_link == SHN_UNDEF
|| shdr->sh_link >= info->hdr->e_shnum)
return -ENOEXEC;
fallthrough;
default:
err = validate_section_offset(info, shdr);
if (err < 0) {
pr_err("Invalid ELF section in module (section %u type %u)\n",
i, shdr->sh_type);
return err;
}
if (shdr->sh_flags & SHF_ALLOC) {
if (shdr->sh_name >= strhdr->sh_size) {
pr_err("Invalid ELF section name in module (section %u type %u)\n",
i, shdr->sh_type);
return -ENOEXEC;
}
}
break;
}
}
return 0; return 0;
} }
@ -3095,11 +3109,6 @@ static int rewrite_section_headers(struct load_info *info, int flags)
for (i = 1; i < info->hdr->e_shnum; i++) { for (i = 1; i < info->hdr->e_shnum; i++) {
Elf_Shdr *shdr = &info->sechdrs[i]; Elf_Shdr *shdr = &info->sechdrs[i];
if (shdr->sh_type != SHT_NOBITS
&& info->len < shdr->sh_offset + shdr->sh_size) {
pr_err("Module len %lu truncated\n", info->len);
return -ENOEXEC;
}
/* /*
* Mark all sections sh_addr with their address in the * Mark all sections sh_addr with their address in the
@ -3133,11 +3142,6 @@ static int setup_load_info(struct load_info *info, int flags)
{ {
unsigned int i; unsigned int i;
/* Set up the convenience variables */
info->sechdrs = (void *)info->hdr + info->hdr->e_shoff;
info->secstrings = (void *)info->hdr
+ info->sechdrs[info->hdr->e_shstrndx].sh_offset;
/* Try to find a name early so we can log errors with a module name */ /* Try to find a name early so we can log errors with a module name */
info->index.info = find_sec(info, ".modinfo"); info->index.info = find_sec(info, ".modinfo");
if (info->index.info) if (info->index.info)
@ -3241,22 +3245,7 @@ static int find_module_sections(struct module *mod, struct load_info *info)
sizeof(*mod->gpl_syms), sizeof(*mod->gpl_syms),
&mod->num_gpl_syms); &mod->num_gpl_syms);
mod->gpl_crcs = section_addr(info, "__kcrctab_gpl"); mod->gpl_crcs = section_addr(info, "__kcrctab_gpl");
mod->gpl_future_syms = section_objs(info,
"__ksymtab_gpl_future",
sizeof(*mod->gpl_future_syms),
&mod->num_gpl_future_syms);
mod->gpl_future_crcs = section_addr(info, "__kcrctab_gpl_future");
#ifdef CONFIG_UNUSED_SYMBOLS
mod->unused_syms = section_objs(info, "__ksymtab_unused",
sizeof(*mod->unused_syms),
&mod->num_unused_syms);
mod->unused_crcs = section_addr(info, "__kcrctab_unused");
mod->unused_gpl_syms = section_objs(info, "__ksymtab_unused_gpl",
sizeof(*mod->unused_gpl_syms),
&mod->num_unused_gpl_syms);
mod->unused_gpl_crcs = section_addr(info, "__kcrctab_unused_gpl");
#endif
#ifdef CONFIG_CONSTRUCTORS #ifdef CONFIG_CONSTRUCTORS
mod->ctors = section_objs(info, ".ctors", mod->ctors = section_objs(info, ".ctors",
sizeof(*mod->ctors), &mod->num_ctors); sizeof(*mod->ctors), &mod->num_ctors);
@ -3437,14 +3426,8 @@ static int check_module_license_and_versions(struct module *mod)
pr_warn("%s: module license taints kernel.\n", mod->name); pr_warn("%s: module license taints kernel.\n", mod->name);
#ifdef CONFIG_MODVERSIONS #ifdef CONFIG_MODVERSIONS
if ((mod->num_syms && !mod->crcs) if ((mod->num_syms && !mod->crcs) ||
|| (mod->num_gpl_syms && !mod->gpl_crcs) (mod->num_gpl_syms && !mod->gpl_crcs)) {
|| (mod->num_gpl_future_syms && !mod->gpl_future_crcs)
#ifdef CONFIG_UNUSED_SYMBOLS
|| (mod->num_unused_syms && !mod->unused_crcs)
|| (mod->num_unused_gpl_syms && !mod->unused_gpl_crcs)
#endif
) {
return try_to_force_load(mod, return try_to_force_load(mod,
"no versions for exported symbols"); "no versions for exported symbols");
} }
@ -3894,26 +3877,50 @@ static int load_module(struct load_info *info, const char __user *uargs,
long err = 0; long err = 0;
char *after_dashes; char *after_dashes;
err = elf_header_check(info); /*
* Do the signature check (if any) first. All that
* the signature check needs is info->len, it does
* not need any of the section info. That can be
* set up later. This will minimize the chances
* of a corrupt module causing problems before
* we even get to the signature check.
*
* The check will also adjust info->len by stripping
* off the sig length at the end of the module, making
* checks against info->len more correct.
*/
err = module_sig_check(info, flags);
if (err)
goto free_copy;
/*
* Do basic sanity checks against the ELF header and
* sections.
*/
err = elf_validity_check(info);
if (err) { if (err) {
pr_err("Module has invalid ELF header\n"); pr_err("Module has invalid ELF structures\n");
goto free_copy; goto free_copy;
} }
/*
* Everything checks out, so set up the section info
* in the info structure.
*/
err = setup_load_info(info, flags); err = setup_load_info(info, flags);
if (err) if (err)
goto free_copy; goto free_copy;
/*
* Now that we know we have the correct module name, check
* if it's blacklisted.
*/
if (blacklisted(info->name)) { if (blacklisted(info->name)) {
err = -EPERM; err = -EPERM;
pr_err("Module %s is blacklisted\n", info->name); pr_err("Module %s is blacklisted\n", info->name);
goto free_copy; goto free_copy;
} }
err = module_sig_check(info, flags);
if (err)
goto free_copy;
err = rewrite_section_headers(info, flags); err = rewrite_section_headers(info, flags);
if (err) if (err)
goto free_copy; goto free_copy;
@ -4374,16 +4381,16 @@ unsigned long module_kallsyms_lookup_name(const char *name)
return ret; return ret;
} }
#ifdef CONFIG_LIVEPATCH
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *, int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
struct module *, unsigned long), struct module *, unsigned long),
void *data) void *data)
{ {
struct module *mod; struct module *mod;
unsigned int i; unsigned int i;
int ret; int ret = 0;
module_assert_mutex();
mutex_lock(&module_mutex);
list_for_each_entry(mod, &modules, list) { list_for_each_entry(mod, &modules, list) {
/* We hold module_mutex: no need for rcu_dereference_sched */ /* We hold module_mutex: no need for rcu_dereference_sched */
struct mod_kallsyms *kallsyms = mod->kallsyms; struct mod_kallsyms *kallsyms = mod->kallsyms;
@ -4399,11 +4406,13 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
ret = fn(data, kallsyms_symbol_name(kallsyms, i), ret = fn(data, kallsyms_symbol_name(kallsyms, i),
mod, kallsyms_symbol_value(sym)); mod, kallsyms_symbol_value(sym));
if (ret != 0) if (ret != 0)
return ret; break;
} }
} }
return 0; mutex_unlock(&module_mutex);
return ret;
} }
#endif /* CONFIG_LIVEPATCH */
#endif /* CONFIG_KALLSYMS */ #endif /* CONFIG_KALLSYMS */
/* Maximum number of characters written by module_flags() */ /* Maximum number of characters written by module_flags() */

View File

@ -25,7 +25,7 @@ int mod_check_sig(const struct module_signature *ms, size_t file_len,
return -EBADMSG; return -EBADMSG;
if (ms->id_type != PKEY_ID_PKCS7) { if (ms->id_type != PKEY_ID_PKCS7) {
pr_err("%s: Module is not signed with expected PKCS#7 message\n", pr_err("%s: not signed with expected PKCS#7 message\n",
name); name);
return -ENOPKG; return -ENOPKG;
} }

View File

@ -30,7 +30,7 @@ int mod_verify_sig(const void *mod, struct load_info *info)
memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms)); memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
ret = mod_check_sig(&ms, modlen, info->name); ret = mod_check_sig(&ms, modlen, "module");
if (ret) if (ret)
return ret; return ret;

View File

@ -124,9 +124,9 @@ static nokprobe_inline bool trace_kprobe_module_exist(struct trace_kprobe *tk)
if (!p) if (!p)
return true; return true;
*p = '\0'; *p = '\0';
mutex_lock(&module_mutex); rcu_read_lock_sched();
ret = !!find_module(tk->symbol); ret = !!find_module(tk->symbol);
mutex_unlock(&module_mutex); rcu_read_unlock_sched();
*p = ':'; *p = ':';
return ret; return ret;

View File

@ -91,8 +91,6 @@ void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
char *secstrings; char *secstrings;
unsigned int i; unsigned int i;
lockdep_assert_held(&module_mutex);
mod->bug_table = NULL; mod->bug_table = NULL;
mod->num_bugs = 0; mod->num_bugs = 0;
@ -118,7 +116,6 @@ void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
void module_bug_cleanup(struct module *mod) void module_bug_cleanup(struct module *mod)
{ {
lockdep_assert_held(&module_mutex);
list_del_rcu(&mod->bug_list); list_del_rcu(&mod->bug_list);
} }

View File

@ -4283,8 +4283,7 @@ sub process {
if (defined $realline_next && if (defined $realline_next &&
exists $lines[$realline_next - 1] && exists $lines[$realline_next - 1] &&
!defined $suppress_export{$realline_next} && !defined $suppress_export{$realline_next} &&
($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
$lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
# Handle definitions which produce identifiers with # Handle definitions which produce identifiers with
# a prefix: # a prefix:
# XXX(foo); # XXX(foo);
@ -4311,8 +4310,7 @@ sub process {
} }
if (!defined $suppress_export{$linenr} && if (!defined $suppress_export{$linenr} &&
$prevline =~ /^.\s*$/ && $prevline =~ /^.\s*$/ &&
($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || ($line =~ /EXPORT_SYMBOL.*\((.*)\)/)) {
$line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
#print "FOO B <$lines[$linenr - 1]>\n"; #print "FOO B <$lines[$linenr - 1]>\n";
$suppress_export{$linenr} = 2; $suppress_export{$linenr} = 2;
} }

View File

@ -42,8 +42,9 @@ static int allow_missing_ns_imports;
static bool error_occurred; static bool error_occurred;
enum export { enum export {
export_plain, export_unused, export_gpl, export_plain,
export_unused_gpl, export_gpl_future, export_unknown export_gpl,
export_unknown
}; };
/* In kernel, this size is defined in linux/module.h; /* In kernel, this size is defined in linux/module.h;
@ -292,10 +293,7 @@ static const struct {
enum export export; enum export export;
} export_list[] = { } export_list[] = {
{ .str = "EXPORT_SYMBOL", .export = export_plain }, { .str = "EXPORT_SYMBOL", .export = export_plain },
{ .str = "EXPORT_UNUSED_SYMBOL", .export = export_unused },
{ .str = "EXPORT_SYMBOL_GPL", .export = export_gpl }, { .str = "EXPORT_SYMBOL_GPL", .export = export_gpl },
{ .str = "EXPORT_UNUSED_SYMBOL_GPL", .export = export_unused_gpl },
{ .str = "EXPORT_SYMBOL_GPL_FUTURE", .export = export_gpl_future },
{ .str = "(unknown)", .export = export_unknown }, { .str = "(unknown)", .export = export_unknown },
}; };
@ -354,14 +352,8 @@ static enum export export_from_secname(struct elf_info *elf, unsigned int sec)
if (strstarts(secname, "___ksymtab+")) if (strstarts(secname, "___ksymtab+"))
return export_plain; return export_plain;
else if (strstarts(secname, "___ksymtab_unused+"))
return export_unused;
else if (strstarts(secname, "___ksymtab_gpl+")) else if (strstarts(secname, "___ksymtab_gpl+"))
return export_gpl; return export_gpl;
else if (strstarts(secname, "___ksymtab_unused_gpl+"))
return export_unused_gpl;
else if (strstarts(secname, "___ksymtab_gpl_future+"))
return export_gpl_future;
else else
return export_unknown; return export_unknown;
} }
@ -370,14 +362,8 @@ static enum export export_from_sec(struct elf_info *elf, unsigned int sec)
{ {
if (sec == elf->export_sec) if (sec == elf->export_sec)
return export_plain; return export_plain;
else if (sec == elf->export_unused_sec)
return export_unused;
else if (sec == elf->export_gpl_sec) else if (sec == elf->export_gpl_sec)
return export_gpl; return export_gpl;
else if (sec == elf->export_unused_gpl_sec)
return export_unused_gpl;
else if (sec == elf->export_gpl_future_sec)
return export_gpl_future;
else else
return export_unknown; return export_unknown;
} }
@ -581,14 +567,8 @@ static int parse_elf(struct elf_info *info, const char *filename)
info->modinfo_len = sechdrs[i].sh_size; info->modinfo_len = sechdrs[i].sh_size;
} else if (strcmp(secname, "__ksymtab") == 0) } else if (strcmp(secname, "__ksymtab") == 0)
info->export_sec = i; info->export_sec = i;
else if (strcmp(secname, "__ksymtab_unused") == 0)
info->export_unused_sec = i;
else if (strcmp(secname, "__ksymtab_gpl") == 0) else if (strcmp(secname, "__ksymtab_gpl") == 0)
info->export_gpl_sec = i; info->export_gpl_sec = i;
else if (strcmp(secname, "__ksymtab_unused_gpl") == 0)
info->export_unused_gpl_sec = i;
else if (strcmp(secname, "__ksymtab_gpl_future") == 0)
info->export_gpl_future_sec = i;
if (sechdrs[i].sh_type == SHT_SYMTAB) { if (sechdrs[i].sh_type == SHT_SYMTAB) {
unsigned int sh_link_idx; unsigned int sh_link_idx;
@ -2146,36 +2126,13 @@ static void check_for_gpl_usage(enum export exp, const char *m, const char *s)
error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n", error("GPL-incompatible module %s.ko uses GPL-only symbol '%s'\n",
m, s); m, s);
break; break;
case export_unused_gpl:
error("GPL-incompatible module %s.ko uses GPL-only symbol marked UNUSED '%s'\n",
m, s);
break;
case export_gpl_future:
warn("GPL-incompatible module %s.ko uses future GPL-only symbol '%s'\n",
m, s);
break;
case export_plain: case export_plain:
case export_unused:
case export_unknown: case export_unknown:
/* ignore */ /* ignore */
break; break;
} }
} }
static void check_for_unused(enum export exp, const char *m, const char *s)
{
switch (exp) {
case export_unused:
case export_unused_gpl:
warn("module %s.ko uses symbol '%s' marked UNUSED\n",
m, s);
break;
default:
/* ignore */
break;
}
}
static void check_exports(struct module *mod) static void check_exports(struct module *mod)
{ {
struct symbol *s, *exp; struct symbol *s, *exp;
@ -2206,7 +2163,6 @@ static void check_exports(struct module *mod)
if (!mod->gpl_compatible) if (!mod->gpl_compatible)
check_for_gpl_usage(exp->export, basename, exp->name); check_for_gpl_usage(exp->export, basename, exp->name);
check_for_unused(exp->export, basename, exp->name);
} }
} }

View File

@ -140,10 +140,7 @@ struct elf_info {
Elf_Sym *symtab_start; Elf_Sym *symtab_start;
Elf_Sym *symtab_stop; Elf_Sym *symtab_stop;
Elf_Section export_sec; Elf_Section export_sec;
Elf_Section export_unused_sec;
Elf_Section export_gpl_sec; Elf_Section export_gpl_sec;
Elf_Section export_unused_gpl_sec;
Elf_Section export_gpl_future_sec;
char *strtab; char *strtab;
char *modinfo; char *modinfo;
unsigned int modinfo_len; unsigned int modinfo_len;

View File

@ -11,14 +11,8 @@ SECTIONS {
__ksymtab 0 : { *(SORT(___ksymtab+*)) } __ksymtab 0 : { *(SORT(___ksymtab+*)) }
__ksymtab_gpl 0 : { *(SORT(___ksymtab_gpl+*)) } __ksymtab_gpl 0 : { *(SORT(___ksymtab_gpl+*)) }
__ksymtab_unused 0 : { *(SORT(___ksymtab_unused+*)) }
__ksymtab_unused_gpl 0 : { *(SORT(___ksymtab_unused_gpl+*)) }
__ksymtab_gpl_future 0 : { *(SORT(___ksymtab_gpl_future+*)) }
__kcrctab 0 : { *(SORT(___kcrctab+*)) } __kcrctab 0 : { *(SORT(___kcrctab+*)) }
__kcrctab_gpl 0 : { *(SORT(___kcrctab_gpl+*)) } __kcrctab_gpl 0 : { *(SORT(___kcrctab_gpl+*)) }
__kcrctab_unused 0 : { *(SORT(___kcrctab_unused+*)) }
__kcrctab_unused_gpl 0 : { *(SORT(___kcrctab_unused_gpl+*)) }
__kcrctab_gpl_future 0 : { *(SORT(___kcrctab_gpl_future+*)) }
.init_array 0 : ALIGN(8) { *(SORT(.init_array.*)) *(.init_array) } .init_array 0 : ALIGN(8) { *(SORT(.init_array.*)) *(.init_array) }

View File

@ -3,8 +3,5 @@
#define EXPORT_SYMBOL(sym) #define EXPORT_SYMBOL(sym)
#define EXPORT_SYMBOL_GPL(sym) #define EXPORT_SYMBOL_GPL(sym)
#define EXPORT_SYMBOL_GPL_FUTURE(sym)
#define EXPORT_UNUSED_SYMBOL(sym)
#define EXPORT_UNUSED_SYMBOL_GPL(sym)
#endif #endif