42 KiB
Package Build Quirks — Known Cross-Compilation Issues and Fixes
Created: 2026-06-19
Scope: All recipes in recipes/ and local/recipes/ that have known build quirks
Purpose: Central reference for cross-compilation issues encountered when building
Red Bear OS packages, their root causes, and fixes.
Overview
Building packages for the x86_64-unknown-redox target from a Linux x86_64 host
involves a cross-compilation environment managed by the cookbook tool. Several
packages require specific adjustments to their recipe or source to build correctly
in this environment. This document captures all known quirks and their fixes.
Cookbook Environment: DYNAMIC_INIT
Problem
The cookbook's DYNAMIC_INIT function (defined in src/cook/script.rs) sets up
the cross-compilation environment. It overwrites several environment variables
entirely, including:
CFLAGS— set to-I<sysroot>/includeCXXFLAGS— set to-I<sysroot>/includeLDFLAGS— set to-L<sysroot>/lib --static -L<prefix>/libCPPFLAGS— set to-I<sysroot>/includePKG_CONFIG_LIBDIR,PKG_CONFIG_SYSROOT_DIRRUSTFLAGSCARGO_TARGET_DIRPATH
Impact
Any export FOO="..." that runs BEFORE DYNAMIC_INIT will be overwritten.
Any export FOO="..." that runs AFTER DYNAMIC_INIT will persist.
Fix Pattern
For variables that DYNAMIC_INIT overwrites, you must set them AFTER
DYNAMIC_INIT:
# WRONG: LDFLAGS overwritten by DYNAMIC_INIT
export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}"
DYNAMIC_INIT
# At this point, LDFLAGS = "-L<sysroot>/lib --static ..." (our flag is gone)
# CORRECT: Set after DYNAMIC_INIT preserves the flag
DYNAMIC_INIT
export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}"
# At this point, LDFLAGS = "-Wl,--allow-multiple-definition -L<sysroot>/lib ..."
For CFLAGS specifically, the pre-DYNAMIC_INIT approach can work because
the reexport_flags function (called by cookbook_configure) re-reads the
current $CFLAGS and incorporates it into CFLAGS_x86_64_unknown_redox. But
this is fragile — the safest approach is to set flags AFTER DYNAMIC_INIT
for ALL variables.
Exceptions
CFLAGS set BEFORE DYNAMIC_INIT works because reexport_flags (called
inside cookbook_configure) reads $CFLAGS at configure time and prepends it
to CFLAGS_x86_64_unknown_redox. However, LDFLAGS set BEFORE DYNAMIC_INIT
is overwritten by DYNAMIC_INIT and NOT re-read by reexport_flags.
Safe rule: Set ALL custom flags AFTER DYNAMIC_INIT.
Reference
src/cook/script.rs—DYNAMIC_INITfunction definitionsrc/cook/script.rs—reexport_flagsfunction definition
Package: m4 (GNU M4 macro processor)
Recipe: local/recipes/dev/m4/recipe.toml
Source: https://ftp.gnu.org/gnu/m4/m4-1.14.21.tar.xz
Template: custom (uses cookbook_configure)
Quirk 1: -fcommon Required (GCC 10+ Compatibility)
Problem: GCC 10+ defaults to -fno-common, which causes multiple definition
errors for gnulib's rpl_realloc wrapper. The gnulib replacement function is
defined as a tentative symbol across multiple translation units.
Error:
/usr/bin/x86_64-unknown-redox-ld: libm4.a(libm4_a-malloc.o):(.bss+0x0):
multiple definition of `rpl_realloc';
libm4_a-realloc.o:(.bss+0x0): first defined here
Fix: Add -fcommon to CFLAGS:
export CFLAGS="-fcommon ${CFLAGS}"
Placement: BEFORE DYNAMIC_INIT (works because reexport_flags re-reads
$CFLAGS at configure time).
Better placement (recommended for future): AFTER DYNAMIC_INIT — same
result, more robust.
Quirk 2: --allow-multiple-definition Required (Linker)
Problem: Even with -fcommon, the linker encounters multiple definitions of
rpl_realloc because gnulib provides it in multiple object files and the
static library linking order causes duplicates.
Error:
/usr/bin/x86_64-unknown-redox-ld: libm4.a(libm4_a-malloc.o):(.bss+0x0):
multiple definition of `rpl_realloc';
libm4_a-realloc.o:(.bss+0x0): first defined here
/usr/bin/x86_64-unknown-redox-ld: error: ld returned 1 exit status
Fix: Add --allow-multiple-definition to LDFLAGS:
DYNAMIC_INIT
export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}"
Placement: MUST be AFTER DYNAMIC_INIT — DYNAMIC_INIT overwrites
LDFLAGS entirely. Setting it before DYNAMIC_INIT has no effect.
Quirk 3: Locale Platform Detection
Problem: m4's gnulib getlocalename_l-unsafe.c has a Redox case guarded by
HAVE_GETLOCALENAME_L, which relibc doesn't define. The file hits an #error
when neither the guard nor the platform detection matches.
Error:
error: #error "Please port gnulib getlocalename_l-unsafe.c to your platform"
Fix: Changed the guard from __redox__ && HAVE_GETLOCALENAME_L to just
__redox__ and return "C" (Redox only supports the C/POSIX locale).
File: lib/getlocalename_l-unsafe.c — line ~661:
#elif defined __redox__
return "C";
Applied via: local/recipes/dev/m4/source/ (local recipe with source bundled)
Quirk 4: Configure Cache Variables
Problem: m4's configure script tests for functions that don't exist or
behave differently on Redox, causing false negatives.
Fix: Pass cache variables to skip problematic tests:
COOKBOOK_CONFIGURE_FLAGS+=(
--disable-nls
ac_cv_func___freadahead=yes
ac_cv_have_decl___freadahead=yes
gl_cv_header_wchar_h_correct_inline=yes
gl_cv_func_btowc_nul=yes
gl_cv_func_btowc_consistent=yes
gl_cv_onwards_func___freadahead=yes
)
Quirk 5: wchar.h Circular Include (gnulib + relibc)
Problem: m4's gnulib generates a wchar.h wrapper that does
#include_next <wchar.h>. relibc's wchar.h included <stdio.h> at line 4,
before defining wint_t (line 20) and mbstate_t (line 41). The circular
chain:
wchar.h -> stdio.h -> stdint.h -> gnulib inttypes.h -> sysroot inttypes.h -> #include wchar.h -> BACK TO GNULIB wchar.h (guard not set!)
caused gnulib's wchar.h to process its replacements before
wint_t/mbstate_t were defined, producing unknown type name 'wint_t' and
unknown type name 'mbstate_t'.
Error:
error: unknown type name 'wint_t'
error: unknown type name 'mbstate_t'
Root cause: relibc's wchar.h included <stdio.h> before defining
wint_t/mbstate_t, AND relibc's inttypes.h included <wchar.h> instead of
<stdint.h> + <stddef.h> per POSIX spec.
Fix: Two parts, both in the relibc fork (local/sources/relibc/):
src/header/wchar/cbindgen.toml: Movedstdio.h/time.hfromsys_includesintoafter_includes, placed AFTER thewchar_t/wint_t/mbstate_ttypedefs. Also definedmbstate_tinafter_includeswith a_RELIBC_MBSTATE_Tguard and excluded it from cbindgen export via[export] exclude = ["mbstate_t"].src/header/inttypes/cbindgen.toml: Changedsys_includesfrom["wchar.h"]to["stdint.h", "stddef.h"]per POSIX spec (inttypes.honly needswchar_tfromstddef.h, not the fullwchar.h).
Reference: See the relibc section below (Quirk 3) for the same fix
documented from the relibc side. Commits d28963d (wchar.h fix) and
a2e4cd2 (inttypes.h fix) in local/sources/relibc/.
Quirk 6: __freadahead Prefix Staleness
Problem: The recipe sets ac_cv_func___freadahead=yes to tell gnulib that
relibc provides __freadahead. The function IS implemented in relibc's
src/header/stdio/ext.rs (commit 047e7c0). But the PREFIX libc.a was built
before that commit, so the symbol was missing from the compiled library.
Error:
undefined reference to `__freadahead'
Fix: Rebuild relibc in the prefix after adding new functions:
touch relibc && make prefix
Prevention: The build script now warns when prefix artifacts are older than fork commits.
Complete Recipe
[source]
tar = "https://ftp.gnu.org/gnu/m4/m4-1.14.21.tar.xz"
patches = ["redox.patch"]
[build]
template = "custom"
script = """
export CFLAGS="-fcommon ${CFLAGS}"
DYNAMIC_INIT
export LDFLAGS="-Wl,--allow-multiple-definition ${LDFLAGS}"
COOKBOOK_CONFIGURE_FLAGS+=(
--disable-nls
ac_cv_func___freadahead=yes
ac_cv_have_decl___freadahead=yes
gl_cv_header_wchar_h_correct_inline=yes
gl_cv_func_btowc_nul=yes
gl_cv_func_btowc_consistent=yes
gl_cv_onwards_func___freadahead=yes
)
cookbook_configure
"""
[package]
description = "GNU M4 macro processor"
Note: Quirks 5 and 6 do not require changes to recipe.toml. Quirk 5 is
fixed in the relibc fork (header generation), and Quirk 6 is resolved by
rebuilding the relibc prefix artifact (touch relibc && make prefix).
Package: ninja-build (Ninja build system)
Recipe: local/recipes/dev/ninja-build/recipe.toml
Source: https://github.com/ninja-build/ninja (v1.13.1)
Template: cmake
Quirk: BUILD_TESTING=OFF Required (Cross-Compilation)
Problem: ninja-build's CMakeLists.txt includes CTest, which enables
BUILD_TESTING=ON by default. Tests require Google Test (gtest), which is found
on the host system at /usr/include/gtest/. When the C++ compiler includes
gtest headers, it transitively includes the host /usr/include/stdlib.h, which
conflicts with the Redox sysroot stdlib.h.
Error:
/usr/include/stdlib.h:67:5: error: conflicting declaration 'typedef struct div_t div_t'
/usr/include/stdlib.h:75:5: error: conflicting declaration 'typedef struct ldiv_t ldiv_t'
/usr/include/stdlib.h:85:5: error: conflicting declaration 'typedef struct lldiv_t lldiv_t'
/usr/include/stdlib.h:761:18: error: conflicting declaration of 'int at_quick_exit(void (*)())' with 'C++' linkage
Additionally, the Redox sysroot stdlib.h uses __noreturn and __deprecated
attributes that C++ doesn't recognize, causing further errors when test files
are compiled.
Root cause: This is NOT a Redox capability gap — it is a fundamental cross-compilation constraint. Test suites require a test framework (gtest) that exists on the host, and tests are designed to run on the target platform, not during cross-compilation. Building tests during cross-compile is incorrect.
Fix: Disable test building via CMake flag:
[build]
template = "cmake"
cmakeflags = ["-DBUILD_TESTING=OFF"]
This is standard practice for cross-compilation — the ninja binary itself is the deliverable, not its test suite.
Complete Recipe
[source]
git = "https://github.com/ninja-build/ninja"
rev = "v1.13.1"
[build]
template = "cmake"
cmakeflags = ["-DBUILD_TESTING=OFF"]
[package]
description = "Ninja build system"
Package: kernel (Redox kernel — local fork)
Recipe: recipes/core/kernel/recipe.toml (uses path = "../../../local/sources/kernel")
Source: local/sources/kernel/ (local fork)
Quirk: -Zjson-target-spec Cargo Flag
Problem: The upstream kernel Makefile passes -Zjson-target-spec as a cargo
CLI flag. The redoxer build environment's cargo accepts it in cargo -Z help
but rejects it when invoked through make inside redoxer env bash -ex.
Fix: Use the env-var form instead of the CLI flag in the kernel Makefile:
export CARGO_UNSTABLE_JSON_TARGET_SPEC=true
# Remove -Zjson-target-spec from the cargo rustc command line
General Cross-Compilation Patterns
Pattern: Disable Tests During Cross-Compilation
When a package's build system includes test targets (cmake BUILD_TESTING,
automake TESTS, meson tests), tests must be disabled during cross-compilation.
Why: Tests require:
- A test framework (gtest, catch2, etc.) that exists on the HOST, not the TARGET
- The test framework headers transitively include HOST system headers
- HOST system headers conflict with TARGET sysroot headers (type redefinition)
- Tests are designed to RUN on the target, not during cross-compile
How (cmake):
cmakeflags = ["-DBUILD_TESTING=OFF"]
How (meson):
mesonflags = ["-Dtests=false"]
How (configure/automake):
COOKBOOK_CONFIGURE_FLAGS+=(--disable-tests)
# or if no --disable-tests option:
COOKBOOK_CONFIGURE_FLAGS+=(ac_cv_prog_have_gtest=no)
Pattern: Gnulib Compatibility (-fcommon and --allow-multiple-definition)
Packages using gnulib (m4, bison, flex, autoconf, etc.) may need:
-
-fcommonin CFLAGS — gnulib provides replacement functions (rpl_malloc,rpl_realloc) that are defined as tentative symbols in multiple translation units. GCC 10+ defaults to-fno-common, causing multiple definition errors. -
-Wl,--allow-multiple-definitionin LDFLAGS — Even with-fcommon, the linker may encounter duplicate definitions from static library archives.
Note: These flags should be added per-recipe only when needed, not globally.
Pattern: Configure Cache Variables for Autoconf Packages
Autoconf-based packages (m4, bison, flex, etc.) run configure tests that may
fail on Redox because:
- The function exists but behaves differently
- The function doesn't exist (but the test expects it)
- Cross-compilation can't run test programs
Fix by passing cache variables to skip problematic tests:
COOKBOOK_CONFIGURE_FLAGS+=(
ac_cv_func___freadahead=yes
ac_cv_have_decl___freadahead=yes
gl_cv_header_wchar_h_correct_inline=yes
)
Pattern: NLS Disabled
Most packages should disable NLS (Native Language Support) for Redox:
COOKBOOK_CONFIGURE_FLAGS+=(--disable-nls)
Redox doesn't have a full gettext/libintl stack. The --disable-nls flag is
standard for cross-compilation targets that don't need internationalization.
Pattern: C++ Cross-Compilation Header Isolation
When compiling C++ files for Redox, the compiler must use the Redox sysroot
headers, not the host headers. The cookbook sets up --sysroot via the CMake
toolchain file and CFLAGS_x86_64_unknown_redox/CXXFLAGS_x86_64_unknown_redox.
If host headers leak in (usually through #include <gtest> or similar), the fix
is to remove the dependency on host headers, not to try to isolate the include
path further. This typically means disabling tests or the feature that requires
the host-only dependency.
Package: libiconv (GNU libiconv 1.17)
Quirk: Libtool Version Mismatch (2.6.0.23-b08cb)
Problem
libiconv 1.17 ships a pre-generated configure script that embeds the
libtool release identifier at configure-generation time. On Debian hosts
where the system libtool has been updated to 2.6.0.23-b08cb, the bundled
configure declares macro_version='2.6.0', but the running libtool
binary advertises VERSION=2.6.0.23-b08cb and package_revision=2.6.0.23.
At compile time, libtool aborts with:
libtool: Version mismatch error. This is libtool 2.6.0.23-b08cb, revision 2.6.0.23,
libtool: but the definition of this LT_INIT comes from revision 2.6.0.
libtool: You should recreate aclocal.m4 with macros from revision 2.6.0.23
libtool: of libtool 2.6.0.23-b08cb and run autoconf again.
This affects the nested libcharset subdir as well — both configure
scripts need patching.
Root Cause
ltmain.sh (the libtool script template that ships with the host's
libtool package) is the source of truth for the running libtool's
VERSION and package_revision. The bundled configure script was
generated against an older libtool and embeds the older macro_version/
macro_revision strings. When libtool runs at compile time it compares
its own VERSION/package_revision against the values configure
generated, and aborts when they differ.
Fix
Source the shared helper from the build script and rewrite both
configure scripts' macro_version and macro_revision to match the host
libtool:
. "${COOKBOOK_ROOT}/local/scripts/lib/libtool-version-sync.sh"
redbear_sync_libtool_version \
"${COOKBOOK_SOURCE}/configure" \
"${COOKBOOK_SOURCE}/libcharset/configure"
The helper detects the host libtool version (via /usr/bin/libtool --version — must be the absolute path, not command -v libtool,
because the cookbook prepends the Redox cross sysroot's bin/ to PATH
and command -v libtool would return the Redox-target libtool
2.5.4-redox-9510 instead of the host toolchain's libtool) and
rewrites the known bundled version strings (2.4.7, 2.5.4-redox-9510,
2.6.0) to the host values. Other recipes that bundle pre-generated
configure scripts (older gettext, autotools packages from before the
host libtool was updated) can use the same helper.
Reference
local/scripts/lib/libtool-version-sync.sh— shared helperrecipes/libs/libiconv/recipe.toml— usage in the [build] scriptredoxerhost:libtool (GNU libtool) 2.6.0.23-b08cb- Upstream libtool version detection:
libtool --version(preferred)VERSION=line in/usr/share/libtool/build-aux/ltmain.sh(fallback)
Package: gnu-binutils (GNU Binutils 2.43.1)
Recipe: recipes/tools/gnu-binutils/recipe.toml
Source: https://ftp.gnu.org/gnu/binutils/binutils-2.43.1.tar.xz
Template: custom (uses cookbook_configure)
Quirk: Autoconf Version Lock — Skip Autoreconf
Problem: Binutils 2.43.1 ships config/override.m4 which enforces
exactly Autoconf 2.69 via _GCC_AUTOCONF_VERSION_CHECK. If the host
has Autoconf 2.73 (common on modern Debian/Ubuntu), autoreconf aborts:
configure.ac:21: error: Please use exactly Autoconf 2.69 instead of 2.73.
The original recipe used COOKBOOK_AUTORECONF=autoreconf2.69 which
requires the Debian-specific autoconf2.69 package — not available on
all distros.
Root cause: GCC/Binutils lock their autoreconf to a specific Autoconf version. This is intentional upstream behavior, not a bug.
Fix: Skip the autoreconf step entirely. The source tarball ships with
pre-generated configure scripts that work correctly without regeneration.
The 01_build_fix.patch modifies configure.ac, but the pre-generated
configure scripts remain functional for cross-compilation.
Recipe change: Removed the autotools_recursive_regenerate call and
the COOKBOOK_AUTORECONF override from the fetch script. Only the
config.sub/config.guess/install-sh copy step remains.
Package: gettext (GNU gettext 0.22.5)
Recipe: recipes/tools/gettext/recipe.toml
Source: https://ftp.gnu.org/gnu/gettext/gettext-0.22.5.tar.gz
Template: custom (uses cookbook_configure)
Quirk: libintl.a Not Installed by Default
Problem: gettext's make install skips installing libintl.a and
libintl.h. The Makefile has install-exec-libintl commented out
with the note: "We must not install the libintl.h/libintl.la files."
Downstream packages (glib, etc.) that depend on intl via meson's
dependency lookup fail with:
Run-time dependency intl found: NO (tried builtin and system)
Root cause: gettext's configure detects the C library might already
provide gettext functions and disables separate libintl installation.
On Redox (relibc), gettext functions are NOT provided by the C library,
so libintl must be installed separately.
Fix: Two changes in the recipe build script:
- Pass
--with-included-gettextto configure to force buildinglibintlfrom the included source. - Manually install
libintl.aandlibintl.haftercookbook_configure:
cookbook_configure
cp -f gettext-runtime/intl/.libs/libgnuintl.a "${COOKBOOK_STAGE}/usr/lib/libintl.a"
cp -f gettext-runtime/intl/libgnuintl.h "${COOKBOOK_STAGE}/usr/include/libintl.h"
The library is built as libgnuintl.a (GNU internal name) and must be
installed as libintl.a (POSIX name that meson/pkg-config expects).
Package: glib (GLib 2.87.0)
Recipe: recipes/libs/glib/recipe.toml
Source: https://download.gnome.org/sources/glib/2.87/glib-2.87.0.tar.xz
Template: custom (uses cookbook_meson)
Quirk: Source Tarball Contains .orig Files (False Positive in Patch Checker)
Problem: glib's source tarball legitimately includes .orig files
as part of its test suite:
gio/tests/org.gtk.test.gschema.xml.orig
gio/tests/org.gtk.test.gschema.override.orig
The cookbook's atomic patch checker (src/cook/fetch.rs) detects .orig
files after patch application and treats them as evidence of failed hunks,
rolling back the patch and aborting the build.
Root cause: The patch checker did not distinguish between pre-existing
.orig files (from the source tarball) and newly-created .orig files
(from patch command failures).
Fix: Updated the atomic patch checker in src/cook/fetch.rs to
snapshot pre-existing .orig files before patch application, and only
flag .orig files that were NOT in the pre-existing set. The
--no-backup-if-mismatch flag (already used) prevents patch from
creating new .orig files, so any .orig file found after patching
that was in the pre-existing set is safe to ignore.
Package: relibc (Redox C Library — local fork)
Recipe: recipes/core/relibc/recipe.toml (uses path = "../../../local/sources/relibc")
Source: local/sources/relibc/ (local fork)
Quirk 1: cbindgen Generates ... Instead of va_list
Problem: cbindgen translates Rust's VaList<'_> type as ...
(variadic) in generated C headers. This affects all v*printf functions:
// Generated (WRONG):
int vsnprintf(char *s, size_t n, const char *format, ...);
// Expected (CORRECT):
int vsnprintf(char *s, size_t n, const char *format, va_list ap);
This breaks C++ standard library code that takes the address of
vsnprintf (e.g., std::to_string(float) → __to_xstring(&std::vsnprintf, ...)),
causing compilation failures in Qt6 and other C++ packages.
Root cause: cbindgen does not natively understand the va_list crate's
VaList type and defaults to generating ....
Fix: Post-generation sed in the relibc recipe build script:
sed -i '/^int vfprintf(/s/\\.\\.\\./va_list ap/' "${COOKBOOK_STAGE}/usr/include/stdio.h"
sed -i '/^int vsnprintf(/s/\\.\\.\\./va_list ap/' "${COOKBOOK_STAGE}/usr/include/stdio.h"
sed -i '/^int vsprintf(/s/\\.\\.\\./va_list ap/' "${COOKBOOK_STAGE}/usr/include/stdio.h"
Note: The \\. sequences are required because TOML triple-quoted
strings interpret \\ as a literal \, producing the sed pattern \.\.\.
Quirk 2: VaList API Version Mismatch Between Toolchains
Problem: relibc HEAD uses VaList<'a> (1 lifetime) and
VaList::next_arg() from Rust 1.98-dev. The PREFIX toolchain
(nightly-2025-11-15) has the older VaList<'a, 'f: 'a> (2 lifetimes)
with no next_arg() method.
When building via make live, mk/prefix.mk:24 exports
REDOXER_TOOLCHAIN=PREFIX, forcing the older Rust. This causes
compilation errors in relibc.
Fix: Override RUSTUP_TOOLCHAIN in the relibc recipe build script
to use the redoxer default toolchain (~/.redoxer/${TARGET}/toolchain,
Rust 1.98-dev) when REDOXER_TOOLCHAIN is set and not pointing to a
.partial directory (PREFIX bootstrap):
REDOXER_TC="${REDOXER_TOOLCHAIN:-}"
if [ -n "${REDOXER_TC}" ] && [[ "${REDOXER_TC}" != *".partial"* ]]; then
REDOXER_DEFAULT="${HOME}/.redoxer/${TARGET}/toolchain"
if [ -d "${REDOXER_DEFAULT}/lib/rustlib/src/rust/library/core" ]; then
export RUSTUP_TOOLCHAIN="${REDOXER_DEFAULT}"
export PATH="${REDOXER_DEFAULT}/bin:${PATH}"
fi
fi
Quirk 3: wchar.h / inttypes.h Circular Include Chain
Problem: relibc's generated wchar.h included <stdio.h> (via cbindgen
sys_includes) before defining wint_t and mbstate_t. And inttypes.h
included <wchar.h> for wchar_t instead of using stddef.h per POSIX spec.
This created a circular include chain that broke gnulib-based packages (m4,
bison, flex, etc.).
Error:
error: unknown type name 'wint_t'
error: unknown type name 'mbstate_t'
Root cause: Two intertwined issues in relibc's cbindgen header generation:
wchar/cbindgen.tomllistedstdio.handtime.hinsys_includes, which cbindgen emits at the top of the generated header, before thewchar_t/wint_t/mbstate_ttypedefs. When a gnulib wrapper did#include_next <wchar.h>, the preprocessor pulled instdio.h->stdint.h->inttypes.h->wchar.hagain before any of those types were defined.inttypes/cbindgen.tomllistedwchar.hinsys_includes. Per POSIX,inttypes.honly needswchar_t(fromstddef.h), not the fullwchar.h. Includingwchar.hclosed the circular chain.
Fix: Both changes are in the relibc fork (local/sources/relibc/):
src/header/wchar/cbindgen.toml:sys_includes = ["features.h"]only. The type definitions (wchar_t,wint_t,mbstate_t) and thestdio.h/time.hincludes were moved toafter_includes, so the typedefs land before any transitive include.mbstate_tis defined inafter_includesunder a_RELIBC_MBSTATE_Tguard, and excluded from cbindgen export via[export] exclude = ["mbstate_t"].src/header/inttypes/cbindgen.toml:sys_includes = ["stdint.h", "stddef.h"](POSIX spec compliant, breaks the circular chain by removing theinttypes.h->wchar.hedge).
Commits: d28963d (wchar.h fix), a2e4cd2 (inttypes.h fix) in
local/sources/relibc/.
Reference: The m4 section (Quirk 5) documents the same fix from the consuming package's perspective.
Package: qtbase (Qt 6 Base — local recipe)
Recipe: local/recipes/qt/qtbase/recipe.toml
Source: local/recipes/qt/qtbase/source/ (local overlay with tar source)
Template: custom
Quirk 1: Source Tarball Not Extracted for Local Overlay Recipes
Problem: The cookbook treats local/recipes/qt/qtbase/source/ as a
local overlay (immutable). The is_local_overlay() check prevents
wiping and re-extracting the source from source.tar. As a result, only
the Red Bear-specific files (tests/, util/) exist in source/, but
the full Qt6 source code is missing.
Error:
FileNotFoundError: No such file or directory: '.../source/src/tools/CMakeLists.txt'
Fix: Manually extract the source tarball into the source/ directory:
cd local/recipes/qt/qtbase
tar xf source.tar --strip-components=1 -C source/
This must be done once after updating the source tarball. Patches must also be applied manually since the cookbook won't apply them to local overlay sources:
cd source
patch -p1 --batch --no-backup-if-mismatch < ../redox.patch
Affected recipes: All Qt6 local overlay recipes with tar sources:
qtbase, qt5compat, qtdeclarative, qtsvg, qtshadertools, qtwayland.
Quirk 2: forkfd.h Missing <sys/resource.h> Include
Problem: src/3rdparty/forkfd/forkfd.h declares functions using
struct rusage but only includes <sys/wait.h>. On Linux glibc,
<sys/wait.h> transitively includes <sys/resource.h> (which defines
struct rusage). On Redox/relibc, this transitive include doesn't exist.
Error:
forkfd.h:57: warning: 'struct rusage' declared inside parameter list
forkfd.c:918: error: conflicting types for 'forkfd_wait4'
Fix: Add #include <sys/resource.h> to forkfd.h:
#include <fcntl.h>
#include <stdint.h>
#include <sys/wait.h>
#include <sys/resource.h> // Added for struct rusage
Quirk 3: netinet/in6_pktinfo_compat.h Not Available
Problem: src/network/socket/qnativesocketengine_unix.cpp includes
<netinet/in6_pktinfo_compat.h>, which relibc does not provide.
Fix: The P0-fix-broken-include.patch removes this include line.
Ensure the patch is applied to the source (see Quirk 1 about manual
patch application).
Package: redox-driver-sys (Driver System Library)
Recipe: local/recipes/drivers/redox-driver-sys/
Source: local/recipes/drivers/redox-driver-sys/source/ (local recipe)
Quirk: Inline Assembly Register Constraints for I/O Port Access
Problem: The io.rs module uses inline assembly for x86 IN/OUT
instructions with generic register constraints (in(reg), out(reg_byte)).
The x86 IN/OUT instructions require specific registers:
- Port: must be
DX - Data: must be
AL(byte),AX(word), orEAX(dword)
Generic constraints let the compiler choose any register, producing
invalid instructions like outb ax, esi instead of outb dx, al.
Error:
error: invalid operand for instruction
--> <inline asm>:2:2
|
2 | outb ax, esi
| ^
Fix: Use explicit register operands in src/io.rs:
pub fn inb(port: u16) -> u8 {
let val: u8;
unsafe { core::arch::asm!("in al, dx", in("dx") port, out("al") val,
options(nomem, nostack, preserves_flags)) };
val
}
pub fn outb(port: u16, val: u8) {
unsafe { core::arch::asm!("out dx, al", in("dx") port, in("al") val,
options(nomem, nostack, preserves_flags)) };
}
Apply the same pattern for inw/outw (ax/dx) and inl/outl
(eax/dx).
Package: mesa (Mesa 3D Graphics Library)
Recipe: recipes/libs/mesa/recipe.toml
Source: https://gitlab.redox-os.org/redox-os/mesa.git (branch redox-24.0)
Template: custom (meson-based)
Status (2026-06-20): ALL RED BEAR PATCHES WIRED
Mesa builds successfully for the x86_64-unknown-redox target. The recipe's patches=[...]
list contains all six Red Bear Mesa patches, in order:
| # | Patch | Purpose |
|---|---|---|
| 01 | local/patches/mesa/01-virgl-redox-disk-cache.patch |
Nullifies virgl's disk shader cache on Redox (no XDG_CACHE_HOME semantics on Redox) |
| 02 | local/patches/mesa/02-gbm-dumb-prime-export.patch |
Adds gbm_dri_bo_get_fd fallback path via DRM dumb buffers (PRIME export on Redox) |
| 03 | local/patches/mesa/03-platform-redox-gpu-probe.patch |
EGL platform probe: tells platform_redox.c to enumerate /scheme/drm/cardN and try virgl-capable GPUs |
| 04 | local/patches/mesa/04-sys-ioccom-stub-header.patch |
New file include/sys/ioccom.h (63 lines) implementing BSD-style IOCPARM_LEN / _IO / _IOW / _IOR macros for DRM UAPI ioctl encoding |
| 05 | local/patches/mesa/05-vk-sync-wchar-include.patch |
Adds #include <wchar.h> to vk_sync.h for wchar_t (relibc does not transitively pull in <wchar.h> via <vulkan/vulkan_core.h> the way glibc does) |
| 06 | local/patches/mesa/06-redox-surface-image-fields.patch |
Adds __DRIimage *dri_image_back; and __DRIimage *dri_image_front; to the HAVE_REDOX_PLATFORM section of egl_dri2.h (see quirk below) |
Build artifacts (verified 2026-06-20 12:30):
recipes/libs/mesa/target/x86_64-unknown-redox/stage/usr/lib/dri/virtio_gpu_dri.so(17,474,080 bytes)stage.pkgarpublished tolocal/cache/pkgar/mesa/
Quirk: dri_image_back / dri_image_front fields missing from Redox platform section (RESOLVED)
Problem: src/egl/drivers/dri2/platform_redox.c references
dri2_surf->dri_image_back and dri2_surf->dri_image_front, but these fields are only
defined inside #ifdef HAVE_ANDROID_PLATFORM in egl_dri2.h. The Redox platform section
(#ifdef HAVE_REDOX_PLATFORM) only had orb_window and orb_surface.
Error (prior to fix):
platform_redox.c:65:18: error: 'struct dri2_egl_surface' has no member named 'dri_image_back'
Root cause: When 03-platform-redox-gpu-probe.patch was added to teach
platform_redox.c to allocate GBM-backed images for the EGL surface (so the virgl probe
can attach to a real GPU), it referenced dri_image_back and dri_image_front. These
fields existed only for the Android platform because the original EGL/Redox port only used
orb_window / orb_surface (the Orbital display server's native window/buffer types).
A complete Redox EGL platform needs both the Orbital legacy fields AND the DRI image
fields so a Wayland/DRM-style compositor can attach GBM-backed buffers.
Fix: Patch 06-redox-surface-image-fields.patch adds the missing fields to the
HAVE_REDOX_PLATFORM section of src/egl/drivers/dri2/egl_dri2.h:
#ifdef HAVE_REDOX_PLATFORM
void *orb_window;
void *orb_surface;
__DRIimage *dri_image_back;
__DRIimage *dri_image_front;
#endif
The patch is a 13-line unified diff. With this fix:
platform_redox.c(post-03-platform-redox-gpu-probe.patch) compiles cleanlyredox_free_images()can now free the GBM-backed images- The EGL platform probe can attach a virgl backend to a Redox DRM/KMS surface
Verification: Mesa cook succeeds, virtio_gpu_dri.so links, stage.pkgar produced.
Runtime caveat: The build is verified. The runtime EGL platform selection
(EGL_PLATFORM=redox, choosing virtio_gpu_dri.so vs swrast_dri.so) is not yet
runtime-validated in a QEMU -device virtio-vga-gl boot. This is the last remaining gap
for hardware-accelerated SDDM greeter rendering.
Why -Dstatic_assert(...)=
Mesa's upstream configure (meson) emits -Dstatic_assert=_Static_assert when
_Static_assert is unavailable on the target. On glibc/Linux, this collides with the
<sys/cdefs.h> static_assert macro. Redox's relibc doesn't define static_assert as
a macro, so the explicit = nullifies the macro and lets Mesa use the C11 keyword
_Static_assert instead. The recipe applies this via
-Dc_args="['-Wno-error=implicit-function-declaration','-Wno-error','-std=gnu11','-Dstatic_assert=_Static_assert']".
Package: sddm (Simple Desktop Display Manager — Wayland-only build)
Recipe: local/recipes/kde/sddm/recipe.toml
Source: https://github.com/sddm/sddm.git (rev 63780fcd79f1dbf81a30eef48c28c699ab15aded)
Template: custom (cmake-based)
**Status (2026-06-20): BUILDS — Wayland-only, all X11 excluded
Build-Side Quirks
SDDM's CMake build assumes X11/XCB/XAUTH are available. Red Bear OS has none of these. The recipe handles this in three coordinated ways:
-
wayland-patch.sh(~245 lines, sed-based) — applied at build time to:- Remove
find_package(XCB ...)calls (CMakeLists) - Remove XCB include/library refs in
src/{daemon,helper,greeter}/CMakeLists.txt - Replace
cmake/FindXKB.cmake's xcb-xkb lookups with xkbcommon - Remove
XcbKeyboardBackend.cppfrom the greeter - Rewrite
src/common/XAuth.cppas a Wayland-only stub - Replace
Qt5.15.0 CONFIG REQUIRED ...with the components SDDM needs (Core, DBus, Gui, Network, Qml, Quick) + OPTIONAL_COMPONENTS LinguistTools/Test/QuickTest - Gate
qt_add_translationonQt6LinguistTools_FOUND - Guard the
test/CMakeLists.txtblock onQt6Test_FOUND AND Qt6QuickTest_FOUND
- Remove
-
remove-x11user-helper.py— strips thesddm-helper-start-x11useradd_executable andinstall(TARGETS sddm-helper-start-x11user ...)fromsrc/helper/CMakeLists.txt, plus thetarget_link_libraries(sddm-helper-start-x11user ${JOURNALD_LIBRARIES})line. -
stubs/directory — provides fake headers for headers SDDM#includes but relibc does not yet provide:linux/kd.h,linux/vt.h— VT ioctl definitions (SDDM only uses the ioctl numbers, not the kernel-side semantics)utmpx.h— POSIXutmpx(SDDM only compiles against it; runtime stub)X11/Xauth.h— minimal stub matching the rewrittenXAuth.cpp(Wayland-only)
Note on stubs: Per local/AGENTS.md zero-tolerance policy, the X11/Xauth.h stub
is a temporary carrier because the corresponding XAuth.cpp is rewritten as a no-op for
Wayland-only mode. When relibc gains a real utmpx.h, the utmpx.h stub here should
be removed and SDDM should compile against the relibc version. The linux/kd.h and
linux/vt.h stubs should be replaced by a proper redbear-input-headers-style header
package in a future cleanup pass.
CMake flags that make the build work
The recipe passes these critical flags:
cmake "${COOKBOOK_SOURCE}" \
-DCMAKE_TOOLCHAIN_FILE="${COOKBOOK_ROOT}/local/recipes/qt/redox-toolchain.cmake" \
-DQT_HOST_PATH="${HOST_BUILD}" \
-DKF6_HOST_TOOLING="${HOST_BUILD}/lib/cmake" \
-DBUILD_TESTING=OFF \
-DBUILD_WITH_QT6=ON \
-DNO_SYSTEMD=ON \
-DENABLE_JOURNALD=OFF \
-DENABLE_PAM=ON \
-DUID_MIN=1000 \
-DUID_MAX=65000 \
-DLIBXKB_INCLUDE_DIR="${COOKBOOK_SYSROOT}/usr/include" \
-DLIBXKB_LIBRARIES="${COOKBOOK_SYSROOT}/usr/lib/libxkbcommon.so" \
-Wno-dev
Key flags:
-DBUILD_WITH_QT6=ON— required; SDDM defaults to Qt5-DNO_SYSTEMD=ON— required; no systemd on Redox-DENABLE_JOURNALD=OFF— required; no journald on Redox-DENABLE_PAM=ON— required;pam-redbearprovideslibpam.so.0-DUID_MIN=1000 -DUID_MAX=65000— required at CMake time; SDDM hardcodes these viaadd_definitionsif not provided-DQT_HOST_PATHand-DKF6_HOST_TOOLING— required formoc/qmltyperegistrar/ECMs tools that must run on the host (they cannot cross-compile for the Redox target)
Build verification (2026-06-20)
cook sddm - successful(0 errors)- Stage output:
usr/bin/sddm— main daemonusr/bin/sddm-greeter-qt6— QML greeter binaryusr/libexec/sddm-helper— session spawn helperusr/libexec/sddm-helper-start-wayland— Wayland session starter (forks compositor + greeter)usr/share/sddm/— themes, scripts, translationsetc/pam.d/{sddm,sddm-greeter,sddm-autologin}— PAM service files
patchelf --set-rpath "/usr/lib"applied to all binaries so dynamic linking works against the sysroot at runtime
Runtime caveat
The QML greeter (sddm-greeter-qt6) is a Qt6 Wayland client and is therefore gated on
the Qt6 Wayland null+8 crash fix (see CONSOLE-TO-KDE-DESKTOP-PLAN.md §3.1). The
daemon and helpers will start under init, but the greeter QML window will not appear
until the null+8 fix is runtime-validated.
General Pattern: Source Tarball Extraction for Local Overlay Recipes
Problem: When a recipe lives under local/recipes/ (local overlay),
the cookbook's is_local_overlay() check prevents wiping and re-extracting
the source from source.tar. If the source/ directory only contains
Red Bear-specific files (tests, utils, etc.), the main source code is
missing.
Affected recipes: Any local overlay recipe that uses tar = "...":
qtbase,qt5compat,qtdeclarative,qtsvg,qtshadertools,qtwayland- Possibly others added in the future
Fix: Extract the tarball manually before building:
cd <recipe-directory>
tar xf source.tar --strip-components=1 -C source/
# Apply patches manually:
cd source
for patch in ../*.patch; do
patch -p1 --batch --no-backup-if-mismatch < "$patch" || true
done
# Clean up rejects:
find . -name "*.rej" -delete
Future improvement: The cookbook could be enhanced to support
"extract-and-overlay" mode for local recipes with tar sources —
extracting the tarball first, then overlaying the local source/ files.
Adding a New Quirk
When you encounter a new build quirk:
- Document it here — add a section under the appropriate package, or create a new package section.
- Explain the root cause — why does this happen?
- Show the fix — what change resolves it?
- Note the placement — should the fix go before or after
DYNAMIC_INIT? - Is it a cross-compilation pattern? — if so, add it to the General Patterns section so future recipes can benefit.
- Update the recipe — apply the fix to
recipe.toml. - Update UPSTREAM-SYNC-PROCEDURE.md — if the quirk is related to upstream sync.
Package: base-initfs (initfs image builder)
pcid Must Start Before pcid-spawner
Problem: pcid-spawner calls fs::read_dir("/scheme/pci") as its first
action. /scheme/pci is created by pcid via register_sync_scheme(). If
pcid is not started, pcid-spawner fails with ENODEV.
Root cause: No init service started pcid in initfs. The upstream model
assumed pcid would be started, but no .service file existed for it. This
was masked in 0.2.3 because driver-manager (Red Bear's replacement) does
its own PCI enumeration via redox_driver_pci and doesn't depend on
/scheme/pci.
Fix: Added 35_pcid.service to init.initfs.d/:
[unit]
description = "PCI bus enumeration daemon (creates /scheme/pci)"
requires_weak = ["30_acpid.service"]
[service]
cmd = "pcid"
type = { scheme = "pci" }
The type = { scheme = "pci" } tells init to wait for /scheme/pci to be
registered before marking the service as started. This ensures pcid-spawner
(which depends on 35_pcid.service via requires_weak) only runs after
/scheme/pci exists.
driver-manager Removed During Sync
Problem: The 0.2.3 → 0.2.4 upstream sync silently removed driver-manager
from base-initfs dependencies and redbear-device-services.toml packages.
Root cause: Upstream doesn't have driver-manager — it's a Red Bear
in-house project. The sync blindly overwrote tracked files with upstream
versions.
Fix: Restored:
"driver-manager"inbase-initfs/recipe.tomldependenciescp driver-managerbinary copy to initfs/bin/driver-manager = {}inredbear-device-services.toml[packages]drivers.d/config directory creation in build scriptset -eo pipefailin build script
See also: UPSTREAM-SYNC-PROCEDURE.md § "driver-manager: Upstream Not Ready"
Redox Flag Values (Critical for Ported Code)
Redox flag values differ from Linux. Code ported from Linux that assumes Linux conventions will be WRONG on Redox:
| Flag | Linux | Redox |
|---|---|---|
O_RDONLY |
0 |
0x0001_0000 |
O_WRONLY |
1 |
0x0002_0000 |
O_RDWR |
2 |
0x0003_0000 |
O_ACCMODE |
3 |
0x0003_0000 |
O_CLOEXEC |
0x80000 |
0x0100_0000 |
Common bug: flags & O_ACCMODE == O_RDONLY evaluates to 0 == 0 on Linux
(read-only check passes), but on Redox O_CLOEXEC & O_ACCMODE = 0 ≠ O_RDONLY,
so a file opened with O_CLOEXEC without explicit O_RDONLY fails with EROFS.
Fix: Always pass O_RDONLY explicitly when opening for read:
let fd = Fd::open(path, O_RDONLY | O_CLOEXEC, 0)?;
Kernel kcall on ACPI Scheme
Problem: Upstream-synced acpid uses AcpiVerb call API (ReadRxsdt=1,
CheckShutdown=2) but the kernel's AcpiScheme didn't implement kcall,
returning EOPNOTSUPP.
Fix: Added kcall method to impl KernelScheme for AcpiScheme in
src/scheme/acpi.rs. The kernel fork commit is c7af6c43.
Signature:
fn kcall(&self, fds: &[usize], payload: UserSliceRw, flags: CallFlags,
metadata: &[u64], token: &mut CleanLockToken) -> Result<usize>
fds[0]= handle idmetadata[0]= verb (u64):1= ReadRxsdt,2= CheckShutdownpayload= UserSliceRw (read/write buffer)
Cross-Reference
| Document | Relevance |
|---|---|
local/docs/UPSTREAM-SYNC-PROCEDURE.md |
Per-package sync notes include build quirks |
AGENTS.md |
Project-level build commands and conventions |
local/AGENTS.md |
Local fork model, recipe structure |
src/cook/script.rs |
DYNAMIC_INIT, reexport_flags, cookbook_cmake definitions |
src/recipe.rs |
Recipe TOML schema (cmakeflags, configureflags, etc.) |