kbuild: link symbol CRCs at final link, removing CONFIG_MODULE_REL_CRCS
authorMasahiro Yamada <masahiroy@kernel.org>
Fri, 13 May 2022 11:39:22 +0000 (20:39 +0900)
committerMasahiro Yamada <masahiroy@kernel.org>
Tue, 24 May 2022 07:33:20 +0000 (16:33 +0900)
include/{linux,asm-generic}/export.h defines a weak symbol, __crc_*
as a placeholder.

Genksyms writes the version CRCs into the linker script, which will be
used for filling the __crc_* symbols. The linker script format depends
on CONFIG_MODULE_REL_CRCS. If it is enabled, __crc_* holds the offset
to the reference of CRC.

It is time to get rid of this complexity.

Now that modpost parses text files (.*.cmd) to collect all the CRCs,
it can generate C code that will be linked to the vmlinux or modules.

Generate a new C file, .vmlinux.export.c, which contains the CRCs of
symbols exported by vmlinux. It is compiled and linked to vmlinux in
scripts/link-vmlinux.sh.

Put the CRCs of symbols exported by modules into the existing *.mod.c
files. No additional build step is needed for modules. As before,
*.mod.c are compiled and linked to *.ko in scripts/Makefile.modfinal.

No linker magic is used here. The new C implementation works in the
same way, whether CONFIG_RELOCATABLE is enabled or not.
CONFIG_MODULE_REL_CRCS is no longer needed.

Previously, Kbuild invoked additional $(LD) to update the CRCs in
objects, but this step is unneeded too.

Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
Tested-by: Nathan Chancellor <nathan@kernel.org>
Tested-by: Nicolas Schier <nicolas@fjasle.eu>
Reviewed-by: Nicolas Schier <nicolas@fjasle.eu>
Tested-by: Sedat Dilek <sedat.dilek@gmail.com> # LLVM-14 (x86-64)
15 files changed:
arch/m68k/include/asm/Kbuild
arch/m68k/include/asm/export.h [deleted file]
arch/powerpc/Kconfig
arch/s390/Kconfig
arch/um/Kconfig
include/asm-generic/export.h
include/linux/export-internal.h [new file with mode: 0644]
include/linux/export.h
init/Kconfig
kernel/module.c
scripts/Makefile.build
scripts/Makefile.vmlinux [new file with mode: 0644]
scripts/genksyms/genksyms.c
scripts/link-vmlinux.sh
scripts/mod/modpost.c

index 0dbf9c5..1b72029 100644 (file)
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 generated-y += syscall_table.h
+generic-y += export.h
 generic-y += extable.h
 generic-y += kvm_para.h
 generic-y += mcs_spinlock.h
diff --git a/arch/m68k/include/asm/export.h b/arch/m68k/include/asm/export.h
deleted file mode 100644 (file)
index b53008b..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-#define KCRC_ALIGN 2
-#include <asm-generic/export.h>
index 174edab..a4e8dd8 100644 (file)
@@ -566,7 +566,6 @@ config RELOCATABLE
        bool "Build a relocatable kernel"
        depends on PPC64 || (FLATMEM && (44x || FSL_BOOKE))
        select NONSTATIC_KERNEL
-       select MODULE_REL_CRCS if MODVERSIONS
        help
          This builds a kernel image that is capable of running at the
          location the kernel is loaded at. For ppc32, there is no any
index 77b5a03..aa58480 100644 (file)
@@ -567,7 +567,6 @@ endchoice
 
 config RELOCATABLE
        bool "Build a relocatable kernel"
-       select MODULE_REL_CRCS if MODVERSIONS
        default y
        help
          This builds a kernel image that retains relocation information
index 4d398b8..e8983d0 100644 (file)
@@ -106,7 +106,6 @@ config LD_SCRIPT_DYN
        bool
        default y
        depends on !LD_SCRIPT_STATIC
-       select MODULE_REL_CRCS if MODVERSIONS
 
 config LD_SCRIPT_DYN_RPATH
        bool "set rpath in the binary" if EXPERT
index 07a36a8..5e4b1f2 100644 (file)
@@ -2,6 +2,14 @@
 #ifndef __ASM_GENERIC_EXPORT_H
 #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.
