Commit Graph

9 Commits

Author SHA1 Message Date
John Ericson
2a1d427bd9 gccNGPackages: Do not use vendored libbacktrace
- `gccNGPackages.libbacktrace`: Remove, in favor of the `libbacktrace`
  package in `pkgs/by-name`

- `stdenvNoCxx`: New, a compiler with a libc but no C++ standard library

- `libbacktrace`: Build with `stdenvNoCxx`, so `libstdc++` can link it
  without a cycle

- `gccNGPackages.gcc`, `gccNGPackages.libgfortran`,
  `gccNGPackages.libstdcxx`: Take `libbacktrace` as a build input and pass
  `--with-system-libbacktrace`

That option is not upstream; it comes from a patch posted to gcc-patches[^1],
which we fetch from the archive rather than vendor.

One posting covers every component that links libbacktrace, so each
package filters it down to the files its own `src` carries, and leaves
out the generated files because we regenerate those locally anyway.

`gcc` and `libgfortran` were already using this `libbacktrace`, but by
faking up the sibling directory an in-tree build would have had --
archive, libtool archive and headers -- for the relative paths to resolve
against. Passing the flag lets that go.

Note that this means our libstdc++ is getting `std::stacktrace` support
for the first time; we were unwittingly excluding the vendored support.
Nothing failed, which is why it went unnoticed:
`--enable-libstdcxx-backtrace` defaults to `auto`, and `auto` means yes
wherever the library is hosted, so the feature was asked for. But the
probe that picks the object format runs `libbacktrace/filetype.awk`,
which was not among the files we copied, so it got nothing back, warned
"could not determine output file type", and quietly settled on
`enable_libstdcxx_backtrace=no`.

Note also that with this approach, a downstream `libbacktrace`
*cannot* be built by the regular `stdenv` with `useGccNG = true`,
because its symbols would conflict. We'll see whether that is acceptable
or not. If it isn't, we can still avoid vendoring, but we will need to
replicate the symbol renaming that GCC would have done externally.

[^1]: https://inbox.sourceware.org/gcc-patches/20260814013206.3818461-1-git@JohnEricson.me/

Assisted-by: Claude Code (Claude Opus 5)
2026-08-13 23:18:53 -04:00
John Ericson
85edf2c64f gcc/ng: let libstdc++ pick its own locale model
`--enable-clocale=gnu` was passed for every target. That selects
`config/locale/gnu`, the glibc locale model, whose
`ctype_members.cc` reads `__ctype_b` -- a glibc symbol. On any other
libc the build fails on the type the table is not:

    ctype_members.cc:50:51: error: cannot convert 'const short unsigned int*'
      to 'const std::ctype_base::mask*' {aka 'const unsigned int*'} in assignment

libstdc++ works this out for itself from the host triple, as it does for
the OS layer next door -- `configure.host:262` maps `linux-musl*` to
`os/generic` -- and the monolithic build passes no `--enable-clocale` at
all, leaving that to configure. Do the same here.

Verified by building the full cross toolchain, `stdenv.cc`, for
`aarch64-unknown-linux-musl`, which builds libstdc++ and previously
failed on exactly this.

Assisted-by: Claude Code (Claude Opus 5)
2026-08-12 17:00:05 -04:00
John Ericson
fcde6eb30d gcc/ng: build libstdc++ with the C driver, and ship libtool-ldflags
Two things a monolithic build does implicitly that the split set had
nobody to do.

Linking `libstdc++.so` with `g++` fails, because that driver implies
`-lstdc++` and the library being linked is the one that would provide it:

    ld: cannot find -lstdc++: No such file or directory

`libstdc++-v3/src/Makefile.am` names the remedy where it defines
`CXXLINK`: use `gcc` as the C++ compilation driver, which the top level
arranges via `RAW_CXX_FOR_TARGET` -- `xgcc`, not `xg++`, plus
`-shared-libgcc` and `-nostdinc++`. Configured standalone there is no top
level to arrange it, so arrange it here. The C driver still compiles
`.cc` as C++ by extension; what it drops is the implicit `-lstdc++`.
`-shared-libgcc` restores the linkage `g++` would have chosen, and
`-nostdinc++` keeps already-installed C++ headers out of the build whose
purpose is to produce them.

`src/Makefile` also computes `LTLDFLAGS` by shelling out to
`$(top_srcdir)/../libtool-ldflags`, reaching out of `libstdc++-v3` into
the GCC top level. Our trimmed source tree never copied it, so the shell
reported `No such file or directory`, `LTLDFLAGS` came out empty, and
`LDFLAGS` was quietly dropped from every library link. Same failure mode
as the `gthr-default.h` bug: a file that exists only because a monolithic
tree has it, missing without anything treating its absence as an error.

Assisted-by: Claude Code (Claude Opus 5)
2026-08-12 17:00:05 -04:00
John Ericson
83cb487509 gcc/ng: build libgcc twice, once for the libc and once against it
The libc bootstrap had every stage except the last. `gccNoLibgcc` builds
libgcc, `gccWithLibgcc` builds the libc with it — and then nothing goes
back to rebuild libgcc now that the libc exists. So the only libgcc
anyone ever got was the one from before there was a libc, which is
necessarily single-threaded: `gthr-posix.h` includes `<pthread.h>`
unconditionally, and at that point there is no libc to provide it.
`libstdcxx` above it assumed `posix` regardless, so the two disagreed by
construction — the unwinder's registry locking compiled away to nothing
underneath a `libstdc++` handing out `std::thread`.

Add the last stage the way the LLVM package set does with
`compiler-rt-no-libc` and `compiler-rt-libc`: instantiate the same
package twice. `libgcc-no-libc` is the existing one and now feeds only
the three bootstrap compilers. `libgcc-libc` is built after the libc, by
a new `gccWithLibcAndBasicLibgcc` — real libc, bootstrap libgcc — and is
what `libgcc` resolves to for any platform that has a libc.

