Commit 5e9e95cc authored by Masahiro Yamada's avatar Masahiro Yamada
Browse files

kbuild: implement CONFIG_TRIM_UNUSED_KSYMS without recursion

When CONFIG_TRIM_UNUSED_KSYMS is enabled, Kbuild recursively traverses
the directory tree to determine which EXPORT_SYMBOL to trim. If an
EXPORT_SYMBOL turns out to be unused by anyone, Kbuild begins the
second traverse, where some source files are recompiled with their
EXPORT_SYMBOL() tuned into a no-op.

Linus stated negative opinions about this slowness in commits:

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

We can do this better now. The final data structures of EXPORT_SYMBOL
are generated by the modpost stage, so modpost can selectively emit
KSYMTAB entries that are really used by modules.

Commit f73edc89 ("kbuild: unify two modpost invocations") is another
ground-work to do this in a one-pass algorithm. With the list of modules,
modpost sets sym->used if it is used by a module. modpost emits KSYMTAB
only for symbols with sym->used==true.

BTW, 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 I guess
that is unlikely to happen.

Signed-off-by: default avatarMasahiro Yamada <masahiroy@kernel.org>
parent 700c48b4
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@
*.symversions
*.tab.[ch]
*.tar
*.usyms
*.xz
*.zst
Module.symvers
@@ -112,7 +111,6 @@ modules.order
#
/include/config/
/include/generated/
/include/ksym/
/arch/*/include/generated/

# stgit generated dirs
+3 −19
Original line number Diff line number Diff line
@@ -1193,28 +1193,12 @@ endif
export KBUILD_VMLINUX_LIBS
export KBUILD_LDS          := arch/$(SRCARCH)/kernel/vmlinux.lds

# Recurse until adjust_autoksyms.sh is satisfied
PHONY += autoksyms_recursive
ifdef CONFIG_TRIM_UNUSED_KSYMS
# For the kernel to actually contain only the needed exported symbols,
# we have to build modules as well to determine what those symbols are.
# (this can be evaluated only once include/config/auto.conf has been included)
KBUILD_MODULES := 1

autoksyms_recursive: $(build-dir) modules.order
	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh \
	  "$(MAKE) -f $(srctree)/Makefile autoksyms_recursive"
endif

autoksyms_h := $(if $(CONFIG_TRIM_UNUSED_KSYMS), include/generated/autoksyms.h)

quiet_cmd_autoksyms_h = GEN     $@
      cmd_autoksyms_h = mkdir -p $(dir $@); \
			$(CONFIG_SHELL) $(srctree)/scripts/gen_autoksyms.sh $@

$(autoksyms_h):
	$(call cmd,autoksyms_h)

# '$(AR) mPi' needs 'T' to workaround the bug of llvm-ar <= 14
quiet_cmd_ar_vmlinux.a = AR      $@
      cmd_ar_vmlinux.a = \
@@ -1223,7 +1207,7 @@ quiet_cmd_ar_vmlinux.a = AR $@
	$(AR) mPiT $$($(AR) t $@ | sed -n 1p) $@ $$($(AR) t $@ | grep -F -f $(srctree)/scripts/head-object-list.txt)

targets += vmlinux.a
vmlinux.a: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt autoksyms_recursive FORCE
vmlinux.a: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt FORCE
	$(call if_changed,ar_vmlinux.a)

PHONY += vmlinux_o
@@ -1279,7 +1263,7 @@ scripts: scripts_basic scripts_dtc
PHONY += prepare archprepare

archprepare: outputmakefile archheaders archscripts scripts include/config/kernel.release \
	asm-generic $(version_h) $(autoksyms_h) include/generated/utsrelease.h \
	asm-generic $(version_h) include/generated/utsrelease.h \
	include/generated/compile.h include/generated/autoconf.h remove-stale-files

prepare0: archprepare
@@ -2039,7 +2023,7 @@ clean: $(clean-dirs)
		-o -name '*.dtb.S' -o -name '*.dtbo.S' \
		-o -name '*.dt.yaml' \
		-o -name '*.dwo' -o -name '*.lst' \
		-o -name '*.su' -o -name '*.mod' -o -name '*.usyms' \
		-o -name '*.su' -o -name '*.mod' \
		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
		-o -name '*.lex.c' -o -name '*.tab.[ch]' \
		-o -name '*.asn1.[ch]' \
+10 −57
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ extern struct module __this_module;
	.long sym
#endif

#define ____EXPORT_SYMBOL(sym, license, ns)		\
#define ___EXPORT_SYMBOL(sym, license, ns)		\
	.section ".export_symbol","a"		ASM_NL	\
	__export_symbol_##sym:			ASM_NL	\
		.asciz license			ASM_NL	\
@@ -50,24 +50,6 @@ extern struct module __this_module;
		__EXPORT_SYMBOL_REF(sym)	ASM_NL	\
	.previous

#ifdef __GENKSYMS__

#define ___EXPORT_SYMBOL(sym, sec, ns)	__GENKSYMS_EXPORT_SYMBOL(sym)

#elif defined(__ASSEMBLY__)

#define ___EXPORT_SYMBOL(sym, license, ns) \
	____EXPORT_SYMBOL(sym, license, ns)

#else

#define ___EXPORT_SYMBOL(sym, license, ns)			\
	extern typeof(sym) sym;					\
	__ADDRESSABLE(sym)					\
	asm(__stringify(____EXPORT_SYMBOL(sym, license, ns)))

#endif

#if !defined(CONFIG_MODULES) || defined(__DISABLE_EXPORTS)

/*
@@ -77,50 +59,21 @@ extern struct module __this_module;
 */