+ */
+
 #ifndef KSYM_FUNC
 #define KSYM_FUNC(x) x
 #endif
@@ -12,9 +20,6 @@
 #else
 #define KSYM_ALIGN 4
 #endif
-#ifndef KCRC_ALIGN
-#define KCRC_ALIGN 4
-#endif
 
 .macro __put, val, name
 #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
@@ -43,17 +48,6 @@ __ksymtab_\name:
 __kstrtab_\name:
        .asciz "\name"
        .previous
-#ifdef CONFIG_MODVERSIONS
-       .section ___kcrctab\sec+\name,"a"
-       .balign KCRC_ALIGN
-#if defined(CONFIG_MODULE_REL_CRCS)
-       .long __crc_\name - .
-#else
-       .long __crc_\name
-#endif
-       .weak __crc_\name
-       .previous
-#endif
 #endif
 .endm
 
diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h
new file mode 100644 (file)
index 0000000..c2b1d4f
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Please do not include this explicitly.
+ * This is used by C files generated by modpost.
+ */
+
+#ifndef __LINUX_EXPORT_INTERNAL_H__
+#define __LINUX_EXPORT_INTERNAL_H__
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+/* __used is needed to keep __crc_* for LTO */
+#define SYMBOL_CRC(sym, crc, sec)   \
+       u32 __section("___kcrctab" sec "+" #sym) __used __crc_##sym = crc
+
+#endif /* __LINUX_EXPORT_INTERNAL_H__ */
index 27d8487..565c5ff 100644 (file)
  * hackers place grumpy comments in header files.
  */
 
+/*
+ * 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.
+ */
+
 #ifndef __ASSEMBLY__
 #ifdef MODULE
 extern struct module __this_module;
@@ -19,26 +27,6 @@ extern struct module __this_module;
 #define THIS_MODULE ((struct module *)0)
 #endif
 
-#ifdef CONFIG_MODVERSIONS
-/* Mark the CRC weak since genksyms apparently decides not to
- * generate a checksums for some symbols */
-#if defined(CONFIG_MODULE_REL_CRCS)
-#define __CRC_SYMBOL(sym, sec)                                         \
-       asm("   .section \"___kcrctab" sec "+" #sym "\", \"a\"  \n"     \
-           "   .weak   __crc_" #sym "                          \n"     \
-           "   .long   __crc_" #sym " - .                      \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")
-#endif
-#else
-#define __CRC_SYMBOL(sym, sec)
-#endif
-
 #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
 #include <linux/compiler.h>
 /*
@@ -85,7 +73,6 @@ struct kernel_symbol {
 /*
  * For every exported symbol, do the following:
  *
- * - If applicable, place a CRC entry in the __kcrctab section.
  * - 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.
@@ -98,7 +85,6 @@ struct kernel_symbol {
        extern typeof(sym) sym;                                                 \
        extern const char __kstrtab_##sym[];                                    \
        extern const char __kstrtabns_##sym[];                                  \
-       __CRC_SYMBOL(sym, sec);                                                 \
        asm("   .section \"__ksymtab_strings\",\"aMS\",%progbits,1      \n"     \
            "__kstrtab_" #sym ":                                        \n"     \
            "   .asciz  \"" #sym "\"                                    \n"     \
index ddcbefe..f5b1431 100644 (file)
@@ -2136,10 +2136,6 @@ config ASM_MODVERSIONS
          assembly. This can be enabled only when the target architecture
          supports it.
 
-config MODULE_REL_CRCS
-       bool
-       depends on MODVERSIONS
-
 config MODULE_SRCVERSION_ALL
        bool "Source checksum for all modules"
        help
index 6cea788..c9e2342 100644 (file)
@@ -1231,11 +1231,6 @@ static int try_to_force_load(struct module *mod, const char *reason)
 
 #ifdef CONFIG_MODVERSIONS
 
-static u32 resolve_rel_crc(const s32 *crc)
-{
-       return *(u32 *)((void *)crc + *crc);
-}
-
 static int check_version(const struct load_info *info,
                         const char *symname,
                         struct module *mod,
@@ -1264,10 +1259,7 @@ static int check_version(const struct load_info *info,
                if (strcmp(versions[i].name, symname) != 0)
                        continue;
 
-               if (IS_ENABLED(CONFIG_MODULE_REL_CRCS))
-                       crcval = resolve_rel_crc(crc);
-               else
-                       crcval = *crc;
+               crcval = *crc;
                if (versions[i].crc == crcval)
                        return 1;
                pr_debug("Found checksum %X vs module %lX\n",
index a102386..ddd9080 100644 (file)
@@ -128,7 +128,6 @@ $(obj)/%.i: $(src)/%.c FORCE
 
 genksyms = scripts/genksyms/genksyms           \
        $(if $(1), -T $(2))                     \
-       $(if $(CONFIG_MODULE_REL_CRCS), -R)     \
        $(if $(KBUILD_PRESERVE), -p)            \
        -r $(or $(wildcard $(2:.symtypes=.symref)), /dev/null)
 
@@ -162,19 +161,11 @@ ifdef CONFIG_MODVERSIONS
 # o if <file>.o doesn't contain a __ksymtab version, 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 postprocess them in a way
-#   that they are usable as a linker script
-# o generate .tmp_<file>.o from <file>.o using the linker to
-#   replace the unresolved symbols __crc_exported_symbol with
-#   the actual value of the checksum generated by genksyms
-# o remove .tmp_<file>.o to <file>.o
+#   genksyms on the preprocessed source and dump them into the .cmd file.
+# o modpost will extract versions from that file and create *.c files that will
+#   be compiled and linked to the kernel and/or modules.
 
-# Generate .o.symversions files for each .o with exported symbols, and link these
-# to the kernel and/or modules at the end.
-
-genksyms_format_rel_crc := [^_]*__crc_\([^ ]*\) = \.; LONG(\([^)]*\)).*
-genksyms_format_normal := __crc_\(.*\) = \(.*\);
-genksyms_format := $(if $(CONFIG_MODULE_REL_CRCS),$(genksyms_format_rel_crc),$(genksyms_format_normal))
+genksyms_format := __crc_\(.*\) = \(.*\);
 
 gen_symversions =                                                              \
        if $(NM) $@ 2>/dev/null | grep -q __ksymtab; then                       \
@@ -188,12 +179,6 @@ gen_symversions =                                                          \
 
 cmd_gen_symversions_c =        $(call gen_symversions,c)
 
-cmd_modversions =                                                              \
-       if [ -r $@.symversions ]; then                                          \
-               $(LD) $(KBUILD_LDFLAGS) -r -o $(@D)/.tmp_$(@F) $@               \
-                       -T $@.symversions;                                      \
-               mv -f $(@D)/.tmp_$(@F) $@;                                      \
-       fi
 endif
 
 ifdef CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
@@ -273,7 +258,6 @@ define rule_cc_o_c
        $(call cmd,checkdoc)
        $(call cmd,gen_objtooldep)
        $(call cmd,gen_symversions_c)
-       $(if $(CONFIG_LTO_CLANG),,$(call cmd,modversions))
        $(call cmd,record_mcount)
 endef
 
@@ -282,7 +266,6 @@ define rule_as_o_S
        $(call cmd,gen_ksymdeps)
        $(call cmd,gen_objtooldep)
        $(call cmd,gen_symversions_S)
-       $(call cmd,modversions)
 endef
 
 # Built-in and composite module parts
@@ -296,8 +279,6 @@ ifneq ($(CONFIG_LTO_CLANG)$(CONFIG_X86_KERNEL_IBT),)
 quiet_cmd_cc_prelink_modules = LD [M]  $@
       cmd_cc_prelink_modules =                                         \
        $(LD) $(ld_flags) -r -o $@                                      \
-               $(shell [ -s $(@:.prelink.o=.o.symversions) ] &&                \
-                       echo -T $(@:.prelink.o=.o.symversions))         \
                --whole-archive $(filter-out FORCE,$^)                  \
                $(cmd_objtool)
 
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
new file mode 100644 (file)
index 0000000..7a63abf
--- /dev/null
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+include include/config/auto.conf
+include $(srctree)/scripts/Kbuild.include
+
+# for c_flags
+include $(srctree)/scripts/Makefile.lib
+
+quiet_cmd_cc_o_c = CC      $@
+      cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
+
+%.o: %.c FORCE
+       $(call if_changed_dep,cc_o_c)
+
+targets := $(MAKECMDGOALS)
+
+# Add FORCE to the prequisites of a target to force it to be always rebuilt.
+# ---------------------------------------------------------------------------
+
+PHONY += FORCE
+FORCE:
+
+# Read all saved command lines and dependencies for the $(targets) we
+# may be building above, using $(if_changed{,_dep}). As an
+# optimization, we don't need to read them if the target does not
+# exist, we will rebuild anyway in that case.
+
+existing-targets := $(wildcard $(sort $(targets)))
+
+-include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
+
+.PHONY: $(PHONY)
index 4827c5a..67b23cc 100644 (file)
@@ -33,7 +33,7 @@ char *cur_filename;
 int in_source_file;
 
 static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
-          flag_preserve, flag_warnings, flag_rel_crcs;
+          flag_preserve, flag_warnings;
 
 static int errors;
 static int nsyms;
@@ -680,11 +680,7 @@ void export_symbol(const char *name)
                if (flag_dump_defs)
                        fputs(">\n", debugfile);
 
-               /* Used as a linker script. */
-               printf(!flag_rel_crcs ? "__crc_%s = 0x%08lx;\n" :
-                      "SECTIONS { .rodata : ALIGN(4) { "
-                      "__crc_%s = .; LONG(0x%08lx); } }\n",
-                      name, crc);
+               printf("__crc_%s = 0x%08lx;\n", name, crc);
        }
 }
 
