Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <maco@android.com>
Signed-off-by: Martijn Coenen <maco@android.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Matthias Maennich <maennich@google.com>
Signed-off-by: Jessica Yu <jeyu@kernel.org>

authored by

Matthias Maennich and committed by
Jessica Yu
8651ec01 ed13fc33

+125 -21
+3 -3
include/asm-generic/export.h
··· 17 17 18 18 .macro __put, val, name 19 19 #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS 20 - .long \val - ., \name - . 20 + .long \val - ., \name - ., 0 - . 21 21 #elif defined(CONFIG_64BIT) 22 - .quad \val, \name 22 + .quad \val, \name, 0 23 23 #else 24 - .long \val, \name 24 + .long \val, \name, 0 25 25 #endif 26 26 .endm 27 27
+77 -18
include/linux/export.h
··· 20 20 21 21 #ifdef CONFIG_MODULES 22 22 23 + #define NS_SEPARATOR "." 24 + 23 25 #if defined(__KERNEL__) && !defined(__GENKSYMS__) 24 26 #ifdef CONFIG_MODVERSIONS 25 27 /* Mark the CRC weak since genksyms apparently decides not to ··· 31 29 asm(" .section \"___kcrctab" sec "+" #sym "\", \"a\" \n" \ 32 30 " .weak __crc_" #sym " \n" \ 33 31 " .long __crc_" #sym " - . \n" \ 34 - " .previous \n"); 32 + " .previous \n") 35 33 #else 36 34 #define __CRC_SYMBOL(sym, sec) \ 37 35 asm(" .section \"___kcrctab" sec "+" #sym "\", \"a\" \n" \ 38 36 " .weak __crc_" #sym " \n" \ 39 37 " .long __crc_" #sym " \n" \ 40 - " .previous \n"); 38 + " .previous \n") 41 39 #endif 42 40 #else 43 41 #define __CRC_SYMBOL(sym, sec) ··· 51 49 * absolute relocations that require runtime processing on relocatable 52 50 * kernels. 53 51 */ 52 + #define __KSYMTAB_ENTRY_NS(sym, sec, ns) \ 53 + __ADDRESSABLE(sym) \ 54 + asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \ 55 + " .balign 4 \n" \ 56 + "__ksymtab_" #sym NS_SEPARATOR #ns ": \n" \ 57 + " .long " #sym "- . \n" \ 58 + " .long __kstrtab_" #sym "- . \n" \ 59 + " .long __kstrtab_ns_" #sym "- . \n" \ 60 + " .previous \n") 61 + 54 62 #define __KSYMTAB_ENTRY(sym, sec) \ 55 63 __ADDRESSABLE(sym) \ 56 64 asm(" .section \"___ksymtab" sec "+" #sym "\", \"a\" \n" \ ··· 68 56 "__ksymtab_" #sym ": \n" \ 69 57 " .long " #sym "- . \n" \ 70 58 " .long __kstrtab_" #sym "- . \n" \ 59 + " .long 0 - . \n" \ 71 60 " .previous \n") 72 61 73 62 struct kernel_symbol { 74 63 int value_offset; 75 64 int name_offset; 65 + int namespace_offset; 76 66 }; 77 67 #else 78 - #define __KSYMTAB_ENTRY(sym, sec) \ 79 - static const struct kernel_symbol __ksymtab_##sym \ 68 + #define __KSYMTAB_ENTRY_NS(sym, sec, ns) \ 69 + static const struct kernel_symbol __ksymtab_##sym##__##ns \ 70 + asm("__ksymtab_" #sym NS_SEPARATOR #ns) \ 80 71 __attribute__((section("___ksymtab" sec "+" #sym), used)) \ 81 72 __aligned(sizeof(void *)) \ 82 - = { (unsigned long)&sym, __kstrtab_##sym } 73 + = { (unsigned long)&sym, __kstrtab_##sym, __kstrtab_ns_##sym } 74 + 75 + #define __KSYMTAB_ENTRY(sym, sec) \ 76 + static const struct kernel_symbol __ksymtab_##sym \ 77 + asm("__ksymtab_" #sym) \ 78 + __attribute__((section("___ksymtab" sec "+" #sym), used)) \ 79 + __aligned(sizeof(void *)) \ 80 + = { (unsigned long)&sym, __kstrtab_##sym, NULL } 83 81 84 82 struct kernel_symbol { 85 83 unsigned long value; 86 84 const char *name; 85 + const char *namespace; 87 86 }; 88 87 #endif 89 88 90 - /* For every exported symbol, place a struct in the __ksymtab section */ 91 - #define ___EXPORT_SYMBOL(sym, sec) \ 89 + #define ___export_symbol_common(sym, sec) \ 92 90 extern typeof(sym) sym; \ 93 - __CRC_SYMBOL(sym, sec) \ 91 + __CRC_SYMBOL(sym, sec); \ 94 92 static const char __kstrtab_##sym[] \ 95 93 __attribute__((section("__ksymtab_strings"), used, aligned(1))) \ 96 - = #sym; \ 94 + = #sym \ 95 + 96 + /* For every exported symbol, place a struct in the __ksymtab section */ 97 + #define ___EXPORT_SYMBOL_NS(sym, sec, ns) \ 98 + ___export_symbol_common(sym, sec); \ 99 + static const char __kstrtab_ns_##sym[] \ 100 + __attribute__((section("__ksymtab_strings"), used, aligned(1))) \ 101 + = #ns; \ 102 + __KSYMTAB_ENTRY_NS(sym, sec, ns) 103 + 104 + #define ___EXPORT_SYMBOL(sym, sec) \ 105 + ___export_symbol_common(sym, sec); \ 97 106 __KSYMTAB_ENTRY(sym, sec) 98 107 99 108 #if defined(__DISABLE_EXPORTS) ··· 124 91 * be reused in other execution contexts such as the UEFI stub or the 125 92 * decompressor. 126 93 */ 94 + #define __EXPORT_SYMBOL_NS(sym, sec, ns) 127 95 #define __EXPORT_SYMBOL(sym, sec) 128 96 129 97 #elif defined(CONFIG_TRIM_UNUSED_KSYMS) ··· 151 117 #define __cond_export_sym_1(sym, sec) ___EXPORT_SYMBOL(sym, sec) 152 118 #define __cond_export_sym_0(sym, sec) /* nothing */ 153 119 120 + #define __EXPORT_SYMBOL_NS(sym, sec, ns) \ 121 + __ksym_marker(sym); \ 122 + __cond_export_ns_sym(sym, sec, ns, __is_defined(__KSYM_##sym)) 123 + #define __cond_export_ns_sym(sym, sec, ns, conf) \ 124 + ___cond_export_ns_sym(sym, sec, ns, conf) 125 + #define ___cond_export_ns_sym(sym, sec, ns, enabled) \ 126 + __cond_export_ns_sym_##enabled(sym, sec, ns) 127 + #define __cond_export_ns_sym_1(sym, sec, ns) ___EXPORT_SYMBOL_NS(sym, sec, ns) 128 + #define __cond_export_ns_sym_0(sym, sec, ns) /* nothing */ 129 + 154 130 #else 131 + #define __EXPORT_SYMBOL_NS ___EXPORT_SYMBOL_NS 155 132 #define __EXPORT_SYMBOL ___EXPORT_SYMBOL 156 133 #endif 157 134 158 - #define EXPORT_SYMBOL(sym) \ 159 - __EXPORT_SYMBOL(sym, "") 160 - 161 - #define EXPORT_SYMBOL_GPL(sym) \ 162 - __EXPORT_SYMBOL(sym, "_gpl") 163 - 164 - #define EXPORT_SYMBOL_GPL_FUTURE(sym) \ 165 - __EXPORT_SYMBOL(sym, "_gpl_future") 135 + #define EXPORT_SYMBOL(sym) __EXPORT_SYMBOL(sym, "") 136 + #define EXPORT_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_gpl") 137 + #define EXPORT_SYMBOL_GPL_FUTURE(sym) __EXPORT_SYMBOL(sym, "_gpl_future") 138 + #define EXPORT_SYMBOL_NS(sym, ns) __EXPORT_SYMBOL_NS(sym, "", ns) 139 + #define EXPORT_SYMBOL_NS_GPL(sym, ns) __EXPORT_SYMBOL_NS(sym, "_gpl", ns) 166 140 167 141 #ifdef CONFIG_UNUSED_SYMBOLS 168 142 #define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused") ··· 180 138 #define EXPORT_UNUSED_SYMBOL_GPL(sym) 181 139 #endif 182 140 183 - #endif /* __GENKSYMS__ */ 141 + #endif /* __KERNEL__ && !__GENKSYMS__ */ 142 + 143 + #if defined(__GENKSYMS__) 144 + /* 145 + * When we're running genksyms, ignore the namespace and make the _NS 146 + * variants look like the normal ones. There are two reasons for this: 147 + * 1) In the normal definition of EXPORT_SYMBOL_NS, the 'ns' macro 148 + * argument is itself not expanded because it's always tokenized or 149 + * concatenated; but when running genksyms, a blank definition of the 150 + * macro does allow the argument to be expanded; if a namespace 151 + * happens to collide with a #define, this can cause issues. 152 + * 2) There's no need to modify genksyms to deal with the _NS variants 153 + */ 154 + #define EXPORT_SYMBOL_NS(sym, ns) EXPORT_SYMBOL(sym) 155 + #define EXPORT_SYMBOL_NS_GPL(sym, ns) EXPORT_SYMBOL_GPL(sym) 156 + #endif 184 157 185 158 #else /* !CONFIG_MODULES... */ 186 159 187 160 #define EXPORT_SYMBOL(sym) 161 + #define EXPORT_SYMBOL_NS(sym, ns) 162 + #define EXPORT_SYMBOL_NS_GPL(sym, ns) 188 163 #define EXPORT_SYMBOL_GPL(sym) 189 164 #define EXPORT_SYMBOL_GPL_FUTURE(sym) 190 165 #define EXPORT_UNUSED_SYMBOL(sym)
+2
include/linux/module.h
··· 280 280 281 281 #ifdef CONFIG_MODULES 282 282 283 + #define MODULE_IMPORT_NS(ns) MODULE_INFO(import_ns, #ns) 284 + 283 285 extern int modules_disabled; /* for sysctl */ 284 286 /* Get/put a kernel symbol (calls must be symmetric) */ 285 287 void *__symbol_get(const char *symbol);
+43
kernel/module.c
··· 544 544 #endif 545 545 } 546 546 547 + static const char *kernel_symbol_namespace(const struct kernel_symbol *sym) 548 + { 549 + #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS 550 + return offset_to_ptr(&sym->namespace_offset); 551 + #else 552 + return sym->namespace; 553 + #endif 554 + } 555 + 547 556 static int cmp_name(const void *va, const void *vb) 548 557 { 549 558 const char *a; ··· 1388 1379 } 1389 1380 #endif /* CONFIG_MODVERSIONS */ 1390 1381 1382 + static char *get_modinfo(const struct load_info *info, const char *tag); 1383 + static char *get_next_modinfo(const struct load_info *info, const char *tag, 1384 + char *prev); 1385 + 1386 + static int verify_namespace_is_imported(const struct load_info *info, 1387 + const struct kernel_symbol *sym, 1388 + struct module *mod) 1389 + { 1390 + const char *namespace; 1391 + char *imported_namespace; 1392 + 1393 + namespace = kernel_symbol_namespace(sym); 1394 + if (namespace) { 1395 + imported_namespace = get_modinfo(info, "import_ns"); 1396 + while (imported_namespace) { 1397 + if (strcmp(namespace, imported_namespace) == 0) 1398 + return 0; 1399 + imported_namespace = get_next_modinfo( 1400 + info, "import_ns", imported_namespace); 1401 + } 1402 + pr_err("%s: module uses symbol (%s) from namespace %s, but does not import it.\n", 1403 + mod->name, kernel_symbol_name(sym), namespace); 1404 + return -EINVAL; 1405 + } 1406 + return 0; 1407 + } 1408 + 1409 + 1391 1410 /* Resolve a symbol for this module. I.e. if we find one, record usage. */ 1392 1411 static const struct kernel_symbol *resolve_symbol(struct module *mod, 1393 1412 const struct load_info *info, ··· 1441 1404 1442 1405 if (!check_version(info, name, mod, crc)) { 1443 1406 sym = ERR_PTR(-EINVAL); 1407 + goto getname; 1408 + } 1409 + 1410 + err = verify_namespace_is_imported(info, sym, mod); 1411 + if (err) { 1412 + sym = ERR_PTR(err); 1444 1413 goto getname; 1445 1414 } 1446 1415