Fedora Account System
Red Hat Associate
Red Hat Customer
The GCC 16 update has created a FTBFS problem in QEMU's static PIE builds related to libatomic.a This example build log shows it https://kojipkgs.fedoraproject.org//work/tasks/5317/140855317/build.log /usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/16/libatomic.a(cas_16_.o): relocation R_X86_64_32 against hidden symbol `libat_compare_exchange_16_i1' can not be used when making a PIE object /usr/bin/ld: failed to set dynamic section sizes: bad value collect2: error: ld returned 1 exit status At first I thought this was caused by glib2 introducing an explicit '-latomic' in its pkg-config file Libs.private as that in turn added /usr/lib/gcc/x86_64-redhat-linux/16/libatomic.a to the gcc command line QEMU invokes, but I think that's a red herring. Bisecting the command line I got it reduced to this fairly minimal reproducer: gcc -o qemu-aarch64 libuser.a.p/accel_tcg_user-exec.c.o -static-pie /usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/16/libatomic.a(cas_16_.o): relocation R_X86_64_32 against hidden symbol `libat_compare_exchange_16_i1' can not be used when making a PIE object /usr/bin/ld: failed to set dynamic section sizes: bad value collect2: error: ld returned 1 exit status Adding '--verbose' to GCC I see GCC itself adding -latomic to COLLECT_GCC_OPTIONS. I'm still unclear why there's a link issue though. The existence of libat_compare_exchange_16_i1 doesn't seem to change in libatomic.a between GCC 15 and 16 and I don't see a reference to 'compare_exchange' in the dissasembly of libuser.a.p/accel_tcg_user-exec.c.o - it seems like the mere addition of libatomic.a by GCC is perhaps causing the PIE relocation problem . Reproducible: Always Steps to Reproduce: 1. Attempt to build a static QEMU user binary as done by qemu.spec $ dnf builddep qemu $ ./configure --cc=gcc --cxx=/bin/false --prefix=/usr --libdir=/usr/lib64 --datadir=/usr/share --sysconfdir=/etc --interp-prefix=/usr/qemu-%M --localstatedir=/var --docdir=/usr/share/doc --libexecdir=/usr/libexec '--extra-ldflags=-Wl,-z,relro -Wl,--as-needed -Wl,-z,pack-relative-relocs -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-hardened-ld-errors -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,--build-id=sha1 -specs=/usr/lib/rpm/redhat/redhat-package-notes ' '--extra-cflags=-O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Wno-complain-wrong-lang -Werror=format-security -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -mtls-dialect=gnu2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer' --with-pkgversion=qemu-10.2.0-0.3.rc4.fc44 --with-suffix=qemu --firmwarepath=/usr/share/qemu-firmware:/usr/share/ipxe/qemu:/usr/share/seavgabios:/usr/share/seabios --enable-trace-backends=dtrace --with-coroutine=ucontext --tls-priority=@QEMU,SYSTEM --audio-drv-list= --disable-af-xdp --disable-alsa --disable-asan --disable-attr --disable-auth-pam --disable-blkio --disable-block-drv-whitelist-in-tools --disable-bochs --disable-bpf --disable-brlapi --disable-bsd-user --disable-bzip2 --disable-cap-ng --disable-capstone --disable-cfi --disable-cfi-debug --disable-cloop --disable-cocoa --disable-colo-proxy --disable-coreaudio --disable-coroutine-pool --disable-crypto-afalg --disable-curl --disable-curses --disable-dbus-display --disable-debug-graph-lock --disable-debug-info --disable-debug-mutex --disable-debug-remap --disable-debug-tcg --disable-dmg --disable-docs --disable-download --disable-dsound --disable-fdt --disable-fuse --disable-fuse-lseek --disable-gcrypt --disable-gettext --disable-gio --disable-glusterfs --disable-gnutls --disable-gtk --disable-gtk-clipboard --disable-guest-agent --disable-guest-agent-msi --disable-hv-balloon --disable-hvf --disable-iconv --disable-igvm --disable-jack --disable-kvm --disable-l2tpv3 --disable-libcbor --disable-libdaxctl --disable-libdw --disable-libkeyutils --disable-libiscsi --disable-libnfs --disable-libpmem --disable-libssh --disable-libudev --disable-libusb --disable-linux-aio --disable-linux-io-uring --disable-linux-user --disable-lto --disable-lzfse --disable-lzo --disable-malloc-trim --disable-membarrier --disable-modules --disable-module-upgrades --disable-mpath --disable-mshv --disable-multiprocess --disable-netmap --disable-nettle --disable-numa --disable-nvmm --disable-opengl --disable-oss --disable-pa --disable-parallels --disable-passt --disable-pie --disable-pipewire --disable-pixman --disable-plugins --disable-pvg --disable-qcow1 --disable-qed --disable-qom-cast-debug --disable-qpl --disable-rbd --disable-rdma --disable-relocatable --disable-replication --disable-rust --disable-rutabaga-gfx --disable-rng-none --disable-safe-stack --disable-sdl --disable-sdl-image --disable-seccomp --disable-selinux --disable-slirp --disable-slirp-smbd --disable-smartcard --disable-snappy --disable-sndio --disable-sparse --disable-spice --disable-spice-protocol --disable-strict-rust-lints --disable-strip --disable-system --disable-tcg --disable-tools --disable-tpm --disable-tsan --disable-uadk --disable-u2f --disable-ubsan --disable-usb-redir --disable-user --disable-valgrind --disable-vpc --disable-vde --disable-vdi --disable-vfio-user-server --disable-vhdx --disable-vhost-crypto --disable-vhost-kernel --disable-vhost-net --disable-vhost-user --disable-vhost-user-blk-server --disable-vhost-vdpa --disable-virglrenderer --disable-virtfs --disable-vnc --disable-vnc-jpeg --disable-png --disable-vnc-sasl --disable-vte --disable-vvfat --disable-werror --disable-whpx --disable-xen --disable-xen-pci-passthrough --disable-xkbcommon --disable-zstd --without-default-devices --enable-attr --enable-linux-user --enable-pie --enable-tcg --disable-install-blobs --static $ make -j 20 Actual Results: Failure with /usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/16/libatomic.a(cas_16_.o): relocation R_X86_64_32 against hidden symbol `libat_compare_exchange_16_i1' can not be used when making a PIE object Expected Results: Build succeeds
Koji log showing RPM versions - tldr: gcc-16.0.0-0.4.fc44 https://kojipkgs.fedoraproject.org//work/tasks/5317/140855317/root.log
GCC since https://gcc.gnu.org/PR81358 indeed adds --push-state --as-needed -latomic --pop-state to the link line from the gcc/g++ etc. drivers, if -fno-link-libatomic option isn't used. And indeed this causes some issues with libtool which ignores the --push-state --as-needed and --pop-state wrappers around the -latomic option and so in some cases can link -latomic even when not really needed. Does qemu actually need -latomic? From the word about it previously buildrequiring libatomic-static I'd guess it does. But in that case I have no idea what further things changed. libatomic.a itself has been compiled with -fPIC both in GCC 15 and 16.
QEMU added a BR on libatomic-static due to glib2 introducing a dep on libatomic - this was redundant in QEMU though, because glib2-static already had a Requires: libatomic-static dep. I'm unclear if QEMU explicitly needs -latomic or not - we've never directly added that ourselves in QEMU, but we use various atomic functions ? Passing the new -fno-link-libatomic flag to GCC avoids the QEMU build errors, but that still leaves the question of why linking to libatomic.a is broken to begin with in GCC 16 ? With GCC 15 if I explicitly added /usr/lib/gcc/x86_64-redhat-linux/15/libatomic.a to QEMU's linker args, we saw no PIE failures.
Actually without -fPIC for both GCC 15 and 16, only libatomic.so.6 has been built with -fPIC.
And it seems it never worked even in GCC 15: gcc --version gcc (GCC) 15.2.1 20251022 (Red Hat 15.2.1-3) Copyright (C) 2025 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. gcc -static-pie -fpie -o a a.c -Wl,--whole-archive -latomic -Wl,--no-whole-archive /usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/15/libatomic.a(gload.o): relocation R_X86_64_32S against `.rodata' can not be used when making a PIE object; recompile with -fPIE /usr/bin/ld: failed to set dynamic section sizes: bad value collect2: error: ld returned 1 exit status
I've cut down the QEMU code until I found a minimal reproducer: $ cat a.c __int128_t demo(__int128_t cmpv, __int128_t newv) { __int128_t *ptr = 0x0; (void)__atomic_compare_exchange_n(ptr, &cmpv, newv, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return cmpv; } int main() { demo(1, 1); return 0; } Works GCC 16 with static build: $ gcc --static ../accel/tcg/a.c Fails GCC 16 with static PIE build: $ gcc --static-pie ../accel/tcg/a.c /usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/16/libatomic.a(cas_16_.o): relocation R_X86_64_32 against hidden symbol `libat_compare_exchange_16_i1' can not be used when making a PIE object /usr/bin/ld: failed to set dynamic section sizes: bad value collect2: error: ld returned 1 exit status This example also fails with GCC 15, so looks like a pre-existing bug in libatomic.a before GCC 16 too. The reason why QEMU builds OK with GCC 15 is that it does NOT use __atomic_compare_exchange_n() with int128, as its configure script detects that as not supported, so QEMU did a fallback codepath using __sync_val_compare_and_swap_16 instead.
Based on the comment in include/qemu/atomic128.h you need to change the configure checks, because GCC 16's new behaviour of linking to libatomic automatically makes the check misbehave. The check thinks it can use __atomic_compare_exchange_n without involving libatomic, but actually it's just getting a libatomic call that no longer causes a linker error.
(In reply to Jonathan Wakely from comment #7) > Based on the comment in include/qemu/atomic128.h you need to change the > configure checks, because GCC 16's new behaviour of linking to libatomic > automatically makes the check misbehave. The check thinks it can use > __atomic_compare_exchange_n without involving libatomic, but actually it's > just getting a libatomic call that no longer causes a linker error. Yeah, so we're back at needing to modify qemu's meson.buld script to always add -fno-link-libatomic, if GCC supports that arg. I'm still surprised libatomic.a breaks with -static-pie for int128 though. If that's a valid bug for GCC to fix then this bug could track it, otherwise feel free to close this and I'll fix QEMU's check regardless
The reason for __atomic_compare_exchange_16 not being supported inline is that while CAS works, for __atomic_* one needs also working atomic load and because the loaded address can be also in .rodata section, CAS loop can't be used for that. And only recently Intel/AMD guaranteed in their docs that for AVX and higher vmovaps load is atomic if the address is 16-byte aligned, a few other chip makers did too but nobody from VIA said anything. So libatomic is used instead and has an IFUNC which selects hw instructions for AVX on Intel/AMD.
While libgcc.a is built with -fPIE, I think libstdc++.a is too, most other *.a libraries are not and so are not supported in -static-pie.
For the record, I've sent the following fix upstream to QEMU https://lists.nongnu.org/archive/html/qemu-devel/2026-01/msg01140.html
> most other *.a libraries are not and so are not supported in -static-pie. Should libatomic should be built with -fPIE, since it's added automatically (even if within as-needed)? For QEMU it's anyway the right thing to add -fno-link-libatomic, but still.