kbuild: lto: fix module versioning
authorSami Tolvanen <samitolvanen@google.com>
Fri, 11 Dec 2020 18:46:20 +0000 (10:46 -0800)
committerKees Cook <keescook@chromium.org>
Thu, 14 Jan 2021 16:21:08 +0000 (08:21 -0800)
With CONFIG_MODVERSIONS, version information is linked into each
compilation unit that exports symbols. With LTO, we cannot use this
method as all C code is compiled into LLVM bitcode instead. This
change collects symbol versions into .symversions files and merges
them in link-vmlinux.sh where they are all linked into vmlinux.o at
the same time.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20201211184633.3213045-4-samitolvanen@google.com
.gitignore
Makefile
arch/Kconfig
scripts/Makefile.build
scripts/Makefile.modpost
scripts/link-vmlinux.sh

index d01cda8..44e3499 100644 (file)
@@ -41,6 +41,7 @@
 *.so.dbg
 *.su
 *.symtypes
+*.symversions
 *.tab.[ch]
 *.tar
 *.xz
index 2b9bcec..173a7c9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1837,7 +1837,8 @@ clean: $(clean-dirs)
                -o -name '.tmp_*.o.*' \
                -o -name '*.c.[012]*.*' \
                -o -name '*.ll' \
-               -o -name '*.gcno' \) -type f -print | xargs rm -f
+               -o -name '*.gcno' \
+               -o -name '*.*.symversions' \) -type f -print | xargs rm -f
 
 # Generate tags for editors
 # ---------------------------------------------------------------------------
index 9494e39..4f2f045 100644 (file)
@@ -668,7 +668,6 @@ config HAS_LTO_CLANG
        depends on !FTRACE_MCOUNT_USE_RECORDMCOUNT
        depends on !KASAN
        depends on !GCOV_KERNEL
-       depends on !MODVERSIONS
        help
          The compiler and Kconfig options support building with Clang's
          LTO.
index 65d4bea..d94fc9a 100644 (file)
@@ -166,6 +166,15 @@ ifdef CONFIG_MODVERSIONS
 #   the actual value of the checksum generated by genksyms
 # o remove .tmp_<file>.o to <file>.o
 
+ifdef CONFIG_LTO_CLANG
+# Generate .o.symversions files for each .o with exported symbols, and link these
+# to the kernel and/or modules at the end.
+cmd_modversions_c =                                                            \
+       if $(NM) $@ 2>/dev/null | grep -q __ksymtab; then                       \
+               $(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes))  \
+                   > $@.symversions;                                           \
+       fi;
+else
 cmd_modversions_c =                                                            \
        if $(OBJDUMP) -h $@ | grep -q __ksymtab; then                           \
                $(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes))  \
@@ -177,6 +186,7 @@ cmd_modversions_c =                                                         \
                rm -f $(@D)/.tmp_$(@F:.o=.ver);                                 \
        fi
 endif
+endif
 
 ifdef CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
 # compiler will not generate __mcount_loc use recordmcount or recordmcount.pl
