[PATCH v4 0/1] gcov: use -fprofile-update=prefer-atomic with compile-time guard

From: Konstantin Khorenko

Date: Mon May 11 2026 - 06:58:02 EST


This is v4 of the patch to add -fprofile-update=prefer-atomic to
CFLAGS_GCOV.

v2 was reported to cause link failures on some architecture/config
combinations because GCC emits calls to libatomic runtime functions
for 64-bit atomic counter increments, and the kernel does not link
against libatomic:

https://lore.kernel.org/all/ff2a4c49-463d-4d8a-9519-bb51308f7ba1@xxxxxxxxxxxxx/

Arnd Bergmann hit this with GCC-16 randconfig builds:

x86_64: undefined reference to `__atomic_fetch_add_8'
aarch64: undefined reference to `__aarch64_ldadd8_relax'

The kernel test robot confirmed the same on i386-allmodconfig with
GCC 14 (Debian):

https://lore.kernel.org/all/202605030611.mBKmkPOF-lkp@xxxxxxxxx/

v3 added a compile-time try-run check that determines whether
-fprofile-update=prefer-atomic is safe to use with the current
compiler and architecture.

=== Approach ===

The check compiles a minimal test program twice using the full
KBUILD_CFLAGS -- once without and once with -fprofile-update=prefer-atomic
-- then compares the undefined symbols in both resulting .o files using
nm. If prefer-atomic introduces any NEW undefined symbols, the flag is
not added.

Several alternative approaches were considered and rejected:

1) Grepping assembly output for known libatomic symbols
(__atomic_fetch_add, __aarch64_ldadd, etc):
Fragile -- requires maintaining a list of arch-specific symbol names.
New architectures or GCC versions may use different names.

2) Checking nm output for any undefined symbol beyond __gcov_*:
Fails because KBUILD_CFLAGS adds kernel-specific instrumentation
(__fentry__, __x86_return_thunk, etc) that creates "expected"
undefined symbols unrelated to libatomic.

3) Grepping only for "__atomic" in undefined symbols:
Misses aarch64 outline-atomics symbols (__aarch64_ldadd8_relax)
which do not contain "atomic" in their name.

4) Filtering KBUILD_CFLAGS to pass only -m32/-m64/-march=* to try-run:
Brittle whitelist -- misses flags like -mno-outline-atomics on arm64
and will break when new relevant flags are added.

The chosen diff-based approach is fully architecture-agnostic: it uses
the real KBUILD_CFLAGS, does not depend on knowing libatomic symbol
names, and will not break when new flags or architectures are added.
The only assumption is that -fprofile-update=prefer-atomic should not
introduce any new linker dependencies.

=== Testing ===

Verified on:
- x86_64, GCC 17.0.0 (trunk 2026-05-09): flag IS added, inline
lock addq for GCOV counters
- arm64 cross-compile, GCC 14.1.1 (aarch64-linux-gnu-gcc):
flag is NOT added (__aarch64_ldadd8_relax detected)

arm64 example showing the try-run detection in action:

$ echo 'long long x; void f(void){x++;}' | \
aarch64-linux-gnu-gcc [KBUILD_CFLAGS] -fprofile-arcs \
-ftest-coverage -c -o base.o
$ nm base.o | grep ' U '
U __gcov_exit
U __gcov_init
U __gcov_merge_add

$ echo 'long long x; void f(void){x++;}' | \
aarch64-linux-gnu-gcc [KBUILD_CFLAGS] -fprofile-arcs \
-ftest-coverage -fprofile-update=prefer-atomic -c -o test.o
$ nm test.o | grep ' U '
U __aarch64_ldadd8_relax <-- new, from libatomic
U __gcov_exit
U __gcov_init
U __gcov_merge_add

The undefined symbols differ => try-run fails => flag not added.

Changes since v3:
- Moved CFLAGS_GCOV block higher in the Makefile: after the core
KBUILD_CFLAGS assignments but before scripts/Makefile.gcc-plugins
include. Sashiko review of v3 correctly identified that placing
the try-run after the GCC plugins include would break on clean
builds when plugin .so files do not yet exist:
https://sashiko.dev/#/patchset/20260509142216.382205-2-khorenko%40virtuozzo.com

Changes since v2:
- Added try-run compile-time check (option 3 from Peter's proposal)
- Moved CFLAGS_GCOV definition after KBUILD_CFLAGS is finalized
- Split -fprofile-update=prefer-atomic from -fno-tree-loop-im

Konstantin Khorenko (1):
gcov: use atomic counter updates to fix concurrent access crashes

Makefile | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)

--
2.47.1