diff mbox series

[3/7] kbuild: generate struct kernel_symbol by modpost

Message ID 20220610183236.1272216-4-masahiroy@kernel.org (mailing list archive)
State New, archived
Headers show
Series Unify <linux/export.h> and <asm/export.h>, remove EXPORT_DATA_SYMBOL() | expand

Commit Message

Masahiro Yamada June 10, 2022, 6:32 p.m. UTC
Commit 7b4537199a4a ("kbuild: link symbol CRCs at final link, removing
CONFIG_MODULE_REL_CRCS") made the module versioning implementation
arch-agnostic; now all the version CRCs are generated by modpost as C
code whether the EXPORT_SYMBOL() is placed in *.c or *.S.

Doing similar for the entire data structure of EXPORT_SYMBOL() makes
further cleanups possible.

This commit splits EXPORT_SYMBOL() compilation into two stages.

When a source file is compiled, EXPORT_SYMBOL() is converted into a
dummy symbol in the .discard.export_symbol section.

For example,

    EXPORT_SYMBOL(foo);
    EXPORT_SYMBOL_NS_GPL(bar, BAR_NAMESPACE);

will be expanded into the following assembly code:

    .section .discard.export_symbol
    __export_symbol.foo:
	.asciz ""
    .previous

    .section .discard.export_symbol
    __export_symbol_gpl.bar:
	.asciz "BAR_NAMESPACE"
    .previous

They are just markers to tell modpost the name, license, and namespace
of the symbols. They will be dropped from the final vmlinux and modules
because the section name starts with ".discard.".

Then, modpost extracts all the information of EXPORT_SYMBOL() from the
.discard.export_symbol section, then generates C code:

    KSYMTAB_ENTRY(foo, "", "");
    KSYMTAB_ENTRY(bar, "_gpl", "BAR_NAMESPACE");

KSYMTAB_ENTRY() is expanded to struct kernel_symbol that will be linked
to the vmlinux or a module.

With this change, EXPORT_SYMBOL() works in the same way for *.c and *.S
files, providing the following benefits.

[1] Deprecate EXPORT_DATA_SYMBOL()

In the old days, EXPORT_SYMBOL() was only available in C files.
To export assembly code, EXPORT_SYMBOL() was placed in a separate file.
arch/arm/kernel/armksyms.c is one example written in the old fashion.

Commit 22823ab419d8 ("EXPORT_SYMBOL() for asm") removed this limitation.
Since then, EXPORT_SYMBOL() can be placed close to the symbol definition
in *.S files. It was a nice improvement.

However, as that commit mentioned, you need to use EXPORT_DATA_SYMBOL()
for data objects on some architectures.

For example, commit e007c53397ac ("ia64: move exports to definitions")
moved EXPORT_SYMBOL(ia64_ivt) to a .S file, but it resulted in
EXPORT_DATA_SYMBOL().

In the new approach, struct kernel_symbol is populated in C.
You can always use EXPORT_SYMBOL().

EXPORT_DATA_SYMBOL() is now deprecated.

[2] merge <linux/export.h> and <asm-generic/export.h>

There are two similar header implementations:

  include/linux/export.h        for .c files
  include/asm-generic/export.h  for .S files

Ideally, the functionality should be consistent between them, but they
tend to diverge.

Commit 8651ec01daed ("module: add support for symbol namespaces.") did
not support the namespace for *.S files.

This commit shifts the essential part to C. EXPORT_SYMBOL_NS() is now
supported for .S files.

<asm/export.h> and <asm-generic/export.h> will remain as a wrapper of
<linux/export.h> for a while.

They will be removed after #include <asm/export.h> directives are all
replaced.

[3] Implement CONFIG_TRIM_UNUSED_KSYMS in one-pass (planned)

CONFIG_TRIM_UNUSED_KSYMS works by recursively traversing the directory
tree. If an EXPORT_SYMBOL turns out to be unused by anyone, the source
file is recompiled, making the EXPORT_SYMBOL() a no-op.

Linus did not like this slowness.

See:

 - 5cf0fd591f2e ("Kbuild: disable TRIM_UNUSED_KSYMS option")
 - a555bdd0c58c ("Kbuild: enable TRIM_UNUSED_KSYMS again, with some guarding")

We can do this better now; modpost can selectively emit KSYMTAB_ENTRY's
that are really referenced by modules.

Nicolas explained why the trimming was implemented with recursion:

  https://lore.kernel.org/all/2o2rpn97-79nq-p7s2-nq5-8p83391473r@syhkavp.arg/

Actually, we never achieved that level of optimization where the chain
reaction of trimming comes into play because:

 - CONFIG_LTO_CLANG cannot remove any unused symbols
 - CONFIG_LD_DEAD_CODE_DATA_ELIMINATION is enabled only for vmlinux,
   but not modules

If deeper trimming is required, we need to revisit this, but making the
directory traverse one-pass is presumably the right way to go.

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
---

 arch/ia64/include/asm/Kbuild    |  1 +
 arch/ia64/include/asm/export.h  |  3 -
 include/asm-generic/export.h    | 84 ++--------------------------
 include/linux/export-internal.h | 44 +++++++++++++++
 include/linux/export.h          | 97 +++++++++++----------------------
 kernel/module/internal.h        | 12 ++++
 kernel/module/main.c            |  1 -
 scripts/Makefile.build          |  8 +--
 scripts/check-local-export      |  4 +-
 scripts/mod/modpost.c           | 85 +++++++++++++++++------------
 scripts/mod/modpost.h           |  1 +
 11 files changed, 151 insertions(+), 189 deletions(-)
 delete mode 100644 arch/ia64/include/asm/export.h

Comments

Masahiro Yamada June 11, 2022, 6:47 p.m. UTC | #1
On Sat, Jun 11, 2022 at 3:34 AM Masahiro Yamada <masahiroy@kernel.org> wrote:
>
> Commit 7b4537199a4a ("kbuild: link symbol CRCs at final link, removing
> CONFIG_MODULE_REL_CRCS") made the module versioning implementation
> arch-agnostic; now all the version CRCs are generated by modpost as C
> code whether the EXPORT_SYMBOL() is placed in *.c or *.S.
>
> Doing similar for the entire data structure of EXPORT_SYMBOL() makes
> further cleanups possible.
>
> This commit splits EXPORT_SYMBOL() compilation into two stages.
>
> When a source file is compiled, EXPORT_SYMBOL() is converted into a
> dummy symbol in the .discard.export_symbol section.
>
> For example,
>
>     EXPORT_SYMBOL(foo);
>     EXPORT_SYMBOL_NS_GPL(bar, BAR_NAMESPACE);
>
> will be expanded into the following assembly code:
>
>     .section .discard.export_symbol
>     __export_symbol.foo:
>         .asciz ""
>     .previous
>
>     .section .discard.export_symbol
>     __export_symbol_gpl.bar:
>         .asciz "BAR_NAMESPACE"
>     .previous
>
> They are just markers to tell modpost the name, license, and namespace
> of the symbols. They will be dropped from the final vmlinux and modules
> because the section name starts with ".discard.".
>
> Then, modpost extracts all the information of EXPORT_SYMBOL() from the
> .discard.export_symbol section, then generates C code:
>
>     KSYMTAB_ENTRY(foo, "", "");
>     KSYMTAB_ENTRY(bar, "_gpl", "BAR_NAMESPACE");
>
> KSYMTAB_ENTRY() is expanded to struct kernel_symbol that will be linked
> to the vmlinux or a module.
>
> With this change, EXPORT_SYMBOL() works in the same way for *.c and *.S
> files, providing the following benefits.
>
> [1] Deprecate EXPORT_DATA_SYMBOL()



Sorry, please let me take back this.

I completely missed how linkage for ia64 works.
I used inline assembly in C, so it would break ia64.

Please ignore this patch.
diff mbox series

Patch

diff --git a/arch/ia64/include/asm/Kbuild b/arch/ia64/include/asm/Kbuild
index f994c1daf9d4..fc998339c405 100644
--- a/arch/ia64/include/asm/Kbuild
+++ b/arch/ia64/include/asm/Kbuild
@@ -1,5 +1,6 @@ 
 # SPDX-License-Identifier: GPL-2.0
 generated-y += syscall_table.h
+generic-y += export.h
 generic-y += kvm_para.h
 generic-y += mcs_spinlock.h
 generic-y += vtime.h
diff --git a/arch/ia64/include/asm/export.h b/arch/ia64/include/asm/export.h
deleted file mode 100644
index ad18c6583252..000000000000
--- a/arch/ia64/include/asm/export.h
+++ /dev/null
@@ -1,3 +0,0 @@ 
-/* EXPORT_DATA_SYMBOL != EXPORT_SYMBOL here */
-#define KSYM_FUNC(name) @fptr(name)
-#include <asm-generic/export.h>
diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h
index 5e4b1f2369d2..0ae9f38a904c 100644
--- a/include/asm-generic/export.h
+++ b/include/asm-generic/export.h
@@ -3,86 +3,12 @@ 
 #define __ASM_GENERIC_EXPORT_H
 
 /*
- * This comment block is used by fixdep. Please do not remove.
- *
- * When CONFIG_MODVERSIONS is changed from n to y, all source files having
- * EXPORT_SYMBOL variants must be re-compiled because genksyms is run as a
- * side effect of the *.o build rule.
+ * <asm/export.h> and <asm-generic/export.h> are deprecated.
+ * Please include <linux/export.h> directly.
  */
+#include <linux/export.h>
 
-#ifndef KSYM_FUNC
-#define KSYM_FUNC(x) x
-#endif
-#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
-#define KSYM_ALIGN 4
-#elif defined(CONFIG_64BIT)
-#define KSYM_ALIGN 8
-#else
-#define KSYM_ALIGN 4
-#endif
-
-.macro __put, val, name
-#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
-	.long	\val - ., \name - ., 0
-#elif defined(CONFIG_64BIT)
-	.quad	\val, \name, 0
-#else
-	.long	\val, \name, 0
-#endif
-.endm
-
-/*
- * note on .section use: we specify progbits since usage of the "M" (SHF_MERGE)
- * section flag requires it. Use '%progbits' instead of '@progbits' since the
- * former apparently works on all arches according to the binutils source.
- */
-
-.macro ___EXPORT_SYMBOL name,val,sec
-#if defined(CONFIG_MODULES) && !defined(__DISABLE_EXPORTS)
-	.section ___ksymtab\sec+\name,"a"
-	.balign KSYM_ALIGN
-__ksymtab_\name:
-	__put \val, __kstrtab_\name
-	.previous
-	.section __ksymtab_strings,"aMS",%progbits,1
-__kstrtab_\name:
-	.asciz "\name"
-	.previous
-#endif
-.endm
-
-#if defined(CONFIG_TRIM_UNUSED_KSYMS)
-
-#include <linux/kconfig.h>
-#include <generated/autoksyms.h>
-
-.macro __ksym_marker sym
-	.section ".discard.ksym","a"
-__ksym_marker_\sym:
-	 .previous
-.endm
-
-#define __EXPORT_SYMBOL(sym, val, sec)				\
-	__ksym_marker sym;					\
-	__cond_export_sym(sym, val, sec, __is_defined(__KSYM_##sym))
-#define __cond_export_sym(sym, val, sec, conf)			\
-	___cond_export_sym(sym, val, sec, conf)
-#define ___cond_export_sym(sym, val, sec, enabled)		\
-	__cond_export_sym_##enabled(sym, val, sec)
-#define __cond_export_sym_1(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
-#define __cond_export_sym_0(sym, val, sec) /* nothing */
-
-#else
-#define __EXPORT_SYMBOL(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
-#endif
-
-#define EXPORT_SYMBOL(name)					\
-	__EXPORT_SYMBOL(name, KSYM_FUNC(name),)
-#define EXPORT_SYMBOL_GPL(name) 				\
-	__EXPORT_SYMBOL(name, KSYM_FUNC(name), _gpl)
-#define EXPORT_DATA_SYMBOL(name)				\
-	__EXPORT_SYMBOL(name, name,)
-#define EXPORT_DATA_SYMBOL_GPL(name)				\
-	__EXPORT_SYMBOL(name, name,_gpl)
+#define EXPORT_DATA_SYMBOL(name)	EXPORT_SYMBOL(name)
+#define EXPORT_DATA_SYMBOL_GPL(name)	EXPORT_SYMBOL_GPL(name)
 
 #endif
diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h
index c2b1d4fd5987..b491ee482947 100644
--- a/include/linux/export-internal.h
+++ b/include/linux/export-internal.h
@@ -14,4 +14,48 @@ 
 #define SYMBOL_CRC(sym, crc, sec)   \
 	u32 __section("___kcrctab" sec "+" #sym) __used __crc_##sym = crc
 
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+/*
+ * Emit the ksymtab entry as a pair of relative references: this reduces the
+ * size by half on 64-bit architectures, and eliminates the need for absolute
+ * relocations that require runtime processing on relocatable kernels.
+ */
+#define __KSYMTAB_ENTRY(sym, sec)					\
+	asm("	.section \"___ksymtab" sec "+" #sym "\", \"a\"	\n"	\
+	    "	.balign	4					\n"	\
+	    "__ksymtab_" #sym ":				\n"	\
+	    "	.long	" #sym "- .				\n"	\
+	    "	.long	__kstrtab_" #sym "- .			\n"	\
+	    "	.long	__kstrtabns_" #sym "- .			\n"	\
+	    "	.previous					\n")
+#else
+#define __KSYMTAB_ENTRY(sym, sec)					\
+	asm("	.section \"___ksymtab" sec "+" #sym "\", \"a\"	\n"	\
+	    "__ksymtab_" #sym ":				\n"	\
+	    "	.long	" #sym "				\n"	\
+	    "	.long	__kstrtab_" #sym "			\n"	\
+	    "	.long	__kstrtabns_" #sym "			\n"	\
+	    "	.previous					\n")
+#endif
+
+/*
+ * For every exported symbol, do the following:
+ *
+ * - Put the name of the symbol and namespace (empty string "" for none) in
+ *   __ksymtab_strings.
+ * - Place a struct kernel_symbol entry in the __ksymtab section.
+ *
+ * Note on .section use: we specify progbits since usage of the "M" (SHF_MERGE)
+ * section flag requires it. Use '%progbits' instead of '@progbits' since the
+ * former apparently works on all arches according to the binutils source.
+ */
+#define KSYMTAB_ENTRY(sym, sec, ns)						\
+	asm("	.section \"__ksymtab_strings\",\"aMS\",%progbits,1	\n"	\
+	    "__kstrtab_" #sym ":					\n"	\
+	    "	.asciz 	\"" #sym "\"					\n"	\
+	    "__kstrtabns_" #sym ":					\n"	\
+	    "	.asciz 	\"" ns "\"					\n"	\
+	    "	.previous						\n");	\
+	__KSYMTAB_ENTRY(sym, sec)
+
 #endif /* __LINUX_EXPORT_INTERNAL_H__ */
diff --git a/include/linux/export.h b/include/linux/export.h
index 3f31ced0d977..0c71577cf8bb 100644
--- a/include/linux/export.h
+++ b/include/linux/export.h
@@ -2,6 +2,7 @@ 
 #ifndef _LINUX_EXPORT_H
 #define _LINUX_EXPORT_H
 
+#include <linux/compiler.h>
 #include <linux/stringify.h>
 
 /*
@@ -28,72 +29,28 @@  extern struct module __this_module;
 #else
 #define THIS_MODULE ((struct module *)0)
 #endif
-
-#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
-#include <linux/compiler.h>
-/*
- * Emit the ksymtab entry as a pair of relative references: this reduces
- * the size by half on 64-bit architectures, and eliminates the need for
- * absolute relocations that require runtime processing on relocatable
- * kernels.
- */
-#define __KSYMTAB_ENTRY(sym, sec)					\
-	__ADDRESSABLE(sym)						\
-	asm("	.section \"___ksymtab" sec "+" #sym "\", \"a\"	\n"	\
-	    "	.balign	4					\n"	\
-	    "__ksymtab_" #sym ":				\n"	\
-	    "	.long	" #sym "- .				\n"	\
-	    "	.long	__kstrtab_" #sym "- .			\n"	\
-	    "	.long	__kstrtabns_" #sym "- .			\n"	\
-	    "	.previous					\n")
-
-struct kernel_symbol {
-	int value_offset;
-	int name_offset;
-	int namespace_offset;
-};
-#else
-#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, __kstrtabns_##sym }
-
-struct kernel_symbol {
-	unsigned long value;
-	const char *name;
-	const char *namespace;
-};
-#endif
+#endif /* __ASSEMBLY__ */
 
 #ifdef __GENKSYMS__
 
 #define ___EXPORT_SYMBOL(sym, sec, ns)	__GENKSYMS_EXPORT_SYMBOL(sym)
 
+#elif defined(__ASSEMBLY__)
+
+#define ___EXPORT_SYMBOL(sym, sec, ns)				\
+	.section .discard.export_symbol ;			\
+	__export_symbol##sec##.sym: ;				\
+	.asciz ns ;						\
+	.previous
+
 #else
 
-/*
- * For every exported symbol, do the following:
- *
- * - Put the name of the symbol and namespace (empty string "" for none) in
- *   __ksymtab_strings.
- * - Place a struct kernel_symbol entry in the __ksymtab section.
- *
- * note on .section use: we specify progbits since usage of the "M" (SHF_MERGE)
- * section flag requires it. Use '%progbits' instead of '@progbits' since the
- * former apparently works on all arches according to the binutils source.
- */
-#define ___EXPORT_SYMBOL(sym, sec, ns)						\
-	extern typeof(sym) sym;							\
-	extern const char __kstrtab_##sym[];					\
-	extern const char __kstrtabns_##sym[];					\
-	asm("	.section \"__ksymtab_strings\",\"aMS\",%progbits,1	\n"	\
-	    "__kstrtab_" #sym ":					\n"	\
-	    "	.asciz 	\"" #sym "\"					\n"	\
-	    "__kstrtabns_" #sym ":					\n"	\
-	    "	.asciz 	\"" ns "\"					\n"	\
-	    "	.previous						\n");	\
-	__KSYMTAB_ENTRY(sym, sec)
+#define ___EXPORT_SYMBOL(sym, sec, ns)				\
+	__ADDRESSABLE(sym)					\
+	asm(".section .discard.export_symbol		\n"	\
+	    "__export_symbol" #sec "." #sym ":		\n"	\
+	    ".asciz " "\"" ns "\"" "			\n"	\
+	    ".previous					\n")
 
 #endif
 
@@ -117,9 +74,21 @@  struct kernel_symbol {
  * from the $(NM) output (see scripts/gen_ksymdeps.sh). These symbols are
  * discarded in the final link stage.
  */
+
+#ifdef __ASSEMBLY__
+
+#define __ksym_marker(sym)					\
+	.section ".discard.ksym","a" ;				\
+__ksym_marker_##sym: ;						\
+	.previous
+
+#else
+
 #define __ksym_marker(sym)	\
 	static int __ksym_marker_##sym[0] __section(".discard.ksym") __used
 
+#endif
+
 #define __EXPORT_SYMBOL(sym, sec, ns)					\
 	__ksym_marker(sym);						\
 	__cond_export_sym(sym, sec, ns, __is_defined(__KSYM_##sym))
@@ -147,11 +116,9 @@  struct kernel_symbol {
 #define _EXPORT_SYMBOL(sym, sec)	__EXPORT_SYMBOL(sym, sec, "")
 #endif
 
-#define EXPORT_SYMBOL(sym)		_EXPORT_SYMBOL(sym, "")
-#define EXPORT_SYMBOL_GPL(sym)		_EXPORT_SYMBOL(sym, "_gpl")
-#define EXPORT_SYMBOL_NS(sym, ns)	__EXPORT_SYMBOL(sym, "", __stringify(ns))
-#define EXPORT_SYMBOL_NS_GPL(sym, ns)	__EXPORT_SYMBOL(sym, "_gpl", __stringify(ns))
-
-#endif /* !__ASSEMBLY__ */
+#define EXPORT_SYMBOL(sym)		_EXPORT_SYMBOL(sym,)
+#define EXPORT_SYMBOL_GPL(sym)		_EXPORT_SYMBOL(sym,_gpl)
+#define EXPORT_SYMBOL_NS(sym, ns)	__EXPORT_SYMBOL(sym,, __stringify(ns))
+#define EXPORT_SYMBOL_NS_GPL(sym, ns)	__EXPORT_SYMBOL(sym,_gpl, __stringify(ns))
 
 #endif /* _LINUX_EXPORT_H */
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index bc5507ab8450..a362905ed24c 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -36,6 +36,18 @@ 
 # define strict_align(X) (X)
 #endif
 
+struct kernel_symbol {
+#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
+	int value_offset;
+	int name_offset;
+	int namespace_offset;
+#else
+	unsigned long value;
+	const char *name;
+	const char *namespace;
+#endif
+};
+
 extern struct mutex module_mutex;
 extern struct list_head modules;
 
diff --git a/kernel/module/main.c b/kernel/module/main.c
index fed58d30725d..e2ed87de6977 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -6,7 +6,6 @@ 
 
 #define INCLUDE_VERMAGIC
 
-#include <linux/export.h>
 #include <linux/extable.h>
 #include <linux/moduleloader.h>
 #include <linux/module_signature.h>
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 1f01ac65c0cd..8848e465ba25 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -164,7 +164,7 @@  quiet_cmd_cc_o_c = CC $(quiet_modtag)  $@
 ifdef CONFIG_MODVERSIONS
 # When module versioning is enabled the following steps are executed:
 # o compile a <file>.o from <file>.c
-# o if <file>.o doesn't contain a __ksymtab version, i.e. does
+# o if <file>.o doesn't contain a __export_symbol*, i.e. does
 #   not export symbols, it's done.
 # o otherwise, we calculate symbol versions using the good old
 #   genksyms on the preprocessed source and dump them into the .cmd file.
@@ -172,7 +172,7 @@  ifdef CONFIG_MODVERSIONS
 #   be compiled and linked to the kernel and/or modules.
 
 gen_symversions =								\
-	if $(NM) $@ 2>/dev/null | grep -q __ksymtab; then			\
+	if $(NM) $@ 2>/dev/null | grep -q '__export_symbol.*\.'; then		\
 		$(call cmd_gensymtypes_$(1),$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
 			>> $(dot-target).cmd;					\
 	fi
@@ -288,9 +288,7 @@  $(obj)/%.lst: $(src)/%.c FORCE
 cmd_gensymtypes_S =                                                         \
    { echo "\#include <linux/kernel.h>" ;                                    \
      echo "\#include <asm/asm-prototypes.h>" ;                              \
-    $(CPP) $(a_flags) $< |                                                  \
-     grep "\<___EXPORT_SYMBOL\>" |                                          \
-     sed 's/.*___EXPORT_SYMBOL[[:space:]]*\([a-zA-Z0-9_]*\)[[:space:]]*,.*/EXPORT_SYMBOL(\1);/' ; } | \
+     $(NM) $@ | sed -n 's/.*__export_symbol.*\.\(.*\)/EXPORT_SYMBOL(\1);/p' ; } | \
     $(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms)
 
 quiet_cmd_cc_symtypes_S = SYM $(quiet_modtag) $@
diff --git a/scripts/check-local-export b/scripts/check-local-export
index da745e2743b7..2e83e0498d4f 100755
--- a/scripts/check-local-export
+++ b/scripts/check-local-export
@@ -34,8 +34,8 @@  do
 	symbol_types[${name}]=${type}
 
 	# append the exported symbol to the array
-	if [[ ${name} == __ksymtab_* ]]; then
-		export_symbols+=(${name#__ksymtab_})
+	if [[ ${name} == __export_symbol*.* ]]; then
+		export_symbols+=(${name#*.})
 	fi
 
 	# If there is no symbol in the object, ${NM} (both GNU nm and llvm-nm)
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index b9f2a040f185..f738dddde7b8 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -533,6 +533,8 @@  static int parse_elf(struct elf_info *info, const char *filename)
 				fatal("%s has NOBITS .modinfo\n", filename);
 			info->modinfo = (void *)hdr + sechdrs[i].sh_offset;
 			info->modinfo_len = sechdrs[i].sh_size;
+		} else if (!strcmp(secname, ".discard.export_symbol")) {
+			info->export_symbol_sec = i;
 		}
 
 		if (sechdrs[i].sh_type == SHT_SYMTAB) {
@@ -655,17 +657,20 @@  static void handle_symbol(struct module *mod, struct elf_info *info,
 				   ELF_ST_BIND(sym->st_info) == STB_WEAK);
 		break;
 	default:
-		/* All exported symbols */
-		if (strstarts(symname, "__ksymtab_")) {
-			const char *name, *secname;
+		if (sym->st_shndx == info->export_symbol_sec) {
+			const char *name;
 
-			name = symname + strlen("__ksymtab_");
-			secname = sec_name_of_symbol(info, sym);
-
-			if (strstarts(secname, "___ksymtab_gpl+"))
+			if (strstarts(symname, "__export_symbol_gpl.")) {
+				name = symname + strlen("__export_symbol_gpl.");
 				sym_add_exported(name, mod, true);
-			else if (strstarts(secname, "___ksymtab+"))
+				sym_update_namespace(name, sym_get_data(info, sym));
+			} else if (strstarts(symname, "__export_symbol.")) {
+				name = symname + strlen("__export_symbol.");
 				sym_add_exported(name, mod, false);
+				sym_update_namespace(name, sym_get_data(info, sym));
+			}
+
+			break;
 		}
 		if (strcmp(symname, "init_module") == 0)
 			mod->has_init = true;
@@ -877,7 +882,6 @@  enum mismatch {
 	XXXEXIT_TO_SOME_EXIT,
 	ANY_INIT_TO_ANY_EXIT,
 	ANY_EXIT_TO_ANY_INIT,
-	EXPORT_TO_INIT_EXIT,
 	EXTABLE_TO_NON_TEXT,
 };
 
@@ -989,13 +993,6 @@  static const struct sectioncheck sectioncheck[] = {
 	.mismatch = ANY_INIT_TO_ANY_EXIT,
 	.symbol_white_list = { NULL },
 },
-/* Do not export init/exit functions or data */
-{
-	.fromsec = { "___ksymtab*", NULL },
-	.bad_tosec = { INIT_SECTIONS, EXIT_SECTIONS, NULL },
-	.mismatch = EXPORT_TO_INIT_EXIT,
-	.symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL },
-},
 {
 	.fromsec = { "__ex_table", NULL },
 	/* If you're adding any new black-listed sections in here, consider
@@ -1435,15 +1432,6 @@  static void report_sec_mismatch(const char *modname,
 		free(prl_from);
 		free(prl_to);
 		break;
-	case EXPORT_TO_INIT_EXIT:
-		prl_to = sec2annotation(tosec);
-		fprintf(stderr,
-		"The symbol %s is exported and annotated %s\n"
-		"Fix this by removing the %sannotation of %s "
-		"or drop the export.\n",
-		tosym, prl_to, prl_to, tosym);
-		free(prl_to);
-		break;
 	case EXTABLE_TO_NON_TEXT:
 		fatal("There's a special handler for this mismatch type, "
 		      "we should never get here.");
@@ -1830,6 +1818,36 @@  static void section_rel(const char *modname, struct elf_info *elf,
 	}
 }
 
+static void check_sec_for_export_symbols(struct module *mod, struct elf_info *elf)
+{
+	static const char *const init_or_exit_sections[] = {
+		INIT_SECTIONS,
+		EXIT_SECTIONS,
+		NULL
+	};
+	Elf_Sym *sym;
+
+	for (sym = elf->symtab_start; sym < elf->symtab_stop; sym++) {
+		const char *symname = sym_name(elf, sym);
+		const char *secname;
+		char *annotate;
+
+		if (!sym_find_with_module(symname, mod))
+			continue;	/* This is not an exported symbol */
+
+		secname = sec_name_of_symbol(elf, sym);
+
+		if (!match(secname, init_or_exit_sections))
+			continue;	/* OK, this is not __init or __exit */
+
+		annotate = sec2annotation(secname);
+
+		error("%s: exported and annotated %s. Remove %s or EXPORT_SYMBOL.\n",
+		      symname, annotate, annotate);
+		free(annotate);
+	}
+}
+
 /**
  * A module includes a number of sections that are discarded
  * either when loaded or when used as built-in.
@@ -2016,16 +2034,8 @@  static void read_symbols(const char *modname)
 		handle_moddevtable(mod, &info, sym, symname);
 	}
 
-	for (sym = info.symtab_start; sym < info.symtab_stop; sym++) {
-		symname = remove_dot(info.strtab + sym->st_name);
-
-		/* Apply symbol namespaces from __kstrtabns_<symbol> entries. */
-		if (strstarts(symname, "__kstrtabns_"))
-			sym_update_namespace(symname + strlen("__kstrtabns_"),
-					     sym_get_data(&info, sym));
-	}
-
 	check_sec_ref(modname, &info);
+	check_sec_for_export_symbols(mod, &info);
 
 	if (!mod->is_vmlinux) {
 		version = get_modinfo(&info, "version");
@@ -2208,6 +2218,13 @@  static void add_exported_symbols(struct buffer *buf, struct module *mod)
 {
 	struct symbol *sym;
 
+	/* generate struct for exported symbols */
+	buf_printf(buf, "\n");
+	list_for_each_entry(sym, &mod->exported_symbols, list)
+		buf_printf(buf, "KSYMTAB_ENTRY(%s, \"%s\", \"%s\");\n",
+			   sym->name, sym->is_gpl_only ? "_gpl" : "",
+			   sym->namespace ?: "");
+
 	if (!modversions)
 		return;
 
diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h
index 044bdfb894b7..ae59417a9df9 100644
--- a/scripts/mod/modpost.h
+++ b/scripts/mod/modpost.h
@@ -139,6 +139,7 @@  struct elf_info {
 	Elf_Shdr     *sechdrs;
 	Elf_Sym      *symtab_start;
 	Elf_Sym      *symtab_stop;
+	unsigned int export_symbol_sec;	/* .discard.export_symbol section */
 	char         *strtab;
 	char	     *modinfo;
 	unsigned int modinfo_len;