Files
RedBear-OS/local/docs/NATIVE-TOOLCHAIN-WORKSTREAM.md
T
vasilito 7f5ef461ec docs(native-toolchain): record findings 5-7 and correct the leak direction
rust-native now builds. Records the three defects found getting there
(host/target header leak, the -fno-hardened cross-flag leak, relibc's
duplicate _POSIX_THREADS) and closes the previously-open "unreachable
'pub' item" item, which deny-warnings = false already resolved.

Corrects the gcc-native diagnosis. It read "the host's libstdc++ headers
are being pulled into a target compile"; the direction is the reverse --
relibc's TARGET headers reach a HOST compile through the redoxer
toolchain's mixed include/ directory, so __GLIBC_PREREQ is undefined
where the host headers expect it. That matters for how gcc-native gets
unblocked: the host: package pattern used for rust-native should apply,
rather than reworking GCC's cross-build header model.

Status flipped DEFERRED -> ACTIVE per the 2026-08-05 operator decision.
2026-08-05 15:41:27 +03:00

12 KiB

Native Toolchain Workstream — gcc-native / rust-native

Status: ACTIVE. Deferred 2026-08-03 ("Treat gcc-native/rust-native as a separate workstream, explicitly deferred by me, for now."), then RE-ENABLED by explicit operator decision, 2026-08-05: "Build and add gcc-native and rust-native to project." Both are = {} in config/redbear-full.toml again. Scope: local/recipes/dev/gcc-native/, local/recipes/dev/rust-native/. Not deferred: binutils-native and llvm-native build and remain in config/redbear-full.toml. llvm-native is required, not optional — it supplies the host LLVM dev tree that libclc and Mesa's iris/radeonsi CLC path consume.

Related: local/patches/gcc-redox-port/README.md, VENDORED-LIBC-FORK.md, FORK-BUMP-PATCHING-POLICY.md, LOCAL-FORK-SUPREMACY-POLICY.md.


What this workstream is

A toolchain that runs on Redox and targets Redox — gcc, cargo/rustc executing inside the OS. Distinct from the cross toolchain in prefix/, which runs on the Linux host and is what actually builds the distribution.

That distinction matters for prioritisation: the cross toolchain is done (GCC 16.1.0, libstdc++.so.6.0.35, working std::ranges::to) and is what compiled every other package. Nothing in the desktop path — Mesa/virgl, Wayland, Qt6, KF6, SDDM — depends on the native toolchain.

Why it is deferred, honestly

Not because it is unimportant, and not for the reason the old config gave.

The previous exclusion said "suppressed: Redox C++/pthread header gaps; not needed for greeter proof". That was wrong on both counts. llvm-native has no header gap at all — it cooks clean in isolation, and only failed in parallel builds because of a cookbook race on shared staging paths (fixed; see src/bin/repo.rs recipe_dir_lock). A real build-system bug had been misdiagnosed as a porting gap and used to justify excluding a required package. Restoring these packages is how the twelve defects below were found at all — including that redbear-iwlwifi had never compiled since the commit that broke it.

The deferral is instead a scope judgement: making GCC build itself for Redox is a genuine porting project. Upstream Redox has only ever shipped GCC 8.2.0 and 13.2.0; nobody has done GCC 16 for this target. Every fix so far has revealed the next layer, and the remaining failures are header-model conflicts (GCC's internal headers vs relibc's, host vs target) that want someone working through GCC's cross-build header model as a whole rather than patching incrementally.

gcc-native — moved to GCC 16, twelve findings

The recipe now builds GCC 16.1.0 from local/recipes/dev/gcc16/source (upstream 16.1.0 + local/patches/gcc-redox-port/), not GCC 13.

Why the move happened

Building GCC 13 with the GCC 16 cross compiler is three major versions of drift and is not a supported configuration. Three fixes landed before it became untenable:

# Defect Resolution
1 char8_t. GCC 16 defaults to C++20, which changed u8"" from const char[] to const char8_t[]; libcody uses them throughout (~100 diagnostics) pin the C++ standard
2 libcody's configure contains #if __cplusplus > 201103 / #error "C++11 is required" — it rejects anything newer than C++11 -std=gnu++11, which also fixes #1 (no char8_t before C++20)
3 bit_AVX512PF / ER / 4VNNIW / 4FMAPS / PREFETCHWT1 undeclared — GCC 15/16 removed these macros for withdrawn ISA extensions, but GCC 13's cpuinfo.h needs them put GCC 13's own cpuid.h ahead of the toolchain's include path

