The cross toolchain now builds real hosted C++: std::string, vector, unique_ptr, iostreams and exceptions all work in a plain x86_64-montauk-g++ invocation with no special flags. This is the library that native cc1plus will link against for the GCC self-host. crt1 now runs .preinit_array/.init_array before main and hooks .fini_array through atexit (main returns via exit(), so destructors run on both exit paths). Global constructors and libgcc's eh_frame registration both ride this. GCC is reconfigured with --enable-initfini-array (modern arrays instead of .ctors), --with-newlib (satisfies libstdc++'s crossconfig for unknown hosts), --disable-wchar_t and --disable-libstdcxx-pch; the montauk gcc patch grows a hunk keeping os/generic ctype under --with-newlib (the Montauk libc is not newlib). libc additions harvested by the libstdc++ build: mbtowc/wctomb, strxfrm, strtok/strtok_r, a real vfscanf/fscanf/scanf (character- driven with widths and l/ll; also unblocks gprof later), fgetpos/ fsetpos/setbuf/fpos_t, modf plus the C99 float math variants (acosf..tanhf, hypot/hypotf as wrappers), FP_* classification macros, the full POSIX errno vocabulary, complete DT_* dirent types, and mbstate_t. The SDK ships the real libstdc++.a (was an empty stand-in), the C++ headers at 0:/sdk/include/c++, and cxx-test.elf (global ctor, string, vector+sort, unique_ptr, throw/catch; exits 0 on 5/5). Ramdisk sits at 1571 files, within the 2048 table. Co-Authored-By: Claude Fable 5 <[email protected]>
209 lines
8.9 KiB
Bash
Executable File
209 lines
8.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# build-montauk-toolchain.sh — Build an x86_64-montauk cross compiler
|
|
# (Binutils + GCC) for MontaukOS userspace programs.
|
|
#
|
|
# Usage: ./toolchain/build-montauk-toolchain.sh
|
|
#
|
|
# Unlike the bare-metal x86_64-elf toolchain (build-toolchain.sh), this
|
|
# target knows the Montauk userspace ABI: it links static ET_EXEC
|
|
# binaries against the Montauk libc (crt1/crti/crtn + libc.a from
|
|
# programs/lib/libc), defaults to -mno-red-zone, forces 4 KiB segment
|
|
# alignment for the kernel ELF loader, and defines __montauk__.
|
|
#
|
|
# The toolchain is installed to toolchain/local/ and provides:
|
|
# x86_64-montauk-gcc, x86_64-montauk-g++, x86_64-montauk-ld, etc.
|
|
#
|
|
# The target sysroot (libc headers + libs) is assembled into
|
|
# toolchain/sysroot/ from programs/include/libc and programs/lib/libc.
|
|
# Re-run this script after changing libc headers or rebuilding libc to
|
|
# refresh the sysroot (the sysroot step always runs; compiler builds
|
|
# are skipped once installed).
|
|
#
|
|
# This script is idempotent — it skips steps whose output already exists.
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Versions (kept in lockstep with build-toolchain.sh) ───────────────────────
|
|
BINUTILS_VERSION="2.43.1"
|
|
GCC_VERSION="14.2.0"
|
|
TARGET="x86_64-montauk"
|
|
|
|
# ── Directories ───────────────────────────────────────────────────────────────
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_DIR="$(dirname "${SCRIPT_DIR}")"
|
|
PREFIX="${SCRIPT_DIR}/local"
|
|
SRC_DIR="${SCRIPT_DIR}/src"
|
|
BUILD_DIR="${SCRIPT_DIR}/build"
|
|
SYSROOT="${SCRIPT_DIR}/sysroot"
|
|
PATCH_DIR="${SCRIPT_DIR}/patches"
|
|
FILES_DIR="${SCRIPT_DIR}/files"
|
|
|
|
BINUTILS_TARBALL="binutils-${BINUTILS_VERSION}.tar.xz"
|
|
GCC_TARBALL="gcc-${GCC_VERSION}.tar.xz"
|
|
BINUTILS_URL="https://ftp.gnu.org/gnu/binutils/${BINUTILS_TARBALL}"
|
|
GCC_URL="https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}/${GCC_TARBALL}"
|
|
|
|
JOBS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)"
|
|
|
|
# ── Color helpers ─────────────────────────────────────────────────────────────
|
|
bold() { printf '\033[1m%s\033[0m\n' "$*"; }
|
|
green() { printf '\033[1;32m%s\033[0m\n' "$*"; }
|
|
red() { printf '\033[1;31m%s\033[0m\n' "$*"; }
|
|
|
|
bold "=== Building ${TARGET} cross compiler ==="
|
|
bold " Binutils ${BINUTILS_VERSION} | GCC ${GCC_VERSION}"
|
|
bold " Install prefix: ${PREFIX}"
|
|
bold " Sysroot: ${SYSROOT}"
|
|
bold " Parallel jobs: ${JOBS}"
|
|
echo
|
|
|
|
export PATH="${PREFIX}/bin:${PATH}"
|
|
|
|
mkdir -p "${SRC_DIR}" "${BUILD_DIR}" "${PREFIX}"
|
|
|
|
# ── Download + extract sources (shared with build-toolchain.sh) ───────────────
|
|
download() {
|
|
local url="$1" dest="$2"
|
|
if [[ -f "${dest}" ]]; then
|
|
bold "Already downloaded: $(basename "${dest}")"
|
|
else
|
|
bold "Downloading $(basename "${dest}")..."
|
|
wget -q --show-progress -O "${dest}" "${url}"
|
|
fi
|
|
}
|
|
|
|
extract() {
|
|
local tarball="$1" name="$2"
|
|
if [[ -d "${SRC_DIR}/${name}" ]]; then
|
|
bold "Already extracted: ${name}"
|
|
else
|
|
bold "Extracting ${name}..."
|
|
tar xf "${tarball}" -C "${SRC_DIR}"
|
|
fi
|
|
}
|
|
|
|
download "${BINUTILS_URL}" "${SRC_DIR}/${BINUTILS_TARBALL}"
|
|
download "${GCC_URL}" "${SRC_DIR}/${GCC_TARBALL}"
|
|
extract "${SRC_DIR}/${BINUTILS_TARBALL}" "binutils-${BINUTILS_VERSION}"
|
|
extract "${SRC_DIR}/${GCC_TARBALL}" "gcc-${GCC_VERSION}"
|
|
|
|
if [[ ! -d "${SRC_DIR}/gcc-${GCC_VERSION}/gmp" ]]; then
|
|
bold "Downloading GCC prerequisites..."
|
|
(cd "${SRC_DIR}/gcc-${GCC_VERSION}" && ./contrib/download_prerequisites)
|
|
fi
|
|
|
|
# ── Apply Montauk target patches ──────────────────────────────────────────────
|
|
apply_patch() {
|
|
local dir="$1" patch="$2"
|
|
if grep -q "montauk" "${dir}/config.sub"; then
|
|
bold "Already patched: $(basename "${dir}")"
|
|
else
|
|
bold "Patching $(basename "${dir}")..."
|
|
patch -d "${dir}" -p1 < "${patch}"
|
|
fi
|
|
}
|
|
|
|
apply_patch "${SRC_DIR}/binutils-${BINUTILS_VERSION}" "${PATCH_DIR}/binutils-${BINUTILS_VERSION}-montauk.patch"
|
|
apply_patch "${SRC_DIR}/gcc-${GCC_VERSION}" "${PATCH_DIR}/gcc-${GCC_VERSION}-montauk.patch"
|
|
cp "${FILES_DIR}/montauk.h" "${SRC_DIR}/gcc-${GCC_VERSION}/gcc/config/montauk.h"
|
|
|
|
# ── Assemble the target sysroot ───────────────────────────────────────────────
|
|
# usr/include — the Montauk libc headers (self-contained C; stddef/stdint/
|
|
# stdarg come from GCC itself) plus the montauk/ SDK headers.
|
|
# usr/lib — crt startup objects and the libc static library.
|
|
bold "Assembling sysroot from programs/lib/libc..."
|
|
make -C "${REPO_DIR}/programs" libc >/dev/null
|
|
|
|
rm -rf "${SYSROOT}"
|
|
mkdir -p "${SYSROOT}/usr/include" "${SYSROOT}/usr/lib"
|
|
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 (<cstdint>, <type_traits>, <atomic>, ...) —
|
|
# there is no hosted libstdc++ yet, so C++ programs get the same
|
|
# freestanding subset the rest of the tree builds against.
|
|
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" \
|
|
"${REPO_DIR}/programs/lib/libc/crtn.o" "${SYSROOT}/usr/lib/"
|
|
cp "${REPO_DIR}/programs/lib/libc/liblibc.a" "${SYSROOT}/usr/lib/libc.a"
|
|
# libm is an empty stand-in (math lives inside libc.a; the g++ driver
|
|
# links -lm unconditionally). libstdc++ is copied from the toolchain
|
|
# once built - the empty stand-in only bridges the first bootstrap.
|
|
ar rcs "${SYSROOT}/usr/lib/libm.a"
|
|
if [[ -f "${PREFIX}/x86_64-montauk/lib/libstdc++.a" ]]; then
|
|
cp "${PREFIX}/x86_64-montauk/lib/libstdc++.a" "${SYSROOT}/usr/lib/libstdc++.a"
|
|
else
|
|
ar rcs "${SYSROOT}/usr/lib/libstdc++.a"
|
|
fi
|
|
green "Sysroot ready."
|
|
|
|
# ── Build Binutils ────────────────────────────────────────────────────────────
|
|
if [[ -x "${PREFIX}/bin/${TARGET}-ld" ]]; then
|
|
bold "Binutils already installed — skipping."
|
|
else
|
|
bold "Building Binutils ${BINUTILS_VERSION} for ${TARGET}..."
|
|
rm -rf "${BUILD_DIR}/binutils-montauk"
|
|
mkdir -p "${BUILD_DIR}/binutils-montauk"
|
|
(
|
|
cd "${BUILD_DIR}/binutils-montauk"
|
|
"${SRC_DIR}/binutils-${BINUTILS_VERSION}/configure" \
|
|
--target="${TARGET}" \
|
|
--prefix="${PREFIX}" \
|
|
--with-sysroot="${SYSROOT}" \
|
|
--disable-nls \
|
|
--disable-werror
|
|
make -j"${JOBS}"
|
|
make install
|
|
)
|
|
green "Binutils installed."
|
|
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).
|
|
if [[ -x "${PREFIX}/bin/${TARGET}-gcc" ]]; then
|
|
bold "GCC already installed — skipping."
|
|
else
|
|
bold "Building GCC ${GCC_VERSION} for ${TARGET}..."
|
|
rm -rf "${BUILD_DIR}/gcc-montauk"
|
|
mkdir -p "${BUILD_DIR}/gcc-montauk"
|
|
(
|
|
cd "${BUILD_DIR}/gcc-montauk"
|
|
"${SRC_DIR}/gcc-${GCC_VERSION}/configure" \
|
|
--target="${TARGET}" \
|
|
--prefix="${PREFIX}" \
|
|
--with-sysroot="${SYSROOT}" \
|
|
--enable-languages=c,c++ \
|
|
--disable-nls \
|
|
--disable-shared \
|
|
--disable-multilib \
|
|
--disable-gcov \
|
|
--with-gnu-as \
|
|
--with-gnu-ld \
|
|
--with-newlib \
|
|
--enable-initfini-array \
|
|
--disable-wchar_t \
|
|
--disable-libstdcxx-pch
|
|
make -j"${JOBS}" all-gcc
|
|
make -j"${JOBS}" all-target-libgcc
|
|
make install-gcc
|
|
make install-target-libgcc
|
|
make -j"${JOBS}" all-target-libstdc++-v3
|
|
make install-target-libstdc++-v3
|
|
)
|
|
green "GCC and libstdc++ installed."
|
|
fi
|
|
|
|
# ── Done ──────────────────────────────────────────────────────────────────────
|
|
echo
|
|
green "=== ${TARGET} cross compiler ready ==="
|
|
"${PREFIX}/bin/${TARGET}-gcc" --version | head -1
|
|
"${PREFIX}/bin/${TARGET}-ld" --version | head -1
|
|
echo
|
|
bold "Try it:"
|
|
bold " echo 'int main(){return 0;}' > /tmp/t.c"
|
|
bold " ${PREFIX}/bin/${TARGET}-gcc /tmp/t.c -o /tmp/t.elf"
|