From ed13fc33f763035a7c290f11c7111877c3a5daab Mon Sep 17 00:00:00 2001 From: Matthias Maennich Date: Fri, 6 Sep 2019 11:32:26 +0100 Subject: export: explicitly align struct kernel_symbol This change allows growing struct kernel_symbol without wasting bytes to alignment. It also concretized the alignment of ksymtab entries if relative references are used for ksymtab entries. struct kernel_symbol was already implicitly being aligned to the word size, except on x86_64 and m68k, where it is aligned to 16 and 2 bytes, respectively. As far as I can tell there is no requirement for aligning struct kernel_symbol to 16 bytes on x86_64, but gcc aligns structs to their size, and the linker aligns the custom __ksymtab sections to the largest data type contained within, so setting KSYM_ALIGN to 16 was necessary to stay consistent with the code generated for non-ASM EXPORT_SYMBOL(). Now that non-ASM EXPORT_SYMBOL() explicitly aligns to word size (8), KSYM_ALIGN is no longer necessary. In case of relative references, the alignment has been changed accordingly to not waste space when adding new struct members. As for m68k, struct kernel_symbol is aligned to 2 bytes even though the structure itself is 8 bytes; using a 4-byte alignment shouldn't hurt. I manually verified the output of the __ksymtab sections didn't change on x86, x86_64, arm, arm64 and m68k. As expected, the section contents didn't change, and the ELF section alignment only changed on x86_64 and m68k. Feedback from other archs more than welcome. Co-developed-by: Martijn Coenen Signed-off-by: Martijn Coenen Reviewed-by: Greg Kroah-Hartman Signed-off-by: Matthias Maennich Signed-off-by: Jessica Yu --- include/linux/export.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux/export.h') diff --git a/include/linux/export.h b/include/linux/export.h index fd8711ed9ac4..28a4d2150689 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -52,7 +52,7 @@ extern struct module __this_module; #define __KSYMTAB_ENTRY(sym, sec) \ __ADDRESSABLE(sym) \ asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \ - " .balign 8 \n" \ + " .balign 4 \n" \ "__ksymtab_" #sym ": \n" \ " .long " #sym "- . \n" \ " .long __kstrtab_" #sym "- . \n" \ @@ -66,6 +66,7 @@ struct kernel_symbol { #define __KSYMTAB_ENTRY(sym, sec) \ static const struct kernel_symbol __ksymtab_##sym \ __attribute__((section("___ksymtab" sec "+" #sym), used)) \ + __aligned(sizeof(void *)) \ = { (unsigned long)&sym, __kstrtab_##sym } struct kernel_symbol { -- cgit v1.2.3 From 8651ec01daedad26290f76beeb4736f9d2da4b87 Mon Sep 17 00:00:00 2001 From: Matthias Maennich Date: Fri, 6 Sep 2019 11:32:27 +0100 Subject: module: add support for symbol namespaces. The EXPORT_SYMBOL_NS() and EXPORT_SYMBOL_NS_GPL() macros can be used to export a symbol to a specific namespace. There are no _GPL_FUTURE and _UNUSED variants because these are currently unused, and I'm not sure they are necessary. I didn't add EXPORT_SYMBOL_NS() for ASM exports; this patch sets the namespace of ASM exports to NULL by default. In case of relative references, it will be relocatable to NULL. If there's a need, this should be pretty easy to add. A module that wants to use a symbol exported to a namespace must add a MODULE_IMPORT_NS() statement to their module code; otherwise, modpost will complain when building the module, and the kernel module loader will emit an error and fail when loading the module. MODULE_IMPORT_NS() adds a modinfo tag 'import_ns' to the module. That tag can be observed by the modinfo command, modpost and kernel/module.c at the time of loading the module. The ELF symbols are renamed to include the namespace with an asm label; for example, symbol 'usb_stor_suspend' in namespace USB_STORAGE becomes 'usb_stor_suspend.USB_STORAGE'. This allows modpost to do namespace checking, without having to go through all the effort of parsing ELF and relocation records just to get to the struct kernel_symbols. On x86_64 I saw no difference in binary size (compression), but at runtime this will require a word of memory per export to hold the namespace. An alternative could be to store namespaced symbols in their own section and use a separate 'struct namespaced_kernel_symbol' for that section, at the cost of making the module loader more complex. Co-developed-by: Martijn Coenen Signed-off-by: Martijn Coenen Reviewed-by: Greg Kroah-Hartman Signed-off-by: Matthias Maennich Signed-off-by: Jessica Yu --- include/asm-generic/export.h | 6 +-- include/linux/export.h | 91 ++++++++++++++++++++++++++++++++++++-------- include/linux/module.h | 2 + kernel/module.c | 43 +++++++++++++++++++++ 4 files changed, 123 insertions(+), 19 deletions(-) (limited to 'include/linux/export.h') diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h index 63f54907317b..e2b5d0f569d3 100644 --- a/include/asm-generic/export.h +++ b/include/asm-generic/export.h @@ -17,11 +17,11 @@ .macro __put, val, name #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS - .long \val - ., \name - . + .long \val - ., \name - ., 0 - . #elif defined(CONFIG_64BIT) - .quad \val, \name + .quad \val, \name, 0 #else - .long \val, \name + .long \val, \name, 0 #endif .endm diff --git a/include/linux/export.h b/include/linux/export.h index 28a4d2150689..d59461e71478 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -20,6 +20,8 @@ extern struct module __this_module; #ifdef CONFIG_MODULES +#define NS_SEPARATOR "." + #if defined(__KERNEL__) && !defined(__GENKSYMS__) #ifdef CONFIG_MODVERSIONS /* Mark the CRC weak since genksyms apparently decides not to @@ -29,13 +31,13 @@ extern struct module __this_module; asm(" .section \"___kcrctab" sec "+" #sym "\", \"a\" \n" \ " .weak __crc_" #sym " \n" \ " .long __crc_" #sym " - . \n" \ - " .previous \n"); + " .previous \n") #else #define __CRC_SYMBOL(sym, sec) \ asm(" .section \"___kcrctab" sec "+" #sym "\", \"a\" \n" \ " .weak __crc_" #sym " \n" \ " .long __crc_" #sym " \n" \ - " .previous \n"); + " .previous \n") #endif #else #define __CRC_SYMBOL(sym, sec) @@ -49,6 +51,16 @@ extern struct module __this_module; * absolute relocations that require runtime processing on relocatable * kernels. */ +#define __KSYMTAB_ENTRY_NS(sym, sec, ns) \ + __ADDRESSABLE(sym) \ + asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \ + " .balign 4 \n" \ + "__ksymtab_" #sym NS_SEPARATOR #ns ": \n" \ + " .long " #sym "- . \n" \ + " .long __kstrtab_" #sym "- . \n" \ + " .long __kstrtab_ns_" #sym "- . \n" \ + " .previous \n") + #define __KSYMTAB_ENTRY(sym, sec) \ __ADDRESSABLE(sym) \ asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \ @@ -56,32 +68,53 @@ extern struct module __this_module; "__ksymtab_" #sym ": \n" \ " .long " #sym "- . \n" \ " .long __kstrtab_" #sym "- . \n" \ + " .long 0 - . \n" \ " .previous \n") struct kernel_symbol { int value_offset; int name_offset; + int namespace_offset; }; #else +#define __KSYMTAB_ENTRY_NS(sym, sec, ns) \ + static const struct kernel_symbol __ksymtab_##sym##__##ns \ + asm("__ksymtab_" #sym NS_SEPARATOR #ns) \ + __attribute__((section("___ksymtab" sec "+" #sym), used)) \ + __aligned(sizeof(void *)) \ + = { (unsigned long)&sym, __kstrtab_##sym, __kstrtab_ns_##sym } + #define __KSYMTAB_ENTRY(sym, sec) \ static const struct kernel_symbol __ksymtab_##sym \ + asm("__ksymtab_" #sym) \ __attribute__((section("___ksymtab" sec "+" #sym), used)) \ __aligned(sizeof(void *)) \ - = { (unsigned long)&sym, __kstrtab_##sym } + = { (unsigned long)&sym, __kstrtab_##sym, NULL } struct kernel_symbol { unsigned long value; const char *name; + const char *namespace; }; #endif -/* For every exported symbol, place a struct in the __ksymtab section */ -#define ___EXPORT_SYMBOL(sym, sec) \ +#define ___export_symbol_common(sym, sec) \ extern typeof(sym) sym; \ - __CRC_SYMBOL(sym, sec) \ + __CRC_SYMBOL(sym, sec); \ static const char __kstrtab_##sym[] \ __attribute__((section("__ksymtab_strings"), used, aligned(1))) \ - = #sym; \ + = #sym \ + +/* For every exported symbol, place a struct in the __ksymtab section */ +#define ___EXPORT_SYMBOL_NS(sym, sec, ns) \ + ___export_symbol_common(sym, sec); \ + static const char __kstrtab_ns_##sym[] \ + __attribute__((section("__ksymtab_strings"), used, aligned(1))) \ + = #ns; \ + __KSYMTAB_ENTRY_NS(sym, sec, ns) + +#define ___EXPORT_SYMBOL(sym, sec) \ + ___export_symbol_common(sym, sec); \ __KSYMTAB_ENTRY(sym, sec) #if defined(__DISABLE_EXPORTS) @@ -91,6 +124,7 @@ struct kernel_symbol { * be reused in other execution contexts such as the UEFI stub or the * decompressor. */ +#define __EXPORT_SYMBOL_NS(sym, sec, ns) #define __EXPORT_SYMBOL(sym, sec) #elif defined(CONFIG_TRIM_UNUSED_KSYMS) @@ -117,18 +151,26 @@ struct kernel_symbol { #define __cond_export_sym_1(sym, sec) ___EXPORT_SYMBOL(sym, sec) #define __cond_export_sym_0(sym, sec) /* nothing */ +#define __EXPORT_SYMBOL_NS(sym, sec, ns) \ + __ksym_marker(sym); \ + __cond_export_ns_sym(sym, sec, ns, __is_defined(__KSYM_##sym)) +#define __cond_export_ns_sym(sym, sec, ns, conf) \ + ___cond_export_ns_sym(sym, sec, ns, conf) +#define ___cond_export_ns_sym(sym, sec, ns, enabled) \ + __cond_export_ns_sym_##enabled(sym, sec, ns) +#define __cond_export_ns_sym_1(sym, sec, ns) ___EXPORT_SYMBOL_NS(sym, sec, ns) +#define __cond_export_ns_sym_0(sym, sec, ns) /* nothing */ + #else +#define __EXPORT_SYMBOL_NS ___EXPORT_SYMBOL_NS #define __EXPORT_SYMBOL ___EXPORT_SYMBOL #endif -#define EXPORT_SYMBOL(sym) \ - __EXPORT_SYMBOL(sym, "") - -#define EXPORT_SYMBOL_GPL(sym) \ - __EXPORT_SYMBOL(sym, "_gpl") - -#define EXPORT_SYMBOL_GPL_FUTURE(sym) \ - __EXPORT_SYMBOL(sym, "_gpl_future") +#define EXPORT_SYMBOL(sym) __EXPORT_SYMBOL(sym, "") +#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_NS(sym, "", ns) +#define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL_NS(sym, "_gpl", ns) #ifdef CONFIG_UNUSED_SYMBOLS #define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused") @@ -138,11 +180,28 @@ struct kernel_symbol { #define EXPORT_UNUSED_SYMBOL_GPL(sym) #endif -#endif /* __GENKSYMS__ */ +#endif /* __KERNEL__ && !__GENKSYMS__ */ + +#if defined(__GENKSYMS__) +/* + * When we're running genksyms, ignore the namespace and make the _NS + * variants look like the normal ones. There are two reasons for this: + * 1) In the normal definition of EXPORT_SYMBOL_NS, the 'ns' macro + * argument is itself not expanded because it's always tokenized or + * concatenated; but when running genksyms, a blank definition of the + * macro does allow the argument to be expanded; if a namespace + * happens to collide with a #define, this can cause issues. + * 2) There's no need to modify genksyms to deal with the _NS variants + */ +#define EXPORT_SYMBOL_NS(sym, ns) EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_NS_GPL(sym, ns) EXPORT_SYMBOL_GPL(sym) +#endif #else /* !CONFIG_MODULES... */ #define EXPORT_SYMBOL(sym) +#define EXPORT_SYMBOL_NS(sym, ns) +#define EXPORT_SYMBOL_NS_GPL(sym, ns) #define EXPORT_SYMBOL_GPL(sym) #define EXPORT_SYMBOL_GPL_FUTURE(sym) #define EXPORT_UNUSED_SYMBOL(sym) diff --git a/include/linux/module.h b/include/linux/module.h index 1455812dd325..b3611e749f72 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -280,6 +280,8 @@ struct notifier_block; #ifdef CONFIG_MODULES +#define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, #ns) + extern int modules_disabled; /* for sysctl */ /* Get/put a kernel symbol (calls must be symmetric) */ void *__symbol_get(const char *symbol); diff --git a/kernel/module.c b/kernel/module.c index 3ee507c0a92f..6bb9b938f9c7 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -544,6 +544,15 @@ static const char *kernel_symbol_name(const struct kernel_symbol *sym) #endif } +static const char *kernel_symbol_namespace(const struct kernel_symbol *sym) +{ +#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS + return offset_to_ptr(&sym->namespace_offset); +#else + return sym->namespace; +#endif +} + static int cmp_name(const void *va, const void *vb) { const char *a; @@ -1379,6 +1388,34 @@ static inline int same_magic(const char *amagic, const char *bmagic, } #endif /* CONFIG_MODVERSIONS */ +static char *get_modinfo(const struct load_info *info, const char *tag); +static char *get_next_modinfo(const struct load_info *info, const char *tag, + char *prev); + +static int verify_namespace_is_imported(const struct load_info *info, + const struct kernel_symbol *sym, + struct module *mod) +{ + const char *namespace; + char *imported_namespace; + + namespace = kernel_symbol_namespace(sym); + if (namespace) { + imported_namespace = get_modinfo(info, "import_ns"); + while (imported_namespace) { + if (strcmp(namespace, imported_namespace) == 0) + return 0; + imported_namespace = get_next_modinfo( + info, "import_ns", imported_namespace); + } + pr_err("%s: module uses symbol (%s) from namespace %s, but does not import it.\n", + mod->name, kernel_symbol_name(sym), namespace); + return -EINVAL; + } + return 0; +} + + /* Resolve a symbol for this module. I.e. if we find one, record usage. */ static const struct kernel_symbol *resolve_symbol(struct module *mod, const struct load_info *info, @@ -1407,6 +1444,12 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod, goto getname; } + err = verify_namespace_is_imported(info, sym, mod); + if (err) { + sym = ERR_PTR(err); + goto getname; + } + err = ref_module(mod, owner); if (err) { sym = ERR_PTR(err); -- cgit v1.2.3 From 8e2adc6a00cd96c07fcfa8adfbedbb4be681b8a7 Mon Sep 17 00:00:00 2001 From: Matthias Maennich Date: Fri, 6 Sep 2019 11:32:30 +0100 Subject: export: allow definition default namespaces in Makefiles or sources To avoid excessive usage of EXPORT_SYMBOL_NS(sym, MY_NAMESPACE), where MY_NAMESPACE will always be the namespace we are exporting to, allow exporting all definitions of EXPORT_SYMBOL() and friends by defining DEFAULT_SYMBOL_NAMESPACE. For example, to export all symbols defined in usb-common into the namespace USB_COMMON, add a line like this to drivers/usb/common/Makefile: ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=USB_COMMON That is equivalent to changing all EXPORT_SYMBOL(sym) definitions to EXPORT_SYMBOL_NS(sym, USB_COMMON). Subsequently all symbol namespaces functionality will apply. Another way of making use of this feature is to define the namespace within source or header files similar to how TRACE_SYSTEM defines are used: #undef DEFAULT_SYMBOL_NAMESPACE #define DEFAULT_SYMBOL_NAMESPACE USB_COMMON Please note that, as opposed to TRACE_SYSTEM, DEFAULT_SYMBOL_NAMESPACE has to be defined before including include/linux/export.h. If DEFAULT_SYMBOL_NAMESPACE is defined, a symbol can still be exported to another namespace by using EXPORT_SYMBOL_NS() and friends with explicitly specifying the namespace. Suggested-by: Arnd Bergmann Reviewed-by: Martijn Coenen Reviewed-by: Greg Kroah-Hartman Signed-off-by: Matthias Maennich Signed-off-by: Jessica Yu --- include/linux/export.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include/linux/export.h') diff --git a/include/linux/export.h b/include/linux/export.h index d59461e71478..2c5468d8ea9a 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -166,6 +166,12 @@ struct kernel_symbol { #define __EXPORT_SYMBOL ___EXPORT_SYMBOL #endif +#ifdef DEFAULT_SYMBOL_NAMESPACE +#undef __EXPORT_SYMBOL +#define __EXPORT_SYMBOL(sym, sec) \ + __EXPORT_SYMBOL_NS(sym, sec, DEFAULT_SYMBOL_NAMESPACE) +#endif + #define EXPORT_SYMBOL(sym) __EXPORT_SYMBOL(sym, "") #define EXPORT_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_gpl") #define EXPORT_SYMBOL_GPL_FUTURE(sym) __EXPORT_SYMBOL(sym, "_gpl_future") -- cgit v1.2.3 From 069e1c07c18ac2ccecdfb5ac287a37d6fb2d7a00 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 11 Sep 2019 13:26:46 +0100 Subject: module: Fix link failure due to invalid relocation on namespace offset Commit 8651ec01daed ("module: add support for symbol namespaces.") broke linking for arm64 defconfig: | lib/crypto/arc4.o: In function `__ksymtab_arc4_setkey': | arc4.c:(___ksymtab+arc4_setkey+0x8): undefined reference to `no symbol' | lib/crypto/arc4.o: In function `__ksymtab_arc4_crypt': | arc4.c:(___ksymtab+arc4_crypt+0x8): undefined reference to `no symbol' This is because the dummy initialisation of the 'namespace_offset' field in 'struct kernel_symbol' when using EXPORT_SYMBOL on architectures with support for PREL32 locations uses an offset from an absolute address (0) in an effort to trick 'offset_to_pointer' into behaving as a NOP, allowing non-namespaced symbols to be treated in the same way as those belonging to a namespace. Unfortunately, place-relative relocations require a symbol reference rather than an absolute value and, although x86 appears to get away with this due to placing the kernel text at the top of the address space, it almost certainly results in a runtime failure if the kernel is relocated dynamically as a result of KASLR. Rework 'namespace_offset' so that a value of 0, which cannot occur for a valid namespaced symbol, indicates that the corresponding symbol does not belong to a namespace. Cc: Matthias Maennich Cc: Jessica Yu Cc: Ard Biesheuvel Cc: Catalin Marinas Fixes: 8651ec01daed ("module: add support for symbol namespaces.") Reported-by: kbuild test robot Tested-by: Matthias Maennich Tested-by: Ard Biesheuvel Reviewed-by: Matthias Maennich Acked-by: Ard Biesheuvel Signed-off-by: Will Deacon Signed-off-by: Jessica Yu --- include/asm-generic/export.h | 2 +- include/linux/export.h | 2 +- kernel/module.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include/linux/export.h') diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h index e2b5d0f569d3..d0912c7ac2fc 100644 --- a/include/asm-generic/export.h +++ b/include/asm-generic/export.h @@ -17,7 +17,7 @@ .macro __put, val, name #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS - .long \val - ., \name - ., 0 - . + .long \val - ., \name - ., 0 #elif defined(CONFIG_64BIT) .quad \val, \name, 0 #else diff --git a/include/linux/export.h b/include/linux/export.h index 2c5468d8ea9a..ef5d015d754a 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -68,7 +68,7 @@ extern struct module __this_module; "__ksymtab_" #sym ": \n" \ " .long " #sym "- . \n" \ " .long __kstrtab_" #sym "- . \n" \ - " .long 0 - . \n" \ + " .long 0 \n" \ " .previous \n") struct kernel_symbol { diff --git a/kernel/module.c b/kernel/module.c index f76efcf2043e..7ab244c4e1ba 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -547,6 +547,8 @@ static const char *kernel_symbol_name(const struct kernel_symbol *sym) static const char *kernel_symbol_namespace(const struct kernel_symbol *sym) { #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS + if (!sym->namespace_offset) + return NULL; return offset_to_ptr(&sym->namespace_offset); #else return sym->namespace; -- cgit v1.2.3