Then GCC 16 ICEd compiling GCC 13's own libstdc++:

libstdc++-v3/libsupc++/eh_call.cc:39:1: internal compiler error:
in gimple_build_eh_must_not_throw

An ICE is where flag-tweaking stops being a legitimate answer. Those three fixes remain in recipes/dev/gcc13/recipe.toml, where they are correct for that recipe. Note that the recipe no longer builds anything: as of 2026-08-05 this fork has no GCC 13 path at allmk/prefix.mk defaults to GCC_RECIPE?=gcc16 and build-redbear.sh refuses a non-GCC-16 toolchain. See TOOLCHAIN-GCC16.md.

After the move to GCC 16

# Defect Resolution
4 The same_as symlink source -> recipes/dev/gcc13/source survived the recipe edit, so the "GCC 16" build was still compiling GCC 13 sources removed; now -> ../gcc16/source
5 gcc13 / gcc13.cxx dependencies build the very compiler being replaced dropped
6 unknown type name 'bool' — GCC 16's sources are C23, where bool is a keyword, but the cookbook pins -std=gnu17 tree-wide for the C17-era recipes exempt this recipe from the pin
7 cannot guess versionsame_as used to supply it; a path source gives the cookbook nothing to infer from explicit [package].version
8 'fenv_t' has not been declared in '::' — dropping gcc13 also removed what transitively staged relibc's headers, leaving the sysroot with no libc headers at all explicit relibc dependency
9 _getopt_internal undeclared — libiberty needs GCC's own include/getopt.h, which declares that GNU-internal symbol; relibc's getopt.h was shadowing it GCC's include/ ahead of the sysroot
10 FNM_FILE_NAME / FNM_LEADING_DIR undeclared — GNU extensions genuinely absent from relibc implemented in the relibc fork (see below)

Correction worth preserving

Finding #8 was initially misdiagnosed as "relibc's fenv.h is a 3-line stub". It is not. relibc's fenv.h correctly delegates to openlibm_fenv.h, which dispatches on __x86_64__ to openlibm_fenv_amd64.h where fenv_t (line 48) and fexcept_t (line 50) are both defined — and relibc stages all of those headers. The header was simply never reaching this recipe's sysroot. A commit message asserting a relibc fenv gap was wrong and is corrected in the commit that follows it.

The lesson generalises to most of this list: an "undeclared identifier" against relibc is more often a header-path problem than a missing implementation. Check whether the header reaches the compiler before concluding relibc is incomplete.

Real relibc gap that was fixed

FNM_FILE_NAME and FNM_LEADING_DIR are now implemented in local/sources/relibc/src/header/fnmatch/mod.rs (pushed on submodule/relibc). FNM_FILE_NAME is the GNU synonym for FNM_PATHNAME, so an alias. FNM_LEADING_DIR is real behaviour — the pattern may match a leading directory, so foo matches foo/bar — implemented by trying the exact match then each prefix ending before a /. Declaring the flag without honouring it would have been a stub. Its value (16) is the next free bit in relibc's own numbering, not glibc's.

Where it stopped

/usr/include/c++/16/x86_64-pc-linux-gnu/bits/os_defines.h:44:
error: missing binary operator before token '('

Root cause identified 2026-08-05 while unblocking rust-native — it is the same defect, and it is now understood. The initial reading above ("host libstdc++ headers pulled into a target compile") had the direction backwards. What actually happens is the reverse: ${HOME}/.redoxer/${TARGET}/toolchain/include is a MIXED directory holding relibc's target headers (stdlib.h, features.h, fenv.h, …) beside the llvm/ and clang/ ones. When that directory reaches a host compile via -I, relibc's features.h shadows glibc's, __GLIBC_PREREQ is never defined, and every host libstdc++/glibc header testing it fails. Reproducible in three lines, no rustc or GCC build involved:

printf '#include <cstdlib>\nint main(){}' > t.cpp
c++ -std=c++17 -I${HOME}/.redoxer/x86_64-unknown-redox/toolchain/include -c t.cpp