Nothing is passed to the package to say which of the two it is. The
distinction is the compiler it is handed, exactly as in the LLVM set:
`gccNoLibgcc` is wrapped with `binutilsNoLibc`, whose `libc` is
`preLibcHeaders` -- the header-only stand-in, or nothing at all on
platforms without one -- and `wrapCCWith` defaults a wrapper's `libc` to
its bintools'. So the package reads `stdenv.cc.libc` and needs no
bootstrap flag of its own.

Which threading model is available is a property of the libc, so the libc
declares it as `passthru.threadModel` and `libgcc` reads it from there.
That is what makes the pre-libc build single-threaded without being told
to be: a headers-only package declares no `threadModel`, a real libc
does.
`libstdcxx` in turn takes both the model and the generated
`gthr-default.h` from `libgcc`, replacing a `$CXX -v` probe that asked
the compiler — which in this package set is configured separately from
libgcc and so answers for the wrong component. Platforms that declare
nothing keep the single-threaded model they already had.

The second libgcc is also the first one that can be shared. Previously
only the static library was built, and the compiler was configured
`--disable-shared` for every target, which is compiled into the driver's
specs, so it named bare `-lgcc` and never `-lgcc_s`:

    $ x86_64-unknown-netbsd-g++ -### eh.cc
    collect2 ... -lstdc++ -lm -lgcc -lc -lgcc

Both halves have to move together. Build the shared library without
changing the specs and the unwinder leaves `libgcc.a` while the specs
still name it:

    libgcc.a         _Unwind_RaiseException: absent
    libgcc_eh.a      _Unwind_RaiseException: present
    libgcc_s.so.1    _Unwind_RaiseException: present

so every throwing C++ program fails to link and `rustc` fails on
`-lgcc_s` outright. Either standard arm would do, `-lgcc_s -lgcc` shared
or `-lgcc -lgcc_eh` static; the halves disagreeing is what breaks.

`--disable-shared` was passed to libgcc with the comment "Do not have
dynamic linker without libc". That does not hold: the monolithic build
ships `libgcc_s` even from its *nolibc* cross stage, and the result needs
nothing at run time --

    $ readelf -d .../nolibc-gcc-15.3.0-libgcc/.../libgcc_s.so.1
     0x...0e (SONAME)  Library soname: [libgcc_s.so.1]
    (no NEEDED entries at all)

What genuinely blocks it is libgcc's own makefile: `SHLIB_LC` defaults to
`-lc`, so the `libgcc_s.so` rule links against a libc that does not exist
yet and fails with `cannot find -lc`. The monolithic build clobbers that
variable -- see `common/libgcc-buildstuff.nix` -- so reuse that helper
rather than reinventing it. On the compiler side the flag derives from
`hasSharedLibraries`, as the monolithic build does, so a target genuinely
without shared libraries keeps the old behaviour; it is spelled
`enableTargetShared` there because it describes the target's libgcc
rather than anything about the compiler being built.

Verified on `x86_64-unknown-netbsd`, where `libgcc_s.so.1` and the
`GROUP ( libgcc_s.so.1 -lgcc )` script now appear in the output, and by
building `stdenv.cc` for `aarch64-unknown-linux-gnu` and
`aarch64-unknown-linux-musl`.

Assisted-by: Claude Code (Claude Opus 5)
2026-08-12 16:59:29 -04:00
John Ericson
be82056397 gcc/ng: cope with a source tree that is a VCS checkout
A release tarball carries generated files that a git tree does not, and
two of those bit us.

`MD5SUMS` is generated when a tarball is rolled. Each sub-source
extractor asserted `[[ -f MD5SUMS ]]` before copying it, so every one of
them failed outright the moment `monorepoSrc` was a git tree:

    unpacking source archive /nix/store/...-source
    source root is source
    <exit 1>

Copy it only when it is there.

The generated sources are the other half: a checkout lacks
`gengtype-lex.cc` and friends, which a tarball ships pre-built, so they
have to be regenerated. Take flex and bison as native inputs when
`fromVCS` says the source is a checkout.

Tarball builds are unaffected either way.

Assisted-by: Claude Code (Claude Opus 5)
2026-08-12 16:58:43 -04:00
John Ericson
120107d894 gccNGPackages: Replace some mailing list fetches with commits
A few patches of mine were just merged (yay!). Let's upgrade the
`fetchpatch` invocations from the original mailing list submission to
the accepted commit, accordingly.

I narrowed down the the files to patch because patching
autoconf-generated files will break, and is not needed because we are
regenerating those files anyways.
2025-12-10 14:05:03 -05:00
John Ericson
424f8f2abb gccNGPackages: Force regular dirs
Because we're building things separately, we don't need the fancy
lib/... namespacing tricks that GCC normally does to squeeze itself in
the FHS. We can just use the normal autotools libdir and include dir,
and the nixpkgs infra will sort everything out.

Where possible I submitted patches to the mailing list, and fetched
those. The ones I am vendoring are the residuals which I don't think are
ready for upstreaming yet. (I can imagine a further reworking upstream
such that we wouldn't need our own patches of that sort, but it would be
good to get the first crop merged first before discussing that.)
2025-07-21 15:40:34 -04:00
John Ericson
1840d4dbf0 gccNGPackages: Rework threading model patch based on feedback on IRC
Also clean up some comments, and avoid unnecessarily patching GCC
itself.
2025-07-13 12:51:54 -04:00
Tristan Ross
e51bc1ab23 gccNGPackages: init 2025-07-11 07:42:18 -07:00