#!/usr/bin/env bash
#
# AdrOS Toolchain Builder
#
# Builds a complete i686-adros cross-compilation toolchain:
# 1. Binutils (assembler, linker)
# 2. GCC (bootstrap, C only, no libc headers)
# 3. Newlib (C library)
# 4. GCC (full rebuild with Newlib sysroot)
# 5. Bash (optional, cross-compiled)
#
# Usage:
# ./toolchain/build.sh [--prefix /opt/adros] [--jobs 4] [--skip-bash]
#
# Prerequisites:
# - GCC (host), G++, Make, Texinfo, GMP, MPFR, MPC, ISL (dev packages)
# - wget or git for downloading sources
# - ~4 GB disk space
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ADROS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# ---- Defaults ----
PREFIX="/opt/adros-toolchain"
TARGET="i686-adros"
JOBS="$(nproc 2>/dev/null || echo 4)"
SKIP_BASH=0
# ---- Versions ----
BINUTILS_VER="2.42"
GCC_VER="13.2.0"
NEWLIB_VER="4.4.0.20231231"
BASH_VER="5.2.21"
BINUTILS_URL="https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER}.tar.xz"
GCC_URL="https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER}/gcc-${GCC_VER}.tar.xz"
NEWLIB_URL="https://sourceware.org/pub/newlib/newlib-${NEWLIB_VER}.tar.gz"
BASH_URL="https://ftp.gnu.org/gnu/bash/bash-${BASH_VER}.tar.gz"
# ---- Parse args ----
while [[ $# -gt 0 ]]; do
case "$1" in
--prefix) PREFIX="$2"; shift 2 ;;
--jobs) JOBS="$2"; shift 2 ;;
--skip-bash) SKIP_BASH=1; shift ;;
--help|-h)
echo "Usage: $0 [--prefix DIR] [--jobs N] [--skip-bash]"
exit 0 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
SYSROOT="${PREFIX}/${TARGET}"
BUILD_DIR="${ADROS_ROOT}/toolchain/build"
SRC_DIR="${ADROS_ROOT}/toolchain/src"
PATCH_DIR="${ADROS_ROOT}/toolchain/patches"
LOG_DIR="${ADROS_ROOT}/toolchain/logs"
export PATH="${PREFIX}/bin:${PATH}"
msg() { echo -e "\n\033[1;32m==>\033[0m \033[1m$1\033[0m"; }
err() { echo -e "\n\033[1;31m==> ERROR:\033[0m $1" >&2; exit 1; }
step() { echo -e " \033[1;34m->\033[0m $1"; }
mkdir -p "$BUILD_DIR" "$SRC_DIR" "$LOG_DIR" "$SYSROOT"
# ---- Download sources ----
download() {
local url="$1" dest="$2"
if [[ -f "$dest" ]]; then
step "Already downloaded: $(basename "$dest")"
return
fi
step "Downloading $(basename "$dest")..."
wget -q --show-progress -O "$dest" "$url"
}
extract() {
local archive="$1" dir="$2"
if [[ -d "$dir" ]]; then
step "Already extracted: $(basename "$dir")"
return
fi
step "Extracting $(basename "$archive")..."
case "$archive" in
*.tar.xz) tar xf "$archive" -C "$SRC_DIR" ;;
*.tar.gz) tar xzf "$archive" -C "$SRC_DIR" ;;
*) err "Unknown archive type: $archive" ;;
esac
}
msg "Downloading sources"
download "$BINUTILS_URL" "$SRC_DIR/binutils-${BINUTILS_VER}.tar.xz"
download "$GCC_URL" "$SRC_DIR/gcc-${GCC_VER}.tar.xz"
download "$NEWLIB_URL" "$SRC_DIR/newlib-${NEWLIB_VER}.tar.gz"
[[ $SKIP_BASH -eq 0 ]] && download "$BASH_URL" "$SRC_DIR/bash-${BASH_VER}.tar.gz"
msg "Extracting sources"
extract "$SRC_DIR/binutils-${BINUTILS_VER}.tar.xz" "$SRC_DIR/binutils-${BINUTILS_VER}"
extract "$SRC_DIR/gcc-${GCC_VER}.tar.xz" "$SRC_DIR/gcc-${GCC_VER}"
extract "$SRC_DIR/newlib-${NEWLIB_VER}.tar.gz" "$SRC_DIR/newlib-${NEWLIB_VER}"
[[ $SKIP_BASH -eq 0 ]] && extract "$SRC_DIR/bash-${BASH_VER}.tar.gz" "$SRC_DIR/bash-${BASH_VER}"
# ---- Apply AdrOS target patches (sed-based, robust against line shifts) ----
# Patch config.sub to recognise 'adros' as an OS.
# Two insertions: (1) canonicalisation case, (2) OS validation list.
patch_config_sub() {
local f="$1"
# 1) Add adros case before the pikeos case in the canonicalisation switch
if ! grep -q 'adros' "$f"; then
sed -i '/^[[:space:]]*pikeos\*)/i\
\tadros*)\
\t\tos=adros\
\t\t;;' "$f"
# 2) Add adros* to the validation list (same line as dicos*)
sed -i 's/| dicos\*/| dicos* | adros*/' "$f"
step "Patched $(basename "$(dirname "$f")")/config.sub"
fi
}
patch_binutils() {
local d="$SRC_DIR/binutils-${BINUTILS_VER}"
local marker="$d/.adros_patched"
[[ -f "$marker" ]] && { step "Binutils already patched"; return; }
patch_config_sub "$d/config.sub"
# bfd/config.bfd — add before i[3-7]86-*-linux-*
if ! grep -q 'adros' "$d/bfd/config.bfd"; then
sed -i '/^ i\[3-7\]86-\*-linux-\*)/i\
i[3-7]86-*-adros*)\
targ_defvec=i386_elf32_vec\
targ_selvecs=\
targ64_selvecs=x86_64_elf64_vec\
;;' "$d/bfd/config.bfd"
step "Patched bfd/config.bfd"
fi
# gas/configure.tgt — add after i386-*-darwin*
if ! grep -q 'adros' "$d/gas/configure.tgt"; then
sed -i '/i386-\*-darwin\*)/a\
i386-*-adros*)\t\t\t\tfmt=elf ;;' "$d/gas/configure.tgt"
step "Patched gas/configure.tgt"
fi
# ld/configure.tgt — add before i[3-7]86-*-linux-*
if ! grep -q 'adros' "$d/ld/configure.tgt"; then
sed -i '/^i\[3-7\]86-\*-linux-\*)/i\
i[3-7]86-*-adros*)\ttarg_emul=elf_i386\
\t\t\t\t;;' "$d/ld/configure.tgt"
step "Patched ld/configure.tgt"
fi
# gas/config/tc-i386.c — fix type mismatch (uint32_t vs unsigned int)
# The header declares uint32_t but the source used unsigned int.
if grep -q 'x86_scfi_callee_saved_p (unsigned int' "$d/gas/config/tc-i386.c" 2>/dev/null; then
sed -i 's/x86_scfi_callee_saved_p (unsigned int dw2reg_num)/x86_scfi_callee_saved_p (uint32_t dw2reg_num)/' \
"$d/gas/config/tc-i386.c"
step "Patched gas/config/tc-i386.c (uint32_t fix)"
fi
touch "$marker"
}
patch_gcc() {
local d="$SRC_DIR/gcc-${GCC_VER}"
local marker="$d/.adros_patched"
[[ -f "$marker" ]] && { step "GCC already patched"; return; }
patch_config_sub "$d/config.sub"
# gcc/config.gcc — add before x86_64-*-elf*
if ! grep -q 'adros' "$d/gcc/config.gcc"; then
sed -i '/^x86_64-\*-elf\*)/i\
i[34567]86-*-adros*)\
\ttm_file="${tm_file} i386/unix.h i386/att.h elfos.h newlib-stdint.h i386/adros.h"\
\ttmake_file="${tmake_file} i386/t-crtstuff"\
\tuse_gcc_stdint=wrap\
\tdefault_use_cxa_atexit=yes\
\t;;' "$d/gcc/config.gcc"
step "Patched gcc/config.gcc"
fi
# gcc/config/i386/adros.h — create target header
if [[ ! -f "$d/gcc/config/i386/adros.h" ]]; then
cat > "$d/gcc/config/i386/adros.h" <<'ADROS_H'
/* Target definitions for i386 AdrOS. */
#undef TARGET_OS_CPP_BUILTINS
#define TARGET_OS_CPP_BUILTINS() \
do { \
builtin_define ("__adros__"); \
builtin_define ("__AdrOS__"); \
builtin_define ("__unix__"); \
builtin_assert ("system=adros"); \
builtin_assert ("system=unix"); \
builtin_assert ("system=posix"); \
} while (0)
#undef OBJECT_FORMAT_ELF
#define OBJECT_FORMAT_ELF 1
#undef TARGET_64BIT_DEFAULT
#define TARGET_64BIT_DEFAULT 0
#undef STARTFILE_SPEC
#define STARTFILE_SPEC "crt0.o%s crti.o%s crtbegin.o%s"
#undef ENDFILE_SPEC
#define ENDFILE_SPEC "crtend.o%s crtn.o%s"
#undef LIB_SPEC
#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}}"
#undef SIZE_TYPE
#define SIZE_TYPE "unsigned int"
#undef PTRDIFF_TYPE
#define PTRDIFF_TYPE "int"
#undef WCHAR_TYPE
#define WCHAR_TYPE "int"
#undef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE 32
ADROS_H
step "Created gcc/config/i386/adros.h"
fi
# libgcc/config.host — add before x86_64-*-elf*
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"\
\textra_parts="$extra_parts crti.o crtn.o crtbegin.o crtend.o"\
\t;;' "$d/libgcc/config.host"
step "Patched libgcc/config.host"
fi
# GCC prerequisites (GMP, MPFR, MPC, ISL) — patch config.sub for adros
# These are only present if contrib/download_prerequisites was run.
for sub in \
"$d/gmp/configfsf.sub" \
"$d/mpfr-*/config.sub" \
"$d/mpc-*/build-aux/config.sub" \
"$d/isl-*/config.sub"; do
# Expand glob
for f in $sub; do
[[ -f "$f" ]] || continue
if ! grep -q 'adros' "$f"; then
# GMP uses configfsf.sub with a different format
if [[ "$f" == *"configfsf.sub" ]]; then
sed -i 's/| nsk\* | powerunix\* | genode\* | zvmoe\* | qnx\* | emx\* \\$/\| nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* \\\n\t | adros*)/' "$f"
else
sed -i 's/| -midnightbsd\*)/| -midnightbsd* | -adros*)/' "$f"
sed -i 's/| nsk\* | powerunix\*)/| nsk* | powerunix* | adros*)/' "$f"
fi
step "Patched $(echo "$f" | sed "s|$d/||") for adros"
fi
done
done
# Remove libcody from host_libs (requires C++ host compiler, breaks Canadian cross)
if grep -q 'libcody' "$d/configure" 2>/dev/null; then
sed -i 's/ libcody / /' "$d/configure"
step "Removed libcody from GCC host_libs"
fi
# crti.S and crtn.S for AdrOS
if [[ ! -f "$d/libgcc/config/i386/crti-adros.S" ]]; then
cat > "$d/libgcc/config/i386/crti-adros.S" <<'EOF'
/* crti.S for AdrOS — .init/.fini prologue */
.section .init
.global _init
.type _init, @function
_init:
push %ebp
mov %esp, %ebp
.section .fini
.global _fini
.type _fini, @function
_fini:
push %ebp
mov %esp, %ebp
EOF
cat > "$d/libgcc/config/i386/crtn-adros.S" <<'EOF'
/* crtn.S for AdrOS — .init/.fini epilogue */
.section .init
pop %ebp
ret
.section .fini
pop %ebp
ret
.section .note.GNU-stack,"",@progbits
EOF
step "Created crti-adros.S / crtn-adros.S"
fi
touch "$marker"
}
patch_newlib() {
local d="$SRC_DIR/newlib-${NEWLIB_VER}"
local marker="$d/.adros_patched"
[[ -f "$marker" ]] && { step "Newlib already patched"; return; }
patch_config_sub "$d/config.sub"
# newlib/configure.host — add after i[34567]86-*-rdos* block
# have_crt0="no" because crt0.o is provided by libgloss/adros, not libc/sys
if ! grep -q 'adros' "$d/newlib/configure.host"; then
sed -i '/i\[34567\]86-\*-rdos\*)/,/;;/{/;;/a\
i[34567]86-*-adros*)\
\tsys_dir=adros\
\thave_crt0="no"\
\tnewlib_cflags="${newlib_cflags} -DSIGNAL_PROVIDED -DHAVE_OPENDIR -DHAVE_SYSTEM"\
\t;;
}' "$d/newlib/configure.host"
step "Patched newlib/configure.host"
fi
# newlib/libc/include/sys/config.h — add after __rtems__ block
if ! grep -q '__adros__' "$d/newlib/libc/include/sys/config.h"; then
sed -i '/#if defined(__rtems__)/,/#endif/{/#endif/a\
\
/* AdrOS target configuration */\
#ifdef __adros__\
#define _READ_WRITE_RETURN_TYPE int\
#define HAVE_SYSTEM\
#define HAVE_OPENDIR\
#endif
}' "$d/newlib/libc/include/sys/config.h"
step "Patched newlib/libc/include/sys/config.h"
fi
# Create newlib/libc/sys/adros/ stub directory (non-recursive Makefile.inc)
# This is empty because all syscalls are in libgloss/adros.
if [[ ! -d "$d/newlib/libc/sys/adros" ]]; then
mkdir -p "$d/newlib/libc/sys/adros"
cat > "$d/newlib/libc/sys/adros/Makefile.inc" <<'EOF'
## AdrOS system directory — empty (syscalls provided by libgloss/adros)
EOF
step "Created newlib/libc/sys/adros/"
fi
# libgloss/configure.ac — add after i[[3456]]86-*-elf* block
if ! grep -q 'adros' "$d/libgloss/configure.ac"; then
sed -i '/i\[\[3456\]\]86-\*-elf\* | i\[\[3456\]\]86-\*-coff\*)/,/;;/{/;;/a\
i[[3456]]86-*-adros*)\
\tAC_CONFIG_FILES([adros/Makefile])\
\tsubdirs="$subdirs adros"\
\t;;
}' "$d/libgloss/configure.ac"
step "Patched libgloss/configure.ac"
fi
# Copy/sync our libgloss/adros stubs into the Newlib source tree
mkdir -p "$d/libgloss/adros"
cp -u "$ADROS_ROOT/newlib/libgloss/adros/"*.{c,S,h} "$d/libgloss/adros/" 2>/dev/null || true
step "Synced libgloss/adros/ stubs"
# Create libgloss/adros autoconf files if not present
if [[ ! -f "$d/libgloss/adros/configure.in" ]]; then
cat > "$d/libgloss/adros/configure.in" <<'EOF'
dnl AdrOS libgloss configure
AC_PREREQ(2.59)
AC_INIT([libgloss-adros],[0.1])
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE([cygnus])
AM_MAINTAINER_MODE
AC_PROG_CC
AC_PROG_AS
AC_PROG_AR
AC_PROG_RANLIB
AM_PROG_AS
host_makefile_frag=${srcdir}/../config/default.mh
AC_SUBST(host_makefile_frag)
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
EOF
step "Created libgloss/adros/configure.in"
fi
if [[ ! -f "$d/libgloss/adros/Makefile.in" ]]; then
cat > "$d/libgloss/adros/Makefile.in" <<'EOF'
# Makefile for AdrOS libgloss (autotools-generated template)
srcdir = @srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
tooldir = $(exec_prefix)/$(target_alias)
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
CC = @CC@
AS = @AS@
AR = @AR@
RANLIB = @RANLIB@
CFLAGS = -g
BSP = libadros.a
OBJS = syscalls.o
CRT0 = crt0.o
all: $(CRT0) $(BSP)
$(BSP): $(OBJS)
$(AR) rcs $@ $^
$(RANLIB) $@
crt0.o: crt0.S
$(CC) $(CFLAGS) -c $< -o $@
syscalls.o: syscalls.c
$(CC) $(CFLAGS) -c $< -o $@
install: all
$(INSTALL_DATA) $(CRT0) $(tooldir)/lib/$(CRT0)
$(INSTALL_DATA) $(BSP) $(tooldir)/lib/$(BSP)
clean:
rm -f $(OBJS) $(CRT0) $(BSP)
.PHONY: all install clean
EOF
step "Created libgloss/adros/Makefile.in"
fi
touch "$marker"
}
patch_bash() {
local d="$SRC_DIR/bash-${BASH_VER}"
[[ ! -d "$d" ]] && return
local marker="$d/.adros_patched"
[[ -f "$marker" ]] && { step "Bash already patched"; return; }
patch_config_sub "$d/support/config.sub"
touch "$marker"
}
msg "Applying AdrOS target patches"
patch_binutils
patch_gcc
patch_newlib
patch_bash
# ---- Install AdrOS-specific sysroot headers ----
# NOTE: We do NOT copy the full ulibc headers here because they conflict
# with Newlib's POSIX-compliant headers (signal.h, errno.h, stdio.h, etc.).
# ulibc headers are for AdrOS userland built with -nostdlib; the cross-
# toolchain uses Newlib headers instead. Only truly AdrOS-specific headers
# (syscall numbers, ioctl defs) are installed.
msg "Installing AdrOS-specific sysroot headers"
mkdir -p "${SYSROOT}/include/sys"
for h in syscall.h; do
if [[ -f "$ADROS_ROOT/user/ulibc/include/$h" ]]; then
cp "$ADROS_ROOT/user/ulibc/include/$h" "${SYSROOT}/include/$h"
step "Installed $h"
fi
done
# Install Linux/POSIX compatibility headers from newlib/sysroot_headers/.
# These provide stubs for headers that newlib doesn't supply but that
# ported software (Bash, Busybox) expects: asm/*, linux/*, net/*,
# netinet/*, sys/socket.h, poll.h, mntent.h, etc.
COMPAT_HEADERS="$ADROS_ROOT/newlib/sysroot_headers"
if [[ -d "$COMPAT_HEADERS" ]]; then
cp -r "$COMPAT_HEADERS"/* "${SYSROOT}/include/"
step "Installed $(find "$COMPAT_HEADERS" -type f | wc -l) sysroot compat headers"
fi
# Patch newlib headers that need small AdrOS-specific additions.
# sys/stat.h — expose lstat()/mknod() for __adros__ (newlib guards them)
if ! grep -q '__adros__' "${SYSROOT}/include/sys/stat.h" 2>/dev/null; then
sed -i 's/defined(__SPU__) || defined(__rtems__) || defined(__CYGWIN__)/defined(__SPU__) || defined(__rtems__) || defined(__CYGWIN__) || defined(__adros__)/' \
"${SYSROOT}/include/sys/stat.h"
step "Patched sys/stat.h (lstat/mknod for __adros__)"
fi
# sys/signal.h — add SA_RESTART and friends to the non-rtems block
if ! grep -q 'SA_RESTART' "${SYSROOT}/include/sys/signal.h" 2>/dev/null; then
sed -i '/^#define SA_NOCLDSTOP 1/a\
#define SA_RESTART 0x10000000\
#define SA_NODEFER 0x40000000\
#define SA_RESETHAND 0x80000000\
#define SA_NOCLDWAIT 0x20000000\
#define SA_SIGINFO 0x2' \
"${SYSROOT}/include/sys/signal.h"
step "Patched sys/signal.h (SA_RESTART etc.)"
fi
# sys/wait.h — add WCOREDUMP macro
if ! grep -q 'WCOREDUMP' "${SYSROOT}/include/sys/wait.h" 2>/dev/null; then
sed -i '/#define WTERMSIG/a\
#define WCOREDUMP(w) ((w) \& 0x80)' \
"${SYSROOT}/include/sys/wait.h"
step "Patched sys/wait.h (WCOREDUMP)"
fi
# glob.h — add GLOB_NOMATCH
if ! grep -q 'GLOB_NOMATCH' "${SYSROOT}/include/glob.h" 2>/dev/null; then
sed -i '/#define.*GLOB_ABEND/a\
#define GLOB_NOMATCH (-3) /* No match found. */' \
"${SYSROOT}/include/glob.h"
step "Patched glob.h (GLOB_NOMATCH)"
fi
# ==================================================================
# STEP 1: Build Binutils
# ==================================================================
msg "Building Binutils ${BINUTILS_VER}"
mkdir -p "$BUILD_DIR/binutils"
cd "$BUILD_DIR/binutils"
if [[ ! -f "$PREFIX/bin/${TARGET}-ld" ]]; then
"$SRC_DIR/binutils-${BINUTILS_VER}/configure" \
--target="$TARGET" \
--prefix="$PREFIX" \
--with-sysroot="$SYSROOT" \
--disable-nls \
--disable-werror \
2>&1 | tee "$LOG_DIR/binutils-configure.log"
make -j"$JOBS" 2>&1 | tee "$LOG_DIR/binutils-build.log"
make install 2>&1 | tee "$LOG_DIR/binutils-install.log"
step "Binutils installed: ${PREFIX}/bin/${TARGET}-ld"
else
step "Binutils already installed"
fi
# ==================================================================
# STEP 2: Build GCC (bootstrap — no libc)
# ==================================================================
msg "Building GCC ${GCC_VER} (bootstrap, C only)"
mkdir -p "$BUILD_DIR/gcc-bootstrap"
cd "$BUILD_DIR/gcc-bootstrap"
if [[ ! -f "$PREFIX/bin/${TARGET}-gcc" ]]; then
"$SRC_DIR/gcc-${GCC_VER}/configure" \
--target="$TARGET" \
--prefix="$PREFIX" \
--with-sysroot="$SYSROOT" \
--without-headers \
--enable-languages=c \
--disable-nls \
--disable-shared \
--disable-threads \
--disable-libssp \
--disable-libquadmath \
--disable-libgomp \
--disable-libatomic \
--with-newlib \
2>&1 | tee "$LOG_DIR/gcc-bootstrap-configure.log"
make -j"$JOBS" all-gcc all-target-libgcc \
2>&1 | tee "$LOG_DIR/gcc-bootstrap-build.log"
make install-gcc install-target-libgcc \
2>&1 | tee "$LOG_DIR/gcc-bootstrap-install.log"
step "Bootstrap GCC installed: ${PREFIX}/bin/${TARGET}-gcc"
else
step "Bootstrap GCC already installed"
fi
# ==================================================================
# STEP 3: Build Newlib
# ==================================================================
msg "Building Newlib ${NEWLIB_VER}"
mkdir -p "$BUILD_DIR/newlib"
cd "$BUILD_DIR/newlib"
if [[ ! -f "${SYSROOT}/lib/libc.a" ]]; then
"$SRC_DIR/newlib-${NEWLIB_VER}/configure" \
--target="$TARGET" \
--prefix="$PREFIX" \
--disable-multilib \
--enable-newlib-nano-malloc \
--enable-newlib-io-long-long \
--enable-newlib-io-c99-formats \
--disable-newlib-supplied-syscalls \
2>&1 | tee "$LOG_DIR/newlib-configure.log"
make -j"$JOBS" 2>&1 | tee "$LOG_DIR/newlib-build.log"
make install 2>&1 | tee "$LOG_DIR/newlib-install.log"
step "Newlib installed to sysroot"
else
step "Newlib already installed"
fi
# ==================================================================
# STEP 3b: Build libgloss/adros (crt0.o + libadros.a)
# ==================================================================
msg "Building libgloss/adros (crt0.o + libadros.a)"
if [[ ! -f "${SYSROOT}/lib/libadros.a" ]]; then
local_gloss="$SRC_DIR/newlib-${NEWLIB_VER}/libgloss/adros"
${TARGET}-gcc -Os -c "$local_gloss/crt0.S" -o /tmp/adros_crt0.o
${TARGET}-gcc -Os -c "$local_gloss/syscalls.c" -o /tmp/adros_syscalls.o
${TARGET}-gcc -Os -c "$local_gloss/posix_stubs.c" -o /tmp/adros_posix_stubs.o
${TARGET}-gcc -Os -c "$local_gloss/posix_compat.c" -o /tmp/adros_posix_compat.o
${TARGET}-ar rcs /tmp/libadros.a /tmp/adros_syscalls.o /tmp/adros_posix_stubs.o /tmp/adros_posix_compat.o
cp /tmp/adros_crt0.o "${SYSROOT}/lib/crt0.o"
cp /tmp/libadros.a "${SYSROOT}/lib/libadros.a"
rm -f /tmp/adros_crt0.o /tmp/adros_syscalls.o /tmp/adros_posix_stubs.o /tmp/adros_posix_compat.o /tmp/libadros.a
step "crt0.o + libadros.a installed to sysroot"
else
step "libadros.a already installed"
fi
# ==================================================================
# STEP 3c: Create GCC specs file (fix LIB_SPEC link order)
# ==================================================================
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"
else
step "Specs file already exists"
fi
# ==================================================================
# STEP 4: Rebuild GCC (full, with Newlib)
# ==================================================================
msg "Rebuilding GCC ${GCC_VER} (full, with Newlib sysroot)"
mkdir -p "$BUILD_DIR/gcc-full"
cd "$BUILD_DIR/gcc-full"
# Only rebuild if the full gcc doesn't link against newlib yet
if [[ ! -f "$BUILD_DIR/gcc-full/.built" ]]; then
"$SRC_DIR/gcc-${GCC_VER}/configure" \
--target="$TARGET" \
--prefix="$PREFIX" \
--with-sysroot="$SYSROOT" \
--enable-languages=c,c++ \
--disable-nls \
--disable-shared \
--with-newlib \
--disable-libssp \
--disable-libquadmath \
2>&1 | tee "$LOG_DIR/gcc-full-configure.log"
make -j"$JOBS" all-gcc all-target-libgcc \
2>&1 | tee "$LOG_DIR/gcc-full-build.log"
make install-gcc install-target-libgcc \
2>&1 | tee "$LOG_DIR/gcc-full-install.log"
touch "$BUILD_DIR/gcc-full/.built"
step "Full GCC installed: ${PREFIX}/bin/${TARGET}-g++"
else
step "Full GCC already installed"
fi
# ==================================================================
# STEP 5: Cross-compile Bash (optional)
# ==================================================================
if [[ $SKIP_BASH -eq 0 ]]; then
msg "Cross-compiling Bash ${BASH_VER}"
mkdir -p "$BUILD_DIR/bash"
cd "$BUILD_DIR/bash"
if [[ ! -f "$BUILD_DIR/bash/bash" ]]; then
# Bash needs a config.cache for cross-compilation
# Comprehensive cross-compilation cache for Bash on AdrOS/newlib.
# Cross-compile configure tests can't run programs, so we must
# pre-seed results for functions/headers that newlib provides.
cat > config.cache <<'CACHE_EOF'
# --- Types ---
ac_cv_type_getgroups=gid_t
ac_cv_type_sigset_t=yes
ac_cv_type_sig_atomic_t=yes
ac_cv_type_clock_t=yes
ac_cv_c_long_double=yes
ac_cv_sizeof_int=4
ac_cv_sizeof_long=4
ac_cv_sizeof_char_p=4
ac_cv_sizeof_double=8
ac_cv_sizeof_long_long=8
ac_cv_sizeof_intmax_t=4
ac_cv_sizeof_wchar_t=4
# --- Headers (newlib provides these) ---
ac_cv_header_unistd_h=yes
ac_cv_header_stdlib_h=yes
ac_cv_header_string_h=yes
ac_cv_header_strings_h=yes
ac_cv_header_memory_h=yes
ac_cv_header_locale_h=yes
ac_cv_header_termios_h=yes
ac_cv_header_termio_h=no
ac_cv_header_sys_wait_h=yes
ac_cv_header_sys_select_h=yes
ac_cv_header_sys_file_h=no
ac_cv_header_sys_resource_h=yes
ac_cv_header_sys_param_h=yes
ac_cv_header_sys_socket_h=no
ac_cv_header_sys_ioctl_h=yes
ac_cv_header_sys_mman_h=no
ac_cv_header_sys_pte_h=no
ac_cv_header_sys_ptem_h=no
ac_cv_header_sys_stream_h=no
ac_cv_header_dirent_h=yes
ac_cv_header_dirent_dirent_h=yes
ac_cv_header_grp_h=yes
ac_cv_header_pwd_h=yes
ac_cv_header_regex_h=yes
ac_cv_header_fnmatch_h=yes
ac_cv_header_dlfcn_h=no
ac_cv_header_netdb_h=no
ac_cv_header_netinet_in_h=no
ac_cv_header_arpa_inet_h=no
ac_cv_header_wctype_h=yes
ac_cv_header_wchar_h=yes
ac_cv_header_langinfo_h=no
ac_cv_header_libintl_h=no
ac_cv_header_stdint_h=yes
ac_cv_header_inttypes_h=yes
ac_cv_header_stdbool_h=yes
ac_cv_header_sys_stat_h=yes
ac_cv_header_sys_types_h=yes
ac_cv_header_fcntl_h=yes
ac_cv_header_signal_h=yes
ac_cv_header_limits_h=yes
# --- Functions in newlib libc.a ---
ac_cv_func_memmove=yes
ac_cv_func_memset=yes
ac_cv_func_strchr=yes
ac_cv_func_strerror=yes
ac_cv_func_strtol=yes
ac_cv_func_strtoul=yes
ac_cv_func_strtod=yes
ac_cv_func_strtoimax=yes
ac_cv_func_strtoumax=yes
ac_cv_func_snprintf=yes
ac_cv_func_vsnprintf=yes
ac_cv_func_setlocale=yes
ac_cv_func_putenv=yes
ac_cv_func_setenv=yes
ac_cv_func_mkstemp=yes
ac_cv_func_rename=yes
ac_cv_func_mbrtowc=yes
ac_cv_func_wcrtomb=yes
ac_cv_func_wctomb=yes
ac_cv_func_mbrlen=yes
ac_cv_func_regcomp=yes
ac_cv_func_regexec=yes
ac_cv_func_fnmatch=yes
ac_cv_func_strsignal=yes
ac_cv_func_raise=yes
ac_cv_func_getopt=no
# --- Functions provided by libadros.a stubs ---
ac_cv_func_dup2=yes
ac_cv_func_fcntl=yes
ac_cv_func_getcwd=yes
ac_cv_func_pipe=yes
ac_cv_func_select=yes
ac_cv_func_pselect=yes
ac_cv_func_chown=yes
ac_cv_func_lstat=yes
ac_cv_func_readlink=no
ac_cv_func_killpg=yes
ac_cv_func_tcgetattr=yes
ac_cv_func_tcsetattr=yes
ac_cv_func_tcgetpgrp=yes
ac_cv_func_tcsetpgrp=yes
ac_cv_func_tcsendbreak=no
ac_cv_func_cfgetospeed=no
ac_cv_func_sigaction=yes
ac_cv_func_sigprocmask=yes
ac_cv_func_siginterrupt=no
ac_cv_func_waitpid=yes
ac_cv_func_gethostname=yes
ac_cv_func_getpwnam=yes
ac_cv_func_getpwuid=yes
ac_cv_func_getpwent=no
ac_cv_func_getgroups=no
ac_cv_func_getrlimit=no
ac_cv_func_setrlimit=no
ac_cv_func_sysconf=no
ac_cv_func_pathconf=no
ac_cv_func_getpagesize=no
ac_cv_func_getdtablesize=no
ac_cv_func_mkfifo=yes
ac_cv_func_opendir=yes
ac_cv_func_readdir=yes
ac_cv_func_closedir=yes
ac_cv_func_mmap_fixed_mapped=no
ac_cv_func_setvbuf_reversed=no
ac_cv_func_strcoll_works=yes
ac_cv_func_working_mktime=yes
ac_cv_func_getenv=yes
ac_cv_func_setpgid=yes
ac_cv_func_setsid=yes
ac_cv_func_getpgrp=yes
ac_cv_func_setpgrp=no
ac_cv_func_getpeername=no
ac_cv_func_gethostbyname=no
ac_cv_func_getaddrinfo=no
ac_cv_func_getservbyname=no
ac_cv_func_getservent=no
ac_cv_func_inet_aton=no
ac_cv_func_dlopen=no
ac_cv_func_dlclose=no
ac_cv_func_dlsym=no
ac_cv_func_confstr=no
ac_cv_func_eaccess=no
ac_cv_func_faccessat=no
ac_cv_func_arc4random=no
ac_cv_func_getrandom=no
ac_cv_func_getentropy=no
ac_cv_func_iconv=no
ac_cv_func_getwd=no
ac_cv_func_doprnt=no
ac_cv_func_mbscasecmp=no
ac_cv_func_mbschr=no
ac_cv_func_mbscmp=no
ac_cv_func_setdtablesize=no
ac_cv_func_getrusage=no
ac_cv_func_locale_charset=no
# --- Bash-specific cross-compile overrides ---
ac_cv_rl_version=8.2
bash_cv_func_sigsetjmp=missing
bash_cv_func_ctype_nonascii=no
bash_cv_func_snprintf=yes
bash_cv_func_vsnprintf=yes
bash_cv_printf_a_format=no
bash_cv_must_reinstall_sighandlers=no
bash_cv_pgrp_pipe=no
bash_cv_sys_named_pipes=missing
bash_cv_job_control_missing=present
bash_cv_sys_siglist=yes
bash_cv_under_sys_siglist=yes
bash_cv_opendir_not_robust=no
bash_cv_ulimit_maxfds=no
bash_cv_getenv_redef=yes
bash_cv_getcwd_malloc=yes
bash_cv_type_rlimit=long
bash_cv_type_intmax_t=int
bash_cv_type_uintmax_t=unsigned
bash_cv_posix_signals=yes
bash_cv_bsd_signals=no
bash_cv_sysv_signals=no
bash_cv_speed_t_in_sys_types=no
bash_cv_struct_winsize_header=other
bash_cv_struct_winsize_in_ioctl=yes
bash_cv_tiocstat_in_ioctl=no
bash_cv_tiocgwinsz_in_ioctl=yes
bash_cv_unusedvar=yes
CACHE_EOF
"$SRC_DIR/bash-${BASH_VER}/configure" \
--host="$TARGET" \
--prefix=/usr \
--without-bash-malloc \
--disable-nls \
--cache-file=config.cache \
CC="${TARGET}-gcc" \
AR="${TARGET}-ar" \
RANLIB="${TARGET}-ranlib" \
CFLAGS="-Os -static -D_POSIX_VERSION=200112L" \
LDFLAGS="-static -Wl,--allow-multiple-definition" \
2>&1 | tee "$LOG_DIR/bash-configure.log"
make -j"$JOBS" 2>&1 | tee "$LOG_DIR/bash-build.log"
step "Bash built: $BUILD_DIR/bash/bash"
# Copy to AdrOS initrd
if [[ -d "$ADROS_ROOT/iso/boot" ]]; then
step "Installing bash to AdrOS filesystem..."
mkdir -p "$ADROS_ROOT/iso/bin"
cp "$BUILD_DIR/bash/bash" "$ADROS_ROOT/iso/bin/bash"
fi
else
step "Bash already built"
fi
fi
# ==================================================================
# Summary
# ==================================================================
msg "Toolchain build complete!"
echo ""
echo " Prefix: $PREFIX"
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 ""
echo " Usage:"
echo " export PATH=${PREFIX}/bin:\$PATH"
echo " ${TARGET}-gcc -o hello hello.c"
echo ""
if [[ $SKIP_BASH -eq 0 ]]; then
echo " Bash: $BUILD_DIR/bash/bash"
fi