So this is a target→host leak, not a host→target one. See the rust-native finding #5 below for the fix pattern (host: packages stage into COOKBOOK_TOOLCHAIN, which is a separate tree from the target sysroot); the same approach should apply here.

rust-native — four findings, one open

# Defect Resolution
1 nix 0.30.1 incompatible with libc 0.2.178: declares SaFlags_t = c_ulong while libc has sa_flags/SA_* as c_int (E0308 both directions) nix 0.31 fixed it; ctrlc 3.5 requires nix "0.31"
2 in-tree miri pinned nix = "0.30.1" and held the broken version in the graph bumped; carried as local/patches/rust/01-miri-nix-0.31.patch
3 CLD_EXITED / P_ALL etc. not in crate libc — its Redox bindings never exposed the waitid surface relibc implements, still absent in 0.2.189 vendored fork, see VENDORED-LIBC-FORK.md
4 llvm/Config/llvm-config.h: No such filellvm-native.dev (which stages the headers) missing from dependencies added
5 ~200 errors in the HOST's own glibc/libstdc++ headers building rustc_llvm for the host triple. rustc_llvm's build script feeds llvm-config --includedir straight to the host c++, and the redoxer toolchain's include/ is the mixed dir described under gcc-native above host:llvm-native (+ .dev/.runtime). Host deps stage into COOKBOOK_TOOLCHAIN, a tree SEPARATE from the target sysroot, so the host LLVM and the Redox llvm21 coexist instead of overwriting each other — which is the collision the recipe's llvm21 comment records
6 c++: error: unknown argument: '-fno-hardened'. DYNAMIC_INIT exports CXXFLAGS=-fno-hardened for the GCC 16 cross compiler; the recipe's unset list dropped CC/CXX/LDFLAGS but not CFLAGS/CXXFLAGS/CPPFLAGS, so it leaked into the host stage1 build, where c++ now resolves through COOKBOOK_TOOLCHAIN/bin to llvm-native's clang++ added CFLAGS CXXFLAGS CPPFLAGS to the unset. Pre-existing leak; the cross recipes/dev/rust has the same list and never hit it only because nothing puts a clang on its PATH
7 '_POSIX_THREADS' redefined [-Werror] building rustc_llvm for the target. relibc defined it twice: unistd.h 202405L (correct — POSIX puts it there) and pthread.h 1, emitted from a pub const in src/header/pthread/mod.rs that had zero users deleted the dead constant in the relibc fork (851a505f on submodule/relibc), + prefix rebuild

Findings 5 and 6 were pre-existing latent defects, not regressions: #5 is the same root cause as gcc-native's stopping point, and #6 only becomes reachable once a clang is on PATH. #7 broke any -Werror C++ TU including both headers, which is every C++ TU — libstdc++'s <iterator> chain reaches pthread.h via gthr-default.h.

Closed: the five error: unreachable 'pub' item diagnostics are resolved by deny-warnings = false in config.toml (documented in that file: the vendored libc fork arrives as a PATH dependency, so cargo's --cap-lints allow for registry deps does not apply to it).

Re-enabling

Both were re-enabled in config/redbear-full.toml on 2026-08-05; nothing needs un-ignoring. Build with ./local/scripts/build-redbear.sh --upstream redbear-full.

gcc-native should now resume past its recorded stopping point using the finding-#5 pattern (a host: package, so the host compile never sees the mixed redoxer include dir) rather than by patching GCC's header model incrementally.

Diagnose failures in isolationCOOKBOOK_COOK_JOBS=1 ./target/release/repo cook <recipe>. The aggregate build multiplexes output from parallel cooks, and several wrong turns in this workstream came from attributing one recipe's error to another in the interleaved log.

Upstream-reportable

Two findings belong upstream rather than in Red Bear indefinitely:

  • The libc crate's Redox module lacks the waitid surface (idtype_t, P_*, CLD_*, waitid) that relibc implements. Retirement procedure in VENDORED-LIBC-FORK.md.
  • relibc's missing GNU fnmatch extensions — now fixed in the fork, and worth offering upstream.
  • relibc's duplicate _POSIX_THREADS (finding #7) — the pthread.h copy is both misplaced per POSIX and carries an invalid value. Fixed in the fork and worth offering upstream.