Re: [PATCH] kbuild: try readelf first in gen_symversions
From: Nathan Chancellor
Date: Fri Jun 05 2026 - 02:28:44 EST
On Thu, Jun 04, 2026 at 11:44:29AM +0800, Wentao Guan wrote:
> Hello,
>
> > On Thu, Jun 04, 2026 at 12:17:32AM +0800, Wentao Guan wrote:
> > > Use readelf to dig out if <file>.o contain a __export_symbol_*.
> > >
> > > Instead of nm, readelf is more faster, and significantly improve speed
> > > when enable CONFIG_MODVERSIONS.
> > >
> > > Build x86_64_defconfigs in 2C4T cloud server with CONFIG_MODVERSIONS=y:
> > > With patch:
> > > real 17m21.019s
> > > user 61m48.388s
> > > sys 4m27.709s
> > > Without patch:
> > > real 17m39.435s
> > > user 62m24.686s
> > > sys 5m3.200s
> > >
> > > Link: https://lore.kernel.org/all/tencent_2FA16E0A18D6D0C0703F5D49@xxxxxx/
> > > Signed-off-by: Wentao Guan <guanwentao@xxxxxxxxxxxxx>
> > > ---
> > > scripts/Makefile.build | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> > > index 3498d25b15e85..54a91bc144cce 100644
> > > --- a/scripts/Makefile.build
> > > +++ b/scripts/Makefile.build
> > > @@ -233,7 +233,7 @@ ifdef CONFIG_MODVERSIONS
> > > # be compiled and linked to the kernel and/or modules.
> > >
> > > gen_symversions = \
> > > - if $(NM) $@ 2>/dev/null | grep -q ' __export_symbol_'; then \
> > > + if $(READELF) -sW $@ 2>/dev/null | grep -q ' __export_symbol_'; then \
> >
> > This breaks modversioning for Clang LTO builds, as llvm-nm can read LLVM
> > bitcode but llvm-readelf cannot, it expects strictly ELF.
> Oh, is it worth to use the following logic to detect LLVM or LLVM-LTO or not ?
> +ifeq ($(LLVM),)
This should probably be CONFIG_LTO_CLANG with flipped branches but...
> + SYM_CHECK = $(READELF) -sW
> +else
> + SYM_CHECK = $(NM)
> +endif
> gen_symversions = \
> - if $(NM) $@ 2>/dev/null | grep -q ' __export_symbol_'; then \
> + if $(SYM_CHECK) $@ 2>/dev/null | grep -q ' __export_symbol_'; then \
>
> > that it stops looking for a match after the first export symbol is
> > found?
> Small, there are my test result in make x86_64_defconfig + enable CONFIG_MODVERSIONS:
> 1. readelf
> if $(READELF) $@ 2>/dev/null | grep -q ' __export_symbol_';
> real 10m44.359s
> user 37m43.596s
> sys 3m2.424s
> 2. nm
> if $(NM) $@ 2>/dev/null | grep -q ' __export_symbol_';
> real 11m8.008s
> user 38m51.644s
> sys 3m29.798s
> 3. nm + grep -m1 -q
> if $(NM) $@ 2>/dev/null | grep -m1 -q ' __export_symbol_';
> real 10m56.891s
> user 38m8.136s
> sys 3m28.096s
'-m1' appears to get us 50% (12s) of the speed up of 'readelf' (24s) in
your environment while sticking with 'nm'. I would be more inclined to
take that change since it is small and correct, rather than switching on
NM or READELF, as I don't think it is worth the additional complexity.
FWIW, on one of my test machines with 8 cores and 16 threads, the
difference is much less noticeable. I think that is going to be in line
with most developer and build farm hardware, rather than a 2C/4T machine
like you mention in the initial commit message.
GCC 16.1.0 + binutils 2.46:
Benchmark 1: $(NM)
Time (mean ± σ): 75.203 s ± 0.283 s [User: 659.465 s, System: 185.605 s]
Range (min … max): 74.898 s … 75.457 s 3 runs
Benchmark 2: $(READELF) -sW
Time (mean ± σ): 73.055 s ± 0.465 s [User: 642.365 s, System: 175.908 s]
Range (min … max): 72.523 s … 73.385 s 3 runs
Summary
$(READELF) -sW ran
1.03 ± 0.01 times faster than $(NM)
LLVM 22:
Benchmark 1: $(NM)
Time (mean ± σ): 75.030 s ± 0.736 s [User: 659.603 s, System: 185.257 s]
Range (min … max): 74.207 s … 75.623 s 3 runs
Benchmark 2: $(READELF) -sW
Time (mean ± σ): 73.405 s ± 0.457 s [User: 642.512 s, System: 176.440 s]
Range (min … max): 72.878 s … 73.679 s 3 runs
Summary
$(READELF) -sW ran
1.02 ± 0.01 times faster than $(NM)
--
Cheers,
Nathan