@@ -733,7 +729,6 @@ static void genksyms_usage(void)
              "  -q, --quiet           Disable warnings (default)\n"
              "  -h, --help            Print this message\n"
              "  -V, --version         Print the release version\n"
-             "  -R, --relative-crc    Emit section relative symbol CRCs\n"
 #else                          /* __GNU_LIBRARY__ */
              "  -s                    Select symbol prefix\n"
              "  -d                    Increment the debug level (repeatable)\n"
@@ -745,7 +740,6 @@ static void genksyms_usage(void)
              "  -q                    Disable warnings (default)\n"
              "  -h                    Print this message\n"
              "  -V                    Print the release version\n"
-             "  -R                    Emit section relative symbol CRCs\n"
 #endif                         /* __GNU_LIBRARY__ */
              , stderr);
 }
@@ -766,14 +760,13 @@ int main(int argc, char **argv)
                {"preserve", 0, 0, 'p'},
                {"version", 0, 0, 'V'},
                {"help", 0, 0, 'h'},
-               {"relative-crc", 0, 0, 'R'},
                {0, 0, 0, 0}
        };
 
-       while ((o = getopt_long(argc, argv, "s:dwqVDr:T:phR",
+       while ((o = getopt_long(argc, argv, "s:dwqVDr:T:ph",
                                &long_opts[0], NULL)) != EOF)
 #else                          /* __GNU_LIBRARY__ */
-       while ((o = getopt(argc, argv, "s:dwqVDr:T:phR")) != EOF)
+       while ((o = getopt(argc, argv, "s:dwqVDr:T:ph")) != EOF)
 #endif                         /* __GNU_LIBRARY__ */
                switch (o) {
                case 'd':
@@ -813,9 +806,6 @@ int main(int argc, char **argv)
                case 'h':
                        genksyms_usage();
                        return 0;
-               case 'R':
-                       flag_rel_crcs = 1;
-                       break;
                default:
                        genksyms_usage();
                        return 1;
index eceb3ee..db34dc3 100755 (executable)
@@ -90,7 +90,6 @@ modpost_link()
 
                if is_enabled CONFIG_MODVERSIONS; then
                        gen_symversions
-                       lds="${lds} -T .tmp_symversions.lds"
                fi
 
                # This might take a while, so indicate that we're doing
@@ -183,6 +182,10 @@ vmlinux_link()
                libs="${KBUILD_VMLINUX_LIBS}"
        fi
 
+       if is_enabled CONFIG_MODULES; then
+               objs="${objs} .vmlinux.export.o"
+       fi
+
        if [ "${SRCARCH}" = "um" ]; then
                wl=-Wl,
                ld="${CC}"
@@ -312,6 +315,7 @@ cleanup()
        rm -f vmlinux.o
        rm -f .vmlinux.d
        rm -f .vmlinux.objs
+       rm -f .vmlinux.export.c
 }
 
 # Use "make V=1" to debug this script
@@ -363,6 +367,10 @@ info GEN modules.builtin
 tr '\0' '\n' < modules.builtin.modinfo | sed -n 's/^[[:alnum:]:_]*\.file=//p' |
        tr ' ' '\n' | uniq | sed -e 's:^:kernel/:' -e 's/$/.ko/' > modules.builtin
 
+if is_enabled CONFIG_MODULES; then
+       ${MAKE} -f "${srctree}/scripts/Makefile.vmlinux" .vmlinux.export.o
+fi
+
 btf_vmlinux_bin_o=""
 if is_enabled CONFIG_DEBUG_INFO_BTF; then
        btf_vmlinux_bin_o=.btf.vmlinux.bin.o
index 1e29497..42e949c 100644 (file)
@@ -2234,6 +2234,7 @@ static void add_header(struct buffer *b, struct module *mod)
        buf_printf(b, "#define INCLUDE_VERMAGIC\n");
        buf_printf(b, "#include <linux/build-salt.h>\n");
        buf_printf(b, "#include <linux/elfnote-lto.h>\n");
+       buf_printf(b, "#include <linux/export-internal.h>\n");
        buf_printf(b, "#include <linux/vermagic.h>\n");
        buf_printf(b, "#include <linux/compiler.h>\n");
        buf_printf(b, "\n");
@@ -2268,20 +2269,26 @@ static void add_header(struct buffer *b, struct module *mod)
                buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
 }
 
-static void check_symversions(struct module *mod)
+static void add_exported_symbols(struct buffer *buf, struct module *mod)
 {
        struct symbol *sym;
 
        if (!modversions)
                return;
 
+       /* record CRCs for exported symbols */
+       buf_printf(buf, "\n");
        list_for_each_entry(sym, &mod->exported_symbols, list) {
                if (!sym->crc_valid) {
                        warn("EXPORT symbol \"%s\" [%s%s] version generation failed, symbol will not be versioned.\n"
                             "Is \"%s\" prototyped in <asm/asm-prototypes.h>?\n",
                             sym->name, mod->name, mod->is_vmlinux ? "" : ".ko",
                             sym->name);
+                       continue;
                }
+
+               buf_printf(buf, "SYMBOL_CRC(%s, 0x%08x, \"%s\");\n",
+                          sym->name, sym->crc, sym->is_gpl_only ? "_gpl" : "");
        }
 }
 
@@ -2418,6 +2425,18 @@ static void write_if_changed(struct buffer *b, const char *fname)
        write_buf(b, fname);
 }
 
+static void write_vmlinux_export_c_file(struct module *mod)
+{
+       struct buffer buf = { };
+
+       buf_printf(&buf,
+                  "#include <linux/export-internal.h>\n");
+
+       add_exported_symbols(&buf, mod);
+       write_if_changed(&buf, ".vmlinux.export.c");
+       free(buf.p);
+}
+
 /* do sanity checks, and generate *.mod.c file */
 static void write_mod_c_file(struct module *mod)
 {
@@ -2429,6 +2448,7 @@ static void write_mod_c_file(struct module *mod)
        check_exports(mod);
 
        add_header(&buf, mod);
+       add_exported_symbols(&buf, mod);
        add_versions(&buf, mod);
        add_depends(&buf, mod);
        add_moddevtable(&buf, mod);
@@ -2626,9 +2646,9 @@ int main(int argc, char **argv)
                if (mod->from_dump)
                        continue;
 
-               check_symversions(mod);
-
-               if (!mod->is_vmlinux)
+               if (mod->is_vmlinux)
+                       write_vmlinux_export_c_file(mod);
+               else
                        write_mod_c_file(mod);
        }