kbuild: lto: fix module versioning

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
This commit is contained in:
Sami Tolvanen 2020-12-11 10:46:20 -08:00 committed by Kees Cook
parent dc5723b02e
commit 38e8918490
6 changed files with 61 additions and 6 deletions

1
.gitignore vendored
View File

@ -41,6 +41,7 @@
*.so.dbg *.so.dbg
*.su *.su
*.symtypes *.symtypes
*.symversions
*.tab.[ch] *.tab.[ch]
*.tar *.tar
*.xz *.xz

View File

@ -1837,7 +1837,8 @@ clean: $(clean-dirs)
-o -name '.tmp_*.o.*' \ -o -name '.tmp_*.o.*' \
-o -name '*.c.[012]*.*' \ -o -name '*.c.[012]*.*' \
-o -name '*.ll' \ -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 # Generate tags for editors
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@ -668,7 +668,6 @@ config HAS_LTO_CLANG
depends on !FTRACE_MCOUNT_USE_RECORDMCOUNT depends on !FTRACE_MCOUNT_USE_RECORDMCOUNT
depends on !KASAN depends on !KASAN
depends on !GCOV_KERNEL depends on !GCOV_KERNEL
depends on !MODVERSIONS
help help
The compiler and Kconfig options support building with Clang's The compiler and Kconfig options support building with Clang's
LTO. LTO.

View File

@ -166,6 +166,15 @@ ifdef CONFIG_MODVERSIONS
# the actual value of the checksum generated by genksyms # the actual value of the checksum generated by genksyms
# o remove .tmp_<file>.o to <file>.o # 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 = \ cmd_modversions_c = \
if $(OBJDUMP) -h $@ | grep -q __ksymtab; then \ if $(OBJDUMP) -h $@ | grep -q __ksymtab; then \
$(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \ $(call cmd_gensymtypes_c,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
@ -177,6 +186,7 @@ cmd_modversions_c = \
rm -f $(@D)/.tmp_$(@F:.o=.ver); \ rm -f $(@D)/.tmp_$(@F:.o=.ver); \
fi fi
endif endif
endif
ifdef CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT ifdef CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
# compiler will not generate __mcount_loc use recordmcount or recordmcount.pl # 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-builtin): $(obj)/%/built-in.a: $(obj)/% ;
$(subdir-modorder): $(obj)/%/modules.order: $(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) # 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 $@ quiet_cmd_ar_builtin = AR $@
cmd_ar_builtin = rm -f $@; $(AR) cDPrST $@ $(real-prereqs) 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 $(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 # 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) # 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 $(obj)/lib.a: $(lib-y) FORCE
$(call if_changed,ar) $(call if_changed,ar_lib)
# NOTE: # NOTE:
# Do not replace $(filter %.o,^) with $(real-prereqs). When a single object # 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 ifdef CONFIG_LTO_CLANG
quiet_cmd_link_multi-m = AR [M] $@ quiet_cmd_link_multi-m = AR [M] $@
cmd_link_multi-m = \ cmd_link_multi-m = \
$(cmd_update_lto_symversions); \
rm -f $@; \ rm -f $@; \
$(AR) cDPrsT $@ $(filter %.o,$^) $(AR) cDPrsT $@ $(filter %.o,$^)
else else

View File

@ -111,7 +111,11 @@ ifdef CONFIG_LTO_CLANG
prelink-ext := .lto prelink-ext := .lto
quiet_cmd_cc_lto_link_modules = LTO [M] $@ 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 %.lto.o: %.o
$(call if_changed,cc_lto_link_modules) $(call if_changed,cc_lto_link_modules)

View File

@ -43,11 +43,26 @@ info()
fi 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 # Link of vmlinux.o used for section mismatch analysis
# ${1} output file # ${1} output file
modpost_link() modpost_link()
{ {
local objects local objects
local lds=""
objects="--whole-archive \ objects="--whole-archive \
${KBUILD_VMLINUX_OBJS} \ ${KBUILD_VMLINUX_OBJS} \
@ -57,6 +72,11 @@ modpost_link()
--end-group" --end-group"
if [ -n "${CONFIG_LTO_CLANG}" ]; then 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 # This might take a while, so indicate that we're doing
# an LTO link # an LTO link
info LTO ${1} info LTO ${1}
@ -64,7 +84,7 @@ modpost_link()
info LD ${1} info LD ${1}
fi fi
${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${objects} ${LD} ${KBUILD_LDFLAGS} -r -o ${1} ${lds} ${objects}
} }
objtool_link() objtool_link()
@ -242,6 +262,7 @@ cleanup()
{ {
rm -f .btf.* rm -f .btf.*
rm -f .tmp_System.map rm -f .tmp_System.map
rm -f .tmp_symversions.lds
rm -f .tmp_vmlinux* rm -f .tmp_vmlinux*
rm -f System.map rm -f System.map
rm -f vmlinux rm -f vmlinux