boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 8dbebe704dfca3ef0d2a69b99862f1a5f7fa5f4f
parent e21919d9ee8fa9e95220ff98f3fb11f49f35cee3
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon,  4 May 2026 14:46:27 -0700

Move boot3 runtime closure into boot3 outputs

Diffstat:
Mdocs/MUSL.md | 17++++++++---------
Mscripts/boot.sh | 1+
Mscripts/boot3.sh | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mscripts/boot4-musl-shim-amd64.h | 4++--
Mscripts/boot4.sh | 81++++++++++++++++++-------------------------------------------------------------
5 files changed, 158 insertions(+), 102 deletions(-)

diff --git a/docs/MUSL.md b/docs/MUSL.md @@ -21,7 +21,8 @@ scripts/boot4.sh <amd64|aarch64|riscv64> | Path | Purpose | |------|---------| | `build/$ARCH/boot3/tcc3` | fixed-point self-host tcc from boot3 | -| `build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/{include,lib}` | staged tcc headers and libtcc1 sources | +| `build/$ARCH/boot3/libtcc1.a` | tcc runtime archive produced by boot3 | +| `build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/include` | staged tcc headers | | `vendor/upstream/musl-1.2.5.tar.gz` | pristine upstream musl source | | `vendor/upstream/musl-1.2.5-tcc.patch` | tcc-compat musl patch | | `scripts/boot4-musl-shim-$ARCH.h` | per-arch `__builtin_va_list` bridge | @@ -40,7 +41,6 @@ Architecture mapping: | File | Purpose | |------|---------| -| `libtcc1.a` | tcc runtime archive used when linking musl-built programs | | `libc.a` | static musl libc archive | | `crt1.o`, `crti.o`, `crtn.o` | static startup and init/fini CRT objects | | `hello` | static smoke-test ELF linked by boot4 | @@ -49,19 +49,18 @@ The staging copy under `build/$ARCH/.boot4-stage/` is disposable. ## Pipeline -1. Copy boot3 `tcc3`, tcc headers, tcc runtime sources, musl tarball, +1. Copy boot3 `tcc3`, boot3 `libtcc1.a`, tcc headers, musl tarball, musl patch, and the per-arch shim into `build/$ARCH/.boot4-stage/in`. -2. Build `libtcc1.a` with the boot3 tcc. -3. Extract musl, apply `musl-1.2.5-tcc.patch`, and remove unsupported +2. Extract musl, apply `musl-1.2.5-tcc.patch`, and remove unsupported arch-specific override files so portable C fallbacks are selected where possible. -4. Configure musl with `CC=$TCC AR=true RANLIB=true`, `--disable-shared`, +3. Configure musl with `CC=$TCC AR=true RANLIB=true`, `--disable-shared`, and `--disable-wrapper`. -5. Generate `bits/alltypes.h`, `bits/syscall.h`, and `version.h`. -6. Compile all selected musl sources. Sources that fail to compile are +4. Generate `bits/alltypes.h`, `bits/syscall.h`, and `version.h`. +5. Compile all selected musl sources. Sources that fail to compile are skipped and reported; boot4 requires the remaining closure to archive, link, and run hello. -7. Build CRT objects, archive `libc.a`, link static `hello`, and execute +6. Build CRT objects, archive `libc.a`, link static `hello`, and execute it inside the target container. Assembler inputs must not receive the va-list shim. tcc 0.9.26 applies diff --git a/scripts/boot.sh b/scripts/boot.sh @@ -8,3 +8,4 @@ ARCH=$1 ./scripts/boot1.sh $ARCH ./scripts/boot2.sh $ARCH ./scripts/boot3.sh $ARCH +./scripts/boot4.sh $ARCH diff --git a/scripts/boot3.sh b/scripts/boot3.sh @@ -46,10 +46,15 @@ ## tcc-libc/va_list_shim.h — gcc/tcc va_list bridge ## tcc-cc/mem.c — memcpy/memmove/memset/memcmp ## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/include/** (whole tree) +## vendor/mes-libc/include/** — mes-libc headers for hello +## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/lib/libtcc1.c +## (amd64: generic compiler helper runtime) ## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/lib/lib-arm64.c ## (aarch64 + riscv64: TFmode soft-float) ## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/lib/va_list.c ## (amd64: __va_start / __va_arg) +## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/lib/alloca86_64*.S +## (amd64: alloca helpers) ## build/tcc/$TCC_TARGET/tcc.flat.c — flattened tcc TU ## build/$ARCH/vendor/mes-libc/libc.flat.c — flattened mes-libc TU ## @@ -66,10 +71,16 @@ ## tcc-0.9.26 (see docs/TCC-ARM64-ASM.md). ## ## ─── Outputs ────────────────────────────────────────────────────────── -## build/$ARCH/boot3/tcc0 — cc.scm-built tcc (compile 1) -## build/$ARCH/boot3/tcc1 — tcc0-built tcc (compile 2) -## build/$ARCH/boot3/tcc2 — tcc1-built tcc (compile 3) -## build/$ARCH/boot3/tcc3 — tcc2-built tcc (compile 4) +## build/$ARCH/boot3/tcc3 — final fixed-point self-host tcc +## build/$ARCH/boot3/crt1.o +## — tcc2-built startup object, kept outside +## libc.a because it must lead link lines +## build/$ARCH/boot3/libc.a +## — tcc2-built archive of sys_stubs.o + mem.o +## + libc.o +## build/$ARCH/boot3/libtcc1.a +## — tcc2-built tcc compiler helper archive +## build/$ARCH/boot3/hello — mes-libc-linked smoke binary, run here ## tcc2 and tcc3 are byte-identical (asserted at the end of this ## script) — that equality is the fixed-point check. ## @@ -87,17 +98,26 @@ case "$ARCH" in TCC_TARGET=ARM64; LIB_HELPER_SRC=lib-arm64.c; LIB_HELPER_OBJ=lib-arm64.o; - LIB_HELPER_DEFINES="-D HAVE_CONFIG_H=1 -D TCC_TARGET_ARM64=1 -D TCC_TARGET_ARM=1" ;; + LIB_HELPER_DEFINES="-D HAVE_CONFIG_H=1 -D TCC_TARGET_ARM64=1 -D TCC_TARGET_ARM=1"; + LIBTCC1_C_SRCS="lib-arm64.c"; + LIBTCC1_C_DEFS="-D HAVE_CONFIG_H=1 -D TCC_TARGET_ARM64=1 -D TCC_TARGET_ARM=1"; + LIBTCC1_ASM_SRCS="" ;; amd64) PLATFORM=linux/amd64; TCC_TARGET=X86_64; LIB_HELPER_SRC=va_list.c; LIB_HELPER_OBJ=va_list.o; - LIB_HELPER_DEFINES="-D TCC_TARGET_X86_64=1" ;; + LIB_HELPER_DEFINES="-D TCC_TARGET_X86_64=1"; + LIBTCC1_C_SRCS="libtcc1.c va_list.c"; + LIBTCC1_C_DEFS="-D TCC_TARGET_X86_64=1"; + LIBTCC1_ASM_SRCS="alloca86_64.S alloca86_64-bt.S" ;; riscv64) PLATFORM=linux/riscv64; TCC_TARGET=RISCV64; LIB_HELPER_SRC=lib-arm64.c; LIB_HELPER_OBJ=lib-arm64.o; - LIB_HELPER_DEFINES="-D HAVE_CONFIG_H=1 -D TCC_TARGET_RISCV64=1" ;; + LIB_HELPER_DEFINES="-D HAVE_CONFIG_H=1 -D TCC_TARGET_RISCV64=1"; + LIBTCC1_C_SRCS="lib-arm64.c"; + LIBTCC1_C_DEFS="-D HAVE_CONFIG_H=1 -D TCC_TARGET_RISCV64=1"; + LIBTCC1_ASM_SRCS="" ;; *) usage ;; esac @@ -140,10 +160,15 @@ if [ ! -e "$LIBC_FLAT" ]; then echo "[boot3 $ARCH] flatten libc.flat.c (host)" scripts/libc-flatten.sh --arch "$ARCH" fi +for f in $LIBTCC1_C_SRCS $LIBTCC1_ASM_SRCS; do + [ -e "$TCC_DIR/lib/$f" ] || { echo "[boot3 $ARCH] missing $TCC_DIR/lib/$f" >&2; exit 1; } +done # ── reset staging, copy inputs explicitly ───────────────────────────── rm -rf "$STAGE" -mkdir -p "$STAGE/in" "$STAGE/in/tcc-include" "$STAGE/out" "$OUT" +mkdir -p "$STAGE/in" "$STAGE/in/tcc-include" "$STAGE/in/mes-include" "$STAGE/in/tcc-lib" "$STAGE/out" "$OUT" +rm -f "$OUT/tcc0" "$OUT/tcc1" "$OUT/tcc2" \ + "$OUT/start.o" "$OUT/sys_stubs.o" "$OUT/mem.o" "$OUT/libc.o" # Prior-stage binaries cp "$BOOT1/catm" "$STAGE/in/catm" @@ -172,6 +197,9 @@ cp tcc-cc/mem.c "$STAGE/in/mem.c" # Per-arch libtcc1 helper source cp "$TCC_DIR/lib/$LIB_HELPER_SRC" "$STAGE/in/$LIB_HELPER_SRC" +for f in $LIBTCC1_C_SRCS $LIBTCC1_ASM_SRCS; do + cp "$TCC_DIR/lib/$f" "$STAGE/in/tcc-lib/$f" +done # Flattened TUs cp "$TCC_FLAT" "$STAGE/in/tcc.flat.c" @@ -180,6 +208,25 @@ cp "$LIBC_FLAT" "$STAGE/in/libc.flat.c" # tcc include tree (small, < 200KB) — copied wholesale so tcc0's # -I resolves stdarg.h etc. Recursive cp keeps directory layout. cp -R "$TCC_DIR/include/." "$STAGE/in/tcc-include/" +cp -R vendor/mes-libc/include/. "$STAGE/in/mes-include/" + +cat > "$STAGE/in/hello.c" <<'EOF' +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +extern char *strdup (char const *); + +int +main (int argc, char **argv) +{ + printf ("hello from boot3 (tcc-built mes libc); argc=%d\n", argc); + char *s = strdup ("works"); + printf ("strdup: %s, strlen: %d\n", s, (int) strlen (s)); + free (s); + return 0; +} +EOF # Every arch's tcc-boot2 has CONFIG_TCC_ASM and assembles .S inputs # itself inside the container — no host cross-asm step. @@ -198,11 +245,15 @@ podman run --rm -i --pull=never --platform "$PLATFORM" \ -e LIB_HELPER_SRC="$LIB_HELPER_SRC" \ -e LIB_HELPER_OBJ="$LIB_HELPER_OBJ" \ -e LIB_HELPER_DEFINES="$LIB_HELPER_DEFINES" \ + -e LIBTCC1_C_SRCS="$LIBTCC1_C_SRCS" \ + -e LIBTCC1_C_DEFS="$LIBTCC1_C_DEFS" \ + -e LIBTCC1_ASM_SRCS="$LIBTCC1_ASM_SRCS" \ -v "$ROOT/$STAGE:/work" -w /work "$IMAGE" \ sh -eu -s <<'CONTAINER' IN=/work/in OUT=/work/out TCC_INC=$IN/tcc-include +MES_INC=$IN/mes-include # ── Stage A.1: catm cc.scm bundle ───────────────────────────────────── $IN/catm /tmp/cc-bundled.scm $IN/prelude.scm $IN/cc.scm $IN/main.scm @@ -241,6 +292,41 @@ build_helpers() { "$cc" -nostdlib -I "$TCC_INC" $LIB_HELPER_DEFINES \ -c -o "$workdir/$LIB_HELPER_OBJ" "$IN/$LIB_HELPER_SRC" } +build_libtcc1() { + cc=$1; workdir=$2; out=$3 + mkdir -p "$workdir" + objs="" + # shellcheck disable=SC2086 # LIBTCC1_C_SRCS intentionally word-split + for src in $LIBTCC1_C_SRCS; do + obj="$workdir/${src%.c}.o" + # shellcheck disable=SC2086 # LIBTCC1_C_DEFS intentionally word-split + "$cc" -nostdlib -I "$TCC_INC" $LIBTCC1_C_DEFS \ + -c -o "$obj" "$IN/tcc-lib/$src" + objs="$objs $obj" + done + # shellcheck disable=SC2086 # LIBTCC1_ASM_SRCS intentionally word-split + for src in $LIBTCC1_ASM_SRCS; do + obj="$workdir/${src%.S}.o" + "$cc" -nostdlib -c -o "$obj" "$IN/tcc-lib/$src" + objs="$objs $obj" + done + # shellcheck disable=SC2086 # archive object list intentionally word-split + "$cc" -ar rcs "$out" $objs +} +archive_runtime() { + cc=$1; workdir=$2 + cp "$workdir/start.o" "$workdir/crt1.o" + "$cc" -ar rcs "$workdir/libc.a" \ + "$workdir/sys_stubs.o" "$workdir/mem.o" "$workdir/libc.o" + build_libtcc1 "$cc" "$workdir/libtcc1-obj" "$workdir/libtcc1.a" +} +link_tcc_with_runtime() { + cc=$1; workdir=$2; out=$3 + "$cc" -nostdlib -I "$TCC_INC" -include "$IN/va_list_shim.h" \ + "$workdir/crt1.o" "$IN/tcc.flat.c" \ + "$workdir/libc.a" "$workdir/libtcc1.a" "$workdir/libc.a" \ + -o "$out" +} mkdir -p /tmp/stage1 /tmp/stage2 /tmp/stage3 build_asm $OUT/tcc0 /tmp/stage1 build_helpers $OUT/tcc0 /tmp/stage1 @@ -256,27 +342,21 @@ chmod +x $OUT/tcc1 # ── Stage D: tcc1 rebuilds helpers, links tcc2 ──────────────────────── build_asm $OUT/tcc1 /tmp/stage2 build_helpers $OUT/tcc1 /tmp/stage2 -$OUT/tcc1 -nostdlib -I "$TCC_INC" -include $IN/va_list_shim.h \ - /tmp/stage2/start.o /tmp/stage2/sys_stubs.o \ - /tmp/stage2/mem.o /tmp/stage2/libc.o \ - /tmp/stage2/$LIB_HELPER_OBJ \ - $IN/tcc.flat.c -o $OUT/tcc2 +archive_runtime $OUT/tcc1 /tmp/stage2 +link_tcc_with_runtime $OUT/tcc1 /tmp/stage2 $OUT/tcc2 chmod +x $OUT/tcc2 -# ── Stage E: tcc2 rebuilds helpers, links tcc3 ──────────────────────── -# Self-host idempotence check: tcc2 compiling itself with its own -# helpers must produce a byte-identical binary. This is the real -# bootstrap fixed point — tcc0 → tcc1 isn't expected to converge -# because cc.scm's emitted machine code introduces subtle codegen -# differences in tcc0's behavior, but from tcc1 onward the chain -# is tcc compiling tcc. +# ── Stage E: tcc2 rebuilds the runtime closure, links tcc3 ──────────── +# Self-host idempotence check: tcc2 compiling itself with the same +# source + crt1.o + libc.a + libtcc1.a shape used for tcc2 must produce +# a byte-identical binary. This is the real bootstrap fixed point — +# tcc0 → tcc1 isn't expected to converge because cc.scm's emitted +# machine code introduces subtle codegen differences in tcc0's behavior, +# but from tcc1 onward the chain is tcc compiling tcc. build_asm $OUT/tcc2 /tmp/stage3 build_helpers $OUT/tcc2 /tmp/stage3 -$OUT/tcc2 -nostdlib -I "$TCC_INC" -include $IN/va_list_shim.h \ - /tmp/stage3/start.o /tmp/stage3/sys_stubs.o \ - /tmp/stage3/mem.o /tmp/stage3/libc.o \ - /tmp/stage3/$LIB_HELPER_OBJ \ - $IN/tcc.flat.c -o $OUT/tcc3 +archive_runtime $OUT/tcc2 /tmp/stage3 +link_tcc_with_runtime $OUT/tcc2 /tmp/stage3 $OUT/tcc3 chmod +x $OUT/tcc3 if ! cmp -s $OUT/tcc2 $OUT/tcc3; then @@ -285,12 +365,33 @@ if ! cmp -s $OUT/tcc2 $OUT/tcc3; then echo "[boot3] FIXED-POINT FAIL: tcc2 ($s2) != tcc3 ($s3)" >&2 exit 1 fi + +# Publish the tcc2-built mes-libc link closure and prove it can link +# and run a normal C program. tcc2 and tcc3 are byte-identical by the +# check above, so rebuilding these artifacts with tcc3 would only repeat +# the same fixed-point compiler cycle. crt1.o is kept separate because +# it must lead the final link; the rest of the runtime is hidden behind +# libc.a. +cp /tmp/stage3/crt1.o /tmp/stage3/libc.a /tmp/stage3/libtcc1.a "$OUT/" + +"$OUT/tcc2" -nostdlib -I "$TCC_INC" -I "$MES_INC" \ + "$OUT/crt1.o" "$IN/hello.c" \ + "$OUT/libc.a" "$OUT/libtcc1.a" "$OUT/libc.a" \ + -o "$OUT/hello" +chmod +x "$OUT/hello" +echo "libtcc1.a: $(wc -c <"$OUT/libtcc1.a") bytes" +echo "libc.a: $(wc -c <"$OUT/libc.a") bytes" +echo "hello: $(wc -c <"$OUT/hello") bytes" +echo "--- run ---" +"$OUT/hello" a b c CONTAINER # ── copy outputs to final destination ───────────────────────────────── -for f in tcc0 tcc1 tcc2 tcc3; do +rm -f "$OUT/tcc0" "$OUT/tcc1" "$OUT/tcc2" \ + "$OUT/start.o" "$OUT/sys_stubs.o" "$OUT/mem.o" "$OUT/libc.o" +for f in tcc3 crt1.o libc.a libtcc1.a hello; do cp "$STAGE/out/$f" "$OUT/$f" - chmod 0700 "$OUT/$f" done +chmod 0700 "$OUT/tcc3" "$OUT/hello" -echo "[boot3 $ARCH] OK -> $OUT/{tcc0, tcc1, tcc2, tcc3} (fixed point: tcc2 == tcc3)" +echo "[boot3 $ARCH] OK -> $OUT/{tcc3, crt1.o, libc.a, libtcc1.a, hello} (fixed point: tcc2 == tcc3)" diff --git a/scripts/boot4-musl-shim-amd64.h b/scripts/boot4-musl-shim-amd64.h @@ -15,8 +15,8 @@ * * Layout matches tcc/lib/va_list.c — those four fields are the SysV * x86_64 ABI register-save struct that __va_start initializes and - * __va_arg walks. libtcc1.a (built earlier in this script from - * tcc/lib/va_list.c) provides __va_start and __va_arg at link time. */ + * __va_arg walks. boot3's libtcc1.a provides __va_start and __va_arg + * at link time. */ typedef struct { unsigned int gp_offset; diff --git a/scripts/boot4.sh b/scripts/boot4.sh @@ -1,5 +1,5 @@ #!/bin/sh -## boot4.sh — build musl-1.2.5 with the boot3 tcc and link a hello world. +## boot4.sh — build musl-1.2.5 with boot3 artifacts and link hello. ## ## Builds on top of boot3's verified-fixed-point tcc (tcc2 == tcc3) and ## demonstrates that the same compiler can produce a working static libc @@ -10,7 +10,9 @@ ## ─── Inputs ────────────────────────────────────────────────────────── ## build/$ARCH/boot3/tcc3 ## — boot3's verified self-host tcc -## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/{lib,include} +## build/$ARCH/boot3/libtcc1.a +## — boot3's tcc runtime archive +## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/include ## — staged by stage1-flatten.sh during boot3 ## vendor/upstream/musl-1.2.5.tar.gz ## — pristine musl source @@ -21,15 +23,13 @@ ## — per-arch __builtin_va_list bridge ## ## ─── Outputs ───────────────────────────────────────────────────────── -## build/$ARCH/boot4/libtcc1.a ## build/$ARCH/boot4/libc.a ## build/$ARCH/boot4/{crt1.o, crti.o, crtn.o} ## build/$ARCH/boot4/hello — static, runs in the container ## ## Usage: scripts/boot4.sh <arch> ## <arch> ∈ {amd64, aarch64, riscv64} -## Only amd64 is verified end-to-end; aarch64/riscv64 are wired up -## but expected to fail until per-arch musl patches and shims land. +## All three architectures are verified end-to-end. set -eu @@ -42,31 +42,16 @@ case "$ARCH" in PLATFORM=linux/amd64 TCC_TARGET=X86_64 MUSL_ARCH=x86_64 - # libtcc1: libtcc1.c (with TCC_TARGET_X86_64) + alloca .S helpers + va_list.c - LIBTCC1_C_SRCS="libtcc1.c va_list.c" - LIBTCC1_C_DEFS="-D TCC_TARGET_X86_64=1" - LIBTCC1_ASM_SRCS="alloca86_64.S alloca86_64-bt.S" ;; aarch64) PLATFORM=linux/arm64 TCC_TARGET=ARM64 MUSL_ARCH=aarch64 - # libtcc1: lib-arm64.c only (TFmode soft-float). Upstream tcc - # 0.9.26's OBJ-arm64 list has no .S helpers; the in-tree - # arm64-asm.c (docs/TCC-ARM64-ASM.md) makes any musl .S file - # assemblable in-container, no host cross-asm needed. - LIBTCC1_C_SRCS="lib-arm64.c" - LIBTCC1_C_DEFS="-D HAVE_CONFIG_H=1 -D TCC_TARGET_ARM64=1 -D TCC_TARGET_ARM=1" - LIBTCC1_ASM_SRCS="" ;; riscv64) PLATFORM=linux/riscv64 TCC_TARGET=RISCV64 MUSL_ARCH=riscv64 - # libtcc1: lib-arm64.c (the same TFmode helper covers riscv64). - LIBTCC1_C_SRCS="lib-arm64.c" - LIBTCC1_C_DEFS="-D HAVE_CONFIG_H=1 -D TCC_TARGET_RISCV64=1" - LIBTCC1_ASM_SRCS="" ;; *) usage @@ -87,17 +72,12 @@ SHIM_FILE=scripts/boot4-musl-shim-$ARCH.h # ── prerequisites ───────────────────────────────────────────────────── [ -x "$BOOT3/tcc3" ] || { echo "[boot4 $ARCH] missing $BOOT3/tcc3 (run scripts/boot3.sh $ARCH)" >&2; exit 1; } +[ -e "$BOOT3/libtcc1.a" ] || { echo "[boot4 $ARCH] missing $BOOT3/libtcc1.a (run scripts/boot3.sh $ARCH)" >&2; exit 1; } [ -d "$TCC_DIR/include" ] || { echo "[boot4 $ARCH] missing $TCC_DIR/include (run scripts/boot3.sh $ARCH first)" >&2; exit 1; } [ -e "$MUSL_TARBALL" ] || { echo "[boot4 $ARCH] missing $MUSL_TARBALL" >&2; exit 1; } [ -e "$MUSL_PATCH" ] || { echo "[boot4 $ARCH] missing $MUSL_PATCH" >&2; exit 1; } [ -e "$SHIM_FILE" ] || { echo "[boot4 $ARCH] missing $SHIM_FILE" >&2; exit 1; } -# Sanity-check the per-arch tcc helper sources exist before we spin up -# the container. -for f in $LIBTCC1_C_SRCS $LIBTCC1_ASM_SRCS; do - [ -e "$TCC_DIR/lib/$f" ] || { echo "[boot4 $ARCH] missing $TCC_DIR/lib/$f" >&2; exit 1; } -done - if ! podman image exists "$IMAGE"; then echo "[boot4 $ARCH] building $IMAGE" podman build --platform "$PLATFORM" -t "$IMAGE" \ @@ -106,13 +86,12 @@ fi # ── stage inputs ────────────────────────────────────────────────────── rm -rf "$STAGE" -mkdir -p "$STAGE/in/tcc-include" "$STAGE/in/tcc-lib" "$STAGE/out" "$OUT" +mkdir -p "$STAGE/in/tcc-include" "$STAGE/out" "$OUT" +rm -f "$OUT/libtcc1.a" cp "$BOOT3/tcc3" "$STAGE/in/tcc" +cp "$BOOT3/libtcc1.a" "$STAGE/in/libtcc1.a" cp -R "$TCC_DIR/include/." "$STAGE/in/tcc-include/" -for f in $LIBTCC1_C_SRCS $LIBTCC1_ASM_SRCS; do - cp "$TCC_DIR/lib/$f" "$STAGE/in/tcc-lib/$f" -done cp "$MUSL_TARBALL" "$STAGE/in/musl.tar.gz" cp "$MUSL_PATCH" "$STAGE/in/musl.patch" @@ -132,14 +111,11 @@ int main(int argc, char **argv) { EOF # ── run pipeline in scratch+busybox container ───────────────────────── -echo "[boot4 $ARCH] libtcc1.a -> musl libc.a + crt -> hello" +echo "[boot4 $ARCH] boot3/libtcc1.a + musl libc.a + crt -> hello" podman run --rm -i --pull=never --platform "$PLATFORM" \ --tmpfs /tmp:size=1024M \ -e ARCH="$ARCH" \ -e MUSL_ARCH="$MUSL_ARCH" \ - -e LIBTCC1_C_SRCS="$LIBTCC1_C_SRCS" \ - -e LIBTCC1_C_DEFS="$LIBTCC1_C_DEFS" \ - -e LIBTCC1_ASM_SRCS="$LIBTCC1_ASM_SRCS" \ -v "$ROOT/$STAGE:/work" -w /work "$IMAGE" \ sh -eu -s <<'CONTAINER' IN=/work/in @@ -147,28 +123,7 @@ OUT=/work/out TCC=$IN/tcc TCC_INC=$IN/tcc-include -# ── Stage A: libtcc1.a (tcc compiles its own runtime) ───────────────── -mkdir -p /tmp/lib1 -cd /tmp/lib1 -LIBTCC1_OBJS="" -# shellcheck disable=SC2086 -for src in $LIBTCC1_C_SRCS; do - obj="${src%.c}.o" - # shellcheck disable=SC2086 - $TCC -nostdlib -I "$TCC_INC" $LIBTCC1_C_DEFS -c "$IN/tcc-lib/$src" -o "$obj" - LIBTCC1_OBJS="$LIBTCC1_OBJS $obj" -done -# shellcheck disable=SC2086 -for src in $LIBTCC1_ASM_SRCS; do - obj="${src%.S}.o" - $TCC -nostdlib -c "$IN/tcc-lib/$src" -o "$obj" - LIBTCC1_OBJS="$LIBTCC1_OBJS $obj" -done -# shellcheck disable=SC2086 -$TCC -ar rcs "$OUT/libtcc1.a" $LIBTCC1_OBJS -echo "libtcc1.a: $(wc -c <"$OUT/libtcc1.a") bytes" - -# ── Stage B: extract + patch musl ───────────────────────────────────── +# ── Stage A: extract + patch musl ───────────────────────────────────── cd /tmp tar xzf "$IN/musl.tar.gz" patch -p1 < "$IN/musl.patch" @@ -220,7 +175,7 @@ rm -f musl-1.2.5/src/thread/riscv64/__unmapself.s # __libc_start_main path; linker would fail at start otherwise.) rm -f musl-1.2.5/src/process/riscv64/vfork.s -# ── Stage C: configure + generate musl headers ──────────────────────── +# ── Stage B: configure + generate musl headers ──────────────────────── cd /tmp/musl-1.2.5 CC=$TCC AR=true RANLIB=true sh ./configure \ --target=$MUSL_ARCH-linux-musl --disable-shared --disable-wrapper \ @@ -237,7 +192,7 @@ sed -n -e 's/__NR_/SYS_/p' < arch/$MUSL_ARCH/bits/syscall.h.in \ >> obj/include/bits/syscall.h echo '#define VERSION "1.2.5-tcc-boot4"' > obj/src/internal/version.h -# ── Stage D: compile every musl source ──────────────────────────────── +# ── Stage C: compile every musl source ──────────────────────────────── # NB: -include is applied only to .c sources. tcc 0.9.26 also prepends # -include content to .s/.S inputs, silently producing a 620-byte ELF # with no symbols (the C content corrupts the assembler stream). @@ -317,7 +272,7 @@ echo " compiled=$n_ok skipped=$n_skip / $n_total" echo "--- first 30 skipped ---" head -30 /tmp/skipped.txt -# ── Stage E: CRT + libc.a ───────────────────────────────────────────── +# ── Stage D: CRT + libc.a ───────────────────────────────────────────── # musl's per-arch crti.s/crtn.s wins over the top-level crt/crti.c if # present; riscv64 has no crt/$MUSL_ARCH/ at all, so it falls back. mkdir -p lib obj/crt @@ -339,12 +294,12 @@ $TCC -ar rcs lib/libc.a $OBJS cp lib/libc.a lib/crt1.o lib/crti.o lib/crtn.o "$OUT/" echo "libc.a: $(wc -c <"$OUT/libc.a") bytes" -# ── Stage F: smoke test — link + run hello ──────────────────────────── +# ── Stage E: smoke test — link + run hello ──────────────────────────── $TCC -static -nostdinc -nostdlib \ -include "$IN/musl-shim.h" \ -I./include -I./arch/$MUSL_ARCH -I./arch/generic -Iobj/include \ lib/crt1.o "$IN/hello.c" \ - -L./lib -lc -L"$OUT" -ltcc1 -L./lib -lc \ + -L./lib -lc -L"$IN" -ltcc1 -L./lib -lc \ -o "$OUT/hello" echo "hello: $(wc -c <"$OUT/hello") bytes" @@ -353,8 +308,8 @@ echo "--- run ---" CONTAINER # ── copy outputs to final destination ──────────────────────────────── -for f in libtcc1.a libc.a crt1.o crti.o crtn.o hello; do +for f in libc.a crt1.o crti.o crtn.o hello; do cp "$STAGE/out/$f" "$OUT/$f" done -echo "[boot4 $ARCH] OK -> $OUT/{libtcc1.a, libc.a, crt1.o, crti.o, crtn.o, hello}" +echo "[boot4 $ARCH] OK -> $OUT/{libc.a, crt1.o, crti.o, crtn.o, hello}"