From b77449faeb979901f9727ebb3530d783e3136c9c Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 7 Aug 2026 16:27:09 +0200 Subject: [PATCH] fix: toolchain - update docs --- toolchain/README.md | 82 ++++++++++++++++++++++++++-- toolchain/build-montauk-toolchain.sh | 16 ++++-- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/toolchain/README.md b/toolchain/README.md index 60df51e..af9546b 100644 --- a/toolchain/README.md +++ b/toolchain/README.md @@ -38,9 +38,80 @@ What the target does by default: wanted. - Defines `__montauk__` / `__MONTAUK__`. -C++ works with `-fno-exceptions -fno-rtti` (the project convention). -There is no libstdc++ yet — only libgcc is built. A hosted libstdc++ -needs a more complete libc and is the next step toward self-hosting. +The in-tree programs are built `-fno-exceptions -fno-rtti` (the project +convention), but that is a project style choice, not a toolchain limit — +see the STL section below. + +## C++ standard library (hosted libstdc++) + +`build-montauk-toolchain.sh` builds and installs a **hosted** libstdc++ +(`all-target-libstdc++-v3`), so the full STL is available to +`x86_64-montauk-g++` with no special flags: + +```bash +toolchain/local/bin/x86_64-montauk-g++ app.cpp -o app.elf +``` + +Headers live in `toolchain/local/x86_64-montauk/include/c++/14.2.0/`, +the library in `toolchain/local/x86_64-montauk/lib/libstdc++.a`. +`_GLIBCXX_HOSTED` follows `__STDC_HOSTED__`, i.e. 1 for ordinary +compiles. + +Note that `toolchain/sysroot/usr/include/` *also* holds a freestanding +C++ subset (copied from `kernel/freestnd-cxx-hdrs`, which is what the +kernel and the in-tree programs build against). That directory is last +on the include search path, so for a plain `g++` invocation the hosted +headers win and the freestanding copies are shadowed. Check +`g++ -x c++ -E -v -` if you ever need to confirm the order. + +### What works + +Link-verified with the cross compiler, no flags beyond the source file: + +| Feature | Status | +|---------|--------| +| ``, ``, ``, `` | works | +| `` (`unique_ptr`, `shared_ptr`, `make_*`) | works | +| Exceptions (`throw` / `catch`, `std::exception`) | works | +| RTTI (`typeid`, `dynamic_cast`) | works | +| ``, `` | works (links a large binary — see below) | +| Static constructors | works (`crt1.c` runs `.init_array`) | + +`toolchain/files/cxx-test.cpp` is the on-OS regression check for this; +`make devkit` compiles it to `0:/sdk/bin/cxx-test.elf`. Run it on the +target to confirm the above at **runtime** rather than just at link +time — that is the authoritative check, since a clean link does not by +itself prove exception unwinding works on hardware. + +### What does not work + +- **Threads.** libstdc++ is built `gthr-single` + (`_GLIBCXX_HAS_GTHREADS` is undefined), because the libc has no + pthreads. `std::thread`, `std::mutex`, `std::condition_variable`, + `std::async` and `std::call_once` do not exist — code using them + fails to *compile*, not to link. + + Consequence worth knowing: in single-threaded mode libstdc++ selects + the **non-atomic** `shared_ptr` refcount path. The kernel does have + real threads (`SYS_THREAD_SPAWN/EXIT/JOIN/SELF`, + `programs/include/montauk/thread.h`), so it is possible to spawn + threads and share a `shared_ptr` across them — that will corrupt the + refcount and is a silent use-after-free. Do not do it until a + pthreads/gthreads shim exists and libstdc++ is rebuilt against it. + +- **`` is expensive.** It drags in the static locale and + iostream init machinery; a hello-world using it links to roughly + 5.7 MB versus ~1.8 MB for `` + ``. Prefer `` + where binary size matters, which on a ramdisk image is most places. + +- **`libm.a` is an empty stand-in.** The math functions live inside + `libc.a` (the g++ driver links `-lm` unconditionally, hence the empty + archive). Roughly 50 functions are present — `sqrt`, `sin`, `cos`, + `pow`, `log`, `exp`, `atan2`, `hypot`, `floor`, `ceil`, `fmod`, + `round` and some `float` variants. Absent: `fma`, `cbrt`, `expm1`, + `log1p`, `erf`, `tgamma`, `nearbyint`, `remquo`, most remaining + `float` variants, and all `long double` variants. Ports that need + these have to add them to `programs/lib/libc/libc.c`. ## Refreshing the sysroot @@ -146,7 +217,10 @@ Notes: - The `devkit` target in programs/GNUmakefile ships the staged tools into the OS image at 0:/sdk/bin (as.elf, ld.elf, ar.elf, ...) with a target-side sysroot at 0:/sdk/include + 0:/sdk/lib (libc.a, crt - objects, empty libm/libstdc++) - the Montauk SDK. The kernel + objects, the real hosted libstdc++.a plus its c++/ headers, and an + empty libm stand-in) - the Montauk SDK. On-OS g++ therefore gets the + same STL as the cross compiler; see the C++ standard library section + above for what is and is not supported. The kernel resolves driveless absolute paths ("/sdk/lib") against the cwd drive, so the --prefix=/sdk layout works natively. The shell searches 0:/sdk/bin and tab-completes it. Future ports configure diff --git a/toolchain/build-montauk-toolchain.sh b/toolchain/build-montauk-toolchain.sh index f508783..1f9b1e2 100755 --- a/toolchain/build-montauk-toolchain.sh +++ b/toolchain/build-montauk-toolchain.sh @@ -121,8 +121,12 @@ cp -r "${REPO_DIR}/programs/include/libc/." "${SYSROOT}/usr/include/" cp -r "${REPO_DIR}/programs/include/montauk" "${SYSROOT}/usr/include/montauk" cp -r "${REPO_DIR}/programs/include/Api" "${SYSROOT}/usr/include/Api" # Freestanding C++ headers (, , , ...) — -# there is no hosted libstdc++ yet, so C++ programs get the same -# freestanding subset the rest of the tree builds against. +# the subset the kernel and the in-tree programs build against. This is +# NOT the whole C++ story: a hosted libstdc++ is built further down and +# installs its own headers under ${PREFIX}/x86_64-montauk/include/c++. +# That directory comes first on the include path and this one comes +# last, so a plain g++ invocation gets the hosted headers and these are +# shadowed. See "C++ standard library" in README.md. cp -r "${REPO_DIR}/kernel/freestnd-cxx-hdrs/x86_64/include/." "${SYSROOT}/usr/include/" cp "${REPO_DIR}/programs/lib/libc/crt1.o" \ "${REPO_DIR}/programs/lib/libc/crti.o" \ @@ -161,9 +165,11 @@ else fi # ── Build GCC ───────────────────────────────────────────────────────────────── -# Hosted against the Montauk libc headers in the sysroot. Only the -# compiler and libgcc are built; libstdc++ needs a more complete libc -# (that is the self-hosting step, not the cross-toolchain step). +# Hosted against the Montauk libc headers in the sysroot. Builds the +# compiler, libgcc, and a hosted libstdc++ (full STL: string/vector/map, +# exceptions, RTTI, iostream). libstdc++ is built gthr-single because +# the libc has no pthreads, so / are absent and +# shared_ptr uses non-atomic refcounts. See README.md. if [[ -x "${PREFIX}/bin/${TARGET}-gcc" ]]; then bold "GCC already installed — skipping." else