From 66918102f88ebc892dc6f6340436138e228025e4 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Fri, 3 Apr 2026 16:53:38 -0300 Subject: [PATCH] =?utf8?q?feat(toolchain):=20dual-libc=20support=20?= =?utf8?q?=E2=80=94=20ulibc=20default,=20Newlib=20optional?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Redesign the toolchain build to safely support both ulibc (dynamic linking) and Newlib (static linking) with a safe-by-default approach: Architecture: - adros.h now has Newlib-safe defaults (LIB_SPEC with -ladros, no dynamic-linker in LINK_SPEC) so a fresh 'git clone' + 'build.sh' always produces a working toolchain even without prior 'make iso'. - Step 4b: generates specs from dumpspecs (Newlib-compatible). - Step 4c: if ulibc build artifacts exist, re-patches specs for ulibc (removes -ladros, adds -dynamic-linker /lib/ld.so). - Step 4d: creates newlib.specs + i686-adros-gcc-newlib wrapper for optional Newlib builds. Bug fixes for fresh-clone reproducibility: - Newlib build skip check changed from libc.a (overwritten by ulibc) to a .built marker file. - Newlib headers/libs backed up unconditionally (before ulibc check) so newlib.specs always has valid include/newlib/ directory. - Bash cross-compilation now uses newlib.specs explicitly (Bash needs Newlib's full POSIX libc: regex, locale, wchar). - Wrapper script uses expanded paths instead of hardcoded GCC version. Usage: i686-adros-gcc -o out in.c # ulibc dynamic (default) i686-adros-gcc -static -o out in.c # ulibc static i686-adros-gcc-newlib -o out in.c # Newlib static --- .gitignore | 4 ++ toolchain/build.sh | 158 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 143 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index b40c275e..d34e8665 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,7 @@ toolchain/logs/ # Third-party dependencies are git submodules — do NOT ignore them # third_party/lwip -> git submodule # user/doom/doomgeneric -> git submodule + +# Others +hello +hello.c diff --git a/toolchain/build.sh b/toolchain/build.sh index 62f44e72..4c95ec73 100755 --- a/toolchain/build.sh +++ b/toolchain/build.sh @@ -225,7 +225,12 @@ i[34567]86-*-adros*)\ #define LIB_SPEC "--start-group -lc -ladros --end-group -lgcc" #undef LINK_SPEC -#define LINK_SPEC "-m elf_i386 %{shared:-shared} %{static:-static} %{!static: %{rdynamic:-export-dynamic}}" +#define LINK_SPEC \ + "-m elf_i386 -z noexecstack " \ + "%{shared:-shared} " \ + "%{!shared:-Ttext-segment=0x00400000} " \ + "%{static:-static} " \ + "%{rdynamic:-export-dynamic}" #undef SIZE_TYPE #define SIZE_TYPE "unsigned int" @@ -246,7 +251,7 @@ ADROS_H if ! grep -q 'adros' "$d/libgcc/config.host"; then sed -i '/^x86_64-\*-elf\* | x86_64-\*-rtems\*)/i\ i[34567]86-*-adros*)\ -\ttmake_file="$tmake_file i386/t-crtstuff t-crtstuff-pic t-libgcc-pic"\ +\ttmake_file="$tmake_file i386/t-crtstuff i386/t-adros t-crtstuff-pic t-libgcc-pic"\ \textra_parts="$extra_parts crti.o crtn.o crtbegin.o crtend.o"\ \t;;' "$d/libgcc/config.host" step "Patched libgcc/config.host" @@ -314,6 +319,19 @@ EOF step "Created crti-adros.S / crtn-adros.S" fi + # t-adros — override CRT build rules to use AdrOS-specific sources + if [[ ! -f "$d/libgcc/config/i386/t-adros" ]]; then + cat > "$d/libgcc/config/i386/t-adros" <<'EOF' +# Build AdrOS CRT files from custom sources (with .note.GNU-stack) +crti.o: $(srcdir)/config/i386/crti-adros.S + $(gcc_compile) -c $< + +crtn.o: $(srcdir)/config/i386/crtn-adros.S + $(gcc_compile) -c $< +EOF + step "Created libgcc/config/i386/t-adros" + fi + touch "$marker" } @@ -535,7 +553,7 @@ msg "Building Newlib ${NEWLIB_VER}" mkdir -p "$BUILD_DIR/newlib" cd "$BUILD_DIR/newlib" -if [[ ! -f "${SYSROOT}/lib/libc.a" ]]; then +if [[ ! -f "$BUILD_DIR/newlib/.built" ]]; then "$SRC_DIR/newlib-${NEWLIB_VER}/configure" \ --target="$TARGET" \ --prefix="$PREFIX" \ @@ -548,6 +566,7 @@ if [[ ! -f "${SYSROOT}/lib/libc.a" ]]; then make -j"$JOBS" 2>&1 | tee "$LOG_DIR/newlib-build.log" make install 2>&1 | tee "$LOG_DIR/newlib-install.log" + touch "$BUILD_DIR/newlib/.built" step "Newlib installed to sysroot" else step "Newlib already installed" @@ -673,18 +692,116 @@ else fi # ================================================================== -# STEP 4b: Create GCC specs file (fix LIB_SPEC link order) +# STEP 4b: Create GCC specs file (Newlib-safe defaults) # ================================================================== # Must run AFTER step 4 — make install-gcc overwrites the GCC lib directory. +# Generates a specs file matching adros.h (Newlib-compatible): +# LIB_SPEC: --start-group -lc -ladros --end-group -lgcc +# LINK_SPEC: noexecstack, base 0x00400000, static-friendly (no dynamic-linker) +# +# If ulibc is available (step 4c), the specs will be re-patched for ulibc. SPECS_FILE="$PREFIX/lib/gcc/${TARGET}/${GCC_VER}/specs" -if [[ ! -f "$SPECS_FILE" ]]; then - ${TARGET}-gcc -dumpspecs > "$SPECS_FILE" - sed -i 's/-lc -ladros -lgcc/--start-group -lc -ladros --end-group -lgcc/' "$SPECS_FILE" - step "Created specs file with corrected LIB_SPEC" +${TARGET}-gcc -dumpspecs > "$SPECS_FILE" +# Ensure LINK_SPEC has noexecstack and AdrOS base address +if ! grep -q 'noexecstack' "$SPECS_FILE"; then + sed -i '/^\*link:/{n;s|.*|-m elf_i386 -z noexecstack %{shared:-shared} %{!shared:-Ttext-segment=0x00400000} %{static:-static} %{rdynamic:-export-dynamic}|}' "$SPECS_FILE" +fi +step "Created specs file (Newlib defaults)" + +# ================================================================== +# STEP 4c: Install ulibc runtime to sysroot +# ================================================================== +# The toolchain uses ulibc as its C library (replacing Newlib for +# user-facing builds). ulibc provides both static (libc.a) and +# shared (libc.so) libraries plus the dynamic linker (ld.so). +# +# ulibc is built by 'make iso' via i686-elf-gcc. We copy the +# pre-built artifacts into the sysroot. If they don't exist yet, +# the user must run 'make ARCH=x86 iso' first. +msg "Installing ulibc runtime to sysroot" +ULIBC_BUILDDIR="${ADROS_ROOT}/build/x86/user/ulibc" +LDSO_BUILDDIR="${ADROS_ROOT}/build/x86/user/cmds/ldso" +ULIBC_INCDIR="${ADROS_ROOT}/user/ulibc/include" +mkdir -p "${SYSROOT}/lib" + +_ulibc_ok=1 +for _f in "$ULIBC_BUILDDIR/crt0.o" "$ULIBC_BUILDDIR/libulibc.a" \ + "$ULIBC_BUILDDIR/libc.so" "$LDSO_BUILDDIR/ld.so"; do + if [[ ! -f "$_f" ]]; then + step "WARNING: $_f not found — run 'make ARCH=x86 iso' first" + _ulibc_ok=0 + fi +done + +# Always back up Newlib headers so newlib.specs can find them +if [[ ! -d "${SYSROOT}/include/newlib" ]]; then + msg "Backing up Newlib headers to include/newlib/" + mkdir -p "${SYSROOT}/include/newlib" + cp -r "${SYSROOT}/include"/* "${SYSROOT}/include/newlib/" 2>/dev/null || true + rm -rf "${SYSROOT}/include/newlib/newlib" # no recursion +fi + +# Always back up Newlib libs so newlib.specs can find them +[[ -f "${SYSROOT}/lib/libc.a" && ! -f "${SYSROOT}/lib/libnewlib.a" ]] && \ + cp "${SYSROOT}/lib/libc.a" "${SYSROOT}/lib/libnewlib.a" +[[ -f "${SYSROOT}/lib/crt0.o" && ! -f "${SYSROOT}/lib/crt0-newlib.o" ]] && \ + cp "${SYSROOT}/lib/crt0.o" "${SYSROOT}/lib/crt0-newlib.o" + +if [[ $_ulibc_ok -eq 1 ]]; then + # Replace Newlib with ulibc as the default libc + cp "$ULIBC_BUILDDIR/crt0.o" "${SYSROOT}/lib/crt0.o" + cp "$ULIBC_BUILDDIR/libulibc.a" "${SYSROOT}/lib/libc.a" + cp "$ULIBC_BUILDDIR/libc.so" "${SYSROOT}/lib/libc.so" + cp "$LDSO_BUILDDIR/ld.so" "${SYSROOT}/lib/ld.so" + + # Install ulibc headers (overwrite Newlib headers for standard names) + cp -r "$ULIBC_INCDIR"/* "${SYSROOT}/include/" + + # Re-patch specs file for ulibc: remove -ladros, add dynamic-linker + sed -i 's/--start-group -lc -ladros --end-group -lgcc/-lc -lgcc/' "$SPECS_FILE" + sed -i 's/-lc -ladros -lgcc/-lc -lgcc/' "$SPECS_FILE" + sed -i '/^\*link:/{n;s|.*|-m elf_i386 -z noexecstack %{shared:-shared} %{!shared:-Ttext-segment=0x00400000} %{static:-static} %{!static:%{!shared:-dynamic-linker /lib/ld.so}} %{rdynamic:-export-dynamic}|}' "$SPECS_FILE" + + step "ulibc installed — dynamic linking by default, static with -static" else - step "Specs file already exists" + step "ulibc NOT installed — toolchain will use Newlib (static only)" fi +# ================================================================== +# STEP 4d: Create newlib.specs for optional Newlib builds +# ================================================================== +# Users can compile with Newlib instead of ulibc: +# i686-adros-gcc -specs=/newlib.specs -o out in.c +# i686-adros-gcc-newlib -o out in.c (wrapper script) +# +# Newlib mode is always static (Newlib has no shared library). +NEWLIB_SPECS="$PREFIX/lib/gcc/${TARGET}/${GCC_VER}/newlib.specs" +cat > "$NEWLIB_SPECS" < "$PREFIX/bin/${TARGET}-gcc-newlib" <&1 | tee "$LOG_DIR/bash-configure.log" make -j"$JOBS" 2>&1 | tee "$LOG_DIR/bash-build.log" @@ -922,16 +1039,19 @@ echo " Target: $TARGET" echo " Sysroot: $SYSROOT" echo "" echo " Tools:" -echo " ${TARGET}-gcc — C compiler" -echo " ${TARGET}-g++ — C++ compiler" -echo " ${TARGET}-ld — Linker" -echo " ${TARGET}-as — Assembler" -echo " ${TARGET}-ar — Archiver" -echo " ${TARGET}-objdump — Disassembler" +echo " ${TARGET}-gcc — C compiler (ulibc, dynamic by default)" +echo " ${TARGET}-gcc-newlib — C compiler (Newlib, static only)" +echo " ${TARGET}-g++ — C++ compiler" +echo " ${TARGET}-ld — Linker" +echo " ${TARGET}-as — Assembler" +echo " ${TARGET}-ar — Archiver" +echo " ${TARGET}-objdump — Disassembler" echo "" echo " Usage:" echo " export PATH=${PREFIX}/bin:\$PATH" -echo " ${TARGET}-gcc -o hello hello.c" +echo " ${TARGET}-gcc -o hello hello.c # dynamic (ulibc)" +echo " ${TARGET}-gcc -static -o hello hello.c # static (ulibc)" +echo " ${TARGET}-gcc-newlib -o hello hello.c # static (Newlib)" echo "" if [[ $SKIP_BASH -eq 0 ]]; then echo " Bash: $BUILD_DIR/bash/bash" -- 2.43.0