@@ -386,6 +396,18 @@ $(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
 $(subdir-builtin): $(obj)/%/built-in.a: $(obj)/% ;
 $(subdir-modorder): $(obj)/%/modules.order: $(obj)/% ;
 
+# combine symversions for later processing
+quiet_cmd_update_lto_symversions = SYMVER  $@
+ifeq ($(CONFIG_LTO_CLANG) $(CONFIG_MODVERSIONS),y y)
+      cmd_update_lto_symversions =                                     \
+       rm -f $@.symversions                                            \
+       $(foreach n, $(filter-out FORCE,$^),                            \
+               $(if $(wildcard $(n).symversions),                      \
+                       ; cat $(n).symversions >> $@.symversions))
+else
+      cmd_update_lto_symversions = echo >/dev/null
+endif
+
 #
 # Rule to compile a set of .o files into one .a file (without symbol table)
 #
@@ -393,8 +415,11 @@ $(subdir-modorder): $(obj)/%/modules.order: $(obj)/% ;
 quiet_cmd_ar_builtin = AR      $@
       cmd_ar_builtin = rm -f $@; $(AR) cDPrST $@ $(real-prereqs)
 
+quiet_cmd_ar_and_symver = AR      $@
+      cmd_ar_and_symver = $(cmd_update_lto_symversions); $(cmd_ar_builtin)
+
 $(obj)/built-in.a: $(real-obj-y) FORCE
-       $(call if_changed,ar_builtin)
+       $(call if_changed,ar_and_symver)
 
 #
 # Rule to create modules.order file
@@ -414,8 +439,11 @@ $(obj)/modules.order: $(obj-m) FORCE
 #
 # Rule to compile a set of .o files into one .a file (with symbol table)
 #
+quiet_cmd_ar_lib = AR      $@
+      cmd_ar_lib = $(cmd_update_lto_symversions); $(cmd_ar)
+
 $(obj)/lib.a: $(lib-y) FORCE
-       $(call if_changed,ar)
+       $(call if_changed,ar_lib)
 
 # NOTE:
 # Do not replace $(filter %.o,^) with $(real-prereqs). When a single object
@@ -424,6 +452,7 @@ $(obj)/lib.a: $(lib-y) FORCE
 ifdef CONFIG_LTO_CLANG
 quiet_cmd_link_multi-m = AR [M]  $@
 cmd_link_multi-m =                                             \
+       $(cmd_update_lto_symversions);                          \
        rm -f $@;                                               \
        $(AR) cDPrsT $@ $(filter %.o,$^)
 else
index 9ff8bfd..066beff 100644 (file)
@@ -111,7 +111,11 @@ ifdef CONFIG_LTO_CLANG
 prelink-ext := .lto
 
 quiet_cmd_cc_lto_link_modules = LTO [M] $@
-cmd_cc_lto_link_modules = $(LD) $(ld_flags) -r -o $@ --whole-archive $^
+cmd_cc_lto_link_modules =                                              \
+       $(LD) $(ld_flags) -r -o $@                                      \
+               $(shell [ -s $(@:.lto.o=.o.symversions) ] &&            \
+                       echo -T $(@:.lto.o=.o.symversions))             \
+               --whole-archive $^
 
 %.lto.o: %.o
        $(call if_changed,cc_lto_link_modules)
index 5965075..78e55fe 100755 (executable)
@@ -43,11 +43,26 @@ info()
        fi
 }
 
+# If CONFIG_LTO_CLANG is selected, collect generated symbol versions into
+# .tmp_symversions.lds
+gen_symversions()
+{
+       info GEN .tmp_symversions.lds
+       rm -f .tmp_symversions.lds
+
+       for o in ${KBUILD_VMLINUX_OBJS} ${KBUILD_VMLINUX_LIBS}; do
+               if [ -f ${o}.symversions ]; then
+                       cat ${o}.symversions >> .tmp_symversions.lds
+               fi
+       done
+}
+
 # Link of vmlinux.o used for section mismatch analysis
 # ${1} output file
 modpost_link()
 {
        local objects
+       local lds=""
 
        objects="--whole-archive                                \
                ${KBUILD_VMLINUX_OBJS}                          \
@@ -57,6 +72,11 @@ modpost_link()
                --end-group"
 
        if [ -n "${CONFIG_LTO_CLANG}" ]; then
+               if [ -n "${CONFIG_MODVERSIONS}" ]; then
+                       gen_symversions
+                       lds="${lds} -T .tmp_symversions.lds"
+               fi
+
                # This might take a while, so indicate that we're doing
                # an LTO link
                info LTO ${1}
@@ -64,7 +84,7 @@ modpost_link()
                info LD ${1}
        fi
 
-       ${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects}
+       ${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${lds} ${objects}
 }
 
 objtool_link()
@@ -242,6 +262,7 @@ cleanup()
 {
        rm -f .btf.*
        rm -f .tmp_System.map
+       rm -f .tmp_symversions.lds
        rm -f .tmp_vmlinux*
        rm -f System.map
        rm -f vmlinux