https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124811

            Bug ID: 124811
           Summary: Non-deterministic DWARF debug info (.debug_info,
                    .debug_loclists) when using precompiled headers
           Product: gcc
           Version: 15.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: pch
          Assignee: unassigned at gcc dot gnu.org
          Reporter: michael at micl dot dev
  Target Milestone: ---

Building the same C++ project twice with identical flags, environment,
and parallelism on x86_64-linux-gnu produces .o files that differ in
.debug_info and .debug_loclists when precompiled headers are in use.
The .text, .rodata, .eh_frame, and all other code/data sections remain
byte-for-byte identical. Only debug sections differ.

The .gch files themselves are non-deterministic between runs, but this
does not always cause .o files to differ, compiling the same source
twice against two different .gch builds can still produce identical .o
files. However, in a full project build with PCH, some .o files end up
with different debug info, and the set of affected files varies each
run.

Compiling without PCH is deterministic. Recompiling the same source
file against the same .gch is also deterministic. The non-determinism
only manifests in .o files produced during a full build that uses PCH.

This breaks reproducible builds because the build-id is computed over
the full unstripped binary including debug sections. The differing
debug info cascades through .o files into the final linked binary's
build-id, making stripped binaries non-reproducible even though the
executable code is identical.

Observed debug section size differences (two-build comparison):
  .debug_info:     0x34eaf8c vs 0x34eb0aa (+286 bytes)
  .debug_loclists: 0x30c72c  vs 0x30c729  (-3 bytes)

Steps to reproduce:

Build quickshell 0.2.1 twice from a clean state with identical
flags and environment, then compare the resulting object files.
A number of .o files will differ in their debug sections, and the
set of affected files varies each run.

Build steps (Debian unstable, requires build-deps for quickshell):

  git clone https://git.outfoxxed.me/quickshell/quickshell
  cd quickshell && git checkout v0.2.1
  mkdir build && cd build
  cmake .. -GNinja \
      -DCMAKE_BUILD_TYPE=RelWithDebInfo \
      -DCMAKE_INSTALL_PREFIX=/usr \
      -DDISTRIBUTOR="Debian" \
      -DDISTRIBUTOR_DEBUGINFO_AVAILABLE=YES \
      -DCRASH_REPORTER=OFF
  ninja -j8

Clean, build and repeat, then compare .o files between builds.

The project uses cmake's target_precompile_headers() with
REUSE_FROM, compiling Qt6 headers (QtCore, QtGui, QtQuick, etc.)
into several .gch files with -std=gnu++20 -O2 -g.

I have not been able to reproduce this with a small standalone test
case. The non-determinism appears to require a large enough set of
headers in the PCH and multiple translation units consuming it.


Tested with:

GCC 15.2.0 (Debian 15.2.0-15):
  31 out of 475 .o files differ (-j8). The set varies each run.

GCC 15.2.0 (Debian 15.2.0-16, git 20260321 from gcc-15 branch):
  6 out of 475 .o files differ (-j2). The set varies each run.

GCC 16.0.1 20260322 (trunk r16-8246-g569ace1fa50):
  33 out of 475 .o files differ.

gcc -v output (GCC 15):
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/15/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 15.2.0-15'
--with-bugurl=file:///usr/share/doc/gcc-15/README.Bugs
--enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2,rust,cobol,algol68
--prefix=/usr --with-gcc-major-version-only --program-suffix=-15
--program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id
--libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix
--libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu
--enable-libstdcxx-debug --enable-libstdcxx-time=yes
--with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace
--enable-gnu-unique-object --disable-vtable-verify --enable-plugin
--enable-default-pie --with-system-zlib --enable-libphobos-checking=release
--with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch
--disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64
--with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic
--enable-offload-targets=nvptx-none=/build/reproducible-path/gcc-15-15.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/reproducible-path/gcc-15-15.2.0/debian/tmp-gcn/usr
--enable-offload-defaulted --without-cuda-driver --enable-checking=release
--build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
--with-build-config=bootstrap-lto-lean --enable-link-serialization=3
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 15.2.0 (Debian 15.2.0-15)

Other notes:

This may be related to bug 111034 (and its predecessor bug 92717),
which identified that .gch files are GCC memory dumps affected by ASLR.
However, this report is against GCC 15/16 (not 13), affects only debug
sections while code remains deterministic, and the number of affected
files varies with parallelism level.

The Debian bug tracking this is https://bugs.debian.org/1131840

Reply via email to