#define __EXPORT_SYMBOL(sym, sec, ns)

#elif defined(CONFIG_TRIM_UNUSED_KSYMS)
#elif defined(__GENKSYMS__)

#include <generated/autoksyms.h>
#define __EXPORT_SYMBOL(sym, sec, ns)	__GENKSYMS_EXPORT_SYMBOL(sym)

/*
 * For fine grained build dependencies, we want to tell the build system
 * about each possible exported symbol even if they're not actually exported.
 * We use a symbol pattern __ksym_marker_<symbol> that the build system filters
 * 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
#elif defined(__ASSEMBLY__)

#endif
#define __EXPORT_SYMBOL(sym, license, ns) \
	___EXPORT_SYMBOL(sym, license, ns)

#define __EXPORT_SYMBOL(sym, sec, ns)					\
	__ksym_marker(sym);						\
	__cond_export_sym(sym, sec, ns, __is_defined(__KSYM_##sym))
#define __cond_export_sym(sym, sec, ns, conf)				\
	___cond_export_sym(sym, sec, ns, conf)
#define ___cond_export_sym(sym, sec, ns, enabled)			\
	__cond_export_sym_##enabled(sym, sec, ns)
#define __cond_export_sym_1(sym, sec, ns) ___EXPORT_SYMBOL(sym, sec, ns)

#ifdef __GENKSYMS__
#define __cond_export_sym_0(sym, sec, ns) __GENKSYMS_EXPORT_SYMBOL(sym)
#else
#define __cond_export_sym_0(sym, sec, ns) /* nothing */
#endif

#else

#define __EXPORT_SYMBOL(sym, sec, ns)	___EXPORT_SYMBOL(sym, sec, ns)
#define __EXPORT_SYMBOL(sym, license, ns)			\
	extern typeof(sym) sym;					\
	__ADDRESSABLE(sym)					\
	asm(__stringify(___EXPORT_SYMBOL(sym, license, ns)))

#endif /* CONFIG_MODULES */

+1 −14
Original line number Diff line number Diff line
@@ -82,7 +82,7 @@ ifdef need-builtin
targets-for-builtin += $(obj)/built-in.a
endif

targets-for-modules := $(foreach x, o mod $(if $(CONFIG_TRIM_UNUSED_KSYMS), usyms), \
targets-for-modules := $(foreach x, o mod, \
				$(patsubst %.o, %.$x, $(filter %.o, $(obj-m))))

ifdef need-modorder
@@ -217,18 +217,12 @@ is-standard-object = $(if $(filter-out y%, $(OBJECT_FILES_NON_STANDARD_$(basetar

$(obj)/%.o: objtool-enabled = $(if $(is-standard-object),$(if $(delay-objtool),$(is-single-obj-m),y))

ifdef CONFIG_TRIM_UNUSED_KSYMS
cmd_gen_ksymdeps = \
	$(CONFIG_SHELL) $(srctree)/scripts/gen_ksymdeps.sh $@ >> $(dot-target).cmd
endif

ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),)
cmd_warn_shared_object = $(if $(word 2, $(modname-multi)),$(warning $(kbuild-file): $*.o is added to multiple modules: $(modname-multi)))
endif

define rule_cc_o_c
	$(call cmd_and_fixdep,cc_o_c)
	$(call cmd,gen_ksymdeps)
	$(call cmd,checksrc)
	$(call cmd,checkdoc)
	$(call cmd,gen_objtooldep)
@@ -239,7 +233,6 @@ endef

define rule_as_o_S
	$(call cmd_and_fixdep,as_o_S)
	$(call cmd,gen_ksymdeps)
	$(call cmd,gen_objtooldep)
	$(call cmd,gen_symversions_S)
	$(call cmd,warn_shared_object)
@@ -258,12 +251,6 @@ cmd_mod = printf '%s\n' $(call real-search, $*.o, .o, -objs -y -m) | \
$(obj)/%.mod: FORCE
	$(call if_changed,mod)

# List module undefined symbols
cmd_undefined_syms = $(NM) $< | sed -n 's/^  *U //p' > $@

$(obj)/%.usyms: $(obj)/%.o FORCE
	$(call if_changed,undefined_syms)

quiet_cmd_cc_lst_c = MKLST   $@
      cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && \
		     $(CONFIG_SHELL) $(srctree)/scripts/makelst $*.o \
+7 −0
Original line number Diff line number Diff line
@@ -91,6 +91,13 @@ targets += .vmlinux.objs
.vmlinux.objs: vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
	$(call if_changed,vmlinux_objs)

ifdef CONFIG_TRIM_UNUSED_KSYMS
ksym-wl := $(CONFIG_UNUSED_KSYMS_WHITELIST)
ksym-wl := $(if $(filter-out /%, $(ksym-wl)),$(srctree)/)$(ksym-wl)
modpost-args += -t $(addprefix -u , $(ksym-wl))
modpost-deps += $(ksym-wl)
endif

ifeq ($(wildcard vmlinux.o),)
missing-input := vmlinux.o
output-symdump := modules-only.symvers
Loading