boot2

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

commit 7c103f48b35e13efa8aafa4fb2c49a5a50a257dd
parent 7a40928766648342fc82fb107a508408ff2908f6
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue,  5 May 2026 13:03:07 -0700

move build/tcc/<TCC_TARGET>/ -> build/<arch>/vendor/tcc/

Per-arch tcc artifacts (flattened TU, unpacked source tree, stdarg
bridge) now live under build/<arch>/vendor/tcc/ alongside mes-libc, so
boot.sh <arch> writes everything beneath build/<arch>/. The bridge
file becomes a per-arch copy (content is byte-identical across arches);
build/tcc/ is gone entirely.

Makefile switches $(TCC_FLAT) to a static pattern rule over $(TCC_FLATS)
and threads the per-arch path through the tcc-boot2 P1pp rule.

Diffstat:
MMakefile | 38+++++++++++++++++++-------------------
Mdocs/LIBC.md | 2+-
Mdocs/MUSL.md | 4++--
Mdocs/TCC.md | 14+++++++-------
Mscripts/boot-build-tcc-tcc.sh | 13+++++++------
Mscripts/boot-run-tests.sh | 4++--
Mscripts/boot3.sh | 21+++++++++++----------
Mscripts/boot4.sh | 34++++++++++++++++------------------
Mscripts/boot5-calibrate.sh | 2+-
Mscripts/boot5.sh | 11+++++++----
Mscripts/libc-flatten.sh | 2+-
Mscripts/run-gcc-libc-flat-tcc.sh | 4++--
Mscripts/stage1-flatten.sh | 30+++++++++++++++---------------
Mscripts/stage2-alpine.sh | 12+++++++-----
Mscripts/stage3-rebuild.sh | 16+++++++++-------
15 files changed, 107 insertions(+), 100 deletions(-)

diff --git a/Makefile b/Makefile @@ -274,20 +274,20 @@ $(CC_BINS): build/%/cc/cc.scm: $(CC_SRCS) build/%/.image build/%/tools/catm # --- tcc-boot2 end-to-end harness ----------------------------------------- # # Drives stage1-flatten.sh (host preprocessor only — no container) to -# produce build/tcc/$(TCC_TARGET)/tcc.flat.c, then runs cc.scm inside +# produce build/$(ARCH)/vendor/tcc/tcc.flat.c, then runs cc.scm inside # the per-arch container against the flattened TU, then assembles the # resulting P1pp into a runnable ELF using the standard P1pp pipeline. -# The resulting binary embeds tcc's $(TCC_TARGET) codegen, so match -# $(ARCH) to it (aarch64↔ARM64, amd64↔X86_64, riscv64↔RISCV64) if you -# want generated programs to run natively in the container. tcc.flat.c -# lives outside the per-arch tree because it depends on TCC_TARGET, not -# the build arch. +# The resulting binary embeds tcc's $(TCC_TARGET) codegen; ARCH and +# TCC_TARGET track 1:1 (aarch64↔ARM64, amd64↔X86_64, riscv64↔RISCV64) +# so generated programs run natively in the per-arch container. TCC_TARGET_aarch64 := ARM64 TCC_TARGET_amd64 := X86_64 TCC_TARGET_riscv64 := RISCV64 TCC_TARGET ?= $(TCC_TARGET_$(ARCH)) -TCC_FLAT := build/tcc/$(TCC_TARGET)/tcc.flat.c +TCC_VENDOR := build/$(ARCH)/vendor/tcc +TCC_FLAT := $(TCC_VENDOR)/tcc.flat.c +TCC_FLATS := $(foreach a,$(ALL_ARCHES),build/$(a)/vendor/tcc/tcc.flat.c) TCC_BOOT2_BINS := $(foreach a,$(ALL_ARCHES),build/$(a)/tcc-boot2/tcc-boot2) TCC_BOOT2_P1PPS := $(foreach a,$(ALL_ARCHES),build/$(a)/tcc-boot2/tcc.flat.P1pp) @@ -302,13 +302,13 @@ LIBC_P1PPS := $(foreach a,$(ALL_ARCHES),build/$(a)/vendor/mes-libc/libc.P1pp) tcc-flat: $(TCC_FLAT) tcc-boot2: $(OUT_DIR)/tcc-boot2/tcc-boot2 -$(TCC_FLAT): scripts/stage1-flatten.sh - sh scripts/stage1-flatten.sh --arch $(TCC_TARGET) +$(TCC_FLATS): build/%/vendor/tcc/tcc.flat.c: scripts/stage1-flatten.sh + sh scripts/stage1-flatten.sh --arch $* # stage1-flatten.sh writes this as a side effect — the post-patch tcc -# <stdarg.h>, used as a cross-arch bridge prepended into both .flat.c +# <stdarg.h>, used as a per-arch bridge prepended into both .flat.c # files (see comments in stage1-flatten.sh / libc-flatten.sh). -build/tcc/stdarg-bridge.h: $(TCC_FLAT) +build/%/vendor/tcc/stdarg-bridge.h: build/%/vendor/tcc/tcc.flat.c # Catalog of inputs the host preprocessor reads when flattening libc. LIBC_VENDOR_SRCS := $(shell find vendor/mes-libc -type f \( -name '*.c' -o -name '*.h' \) 2>/dev/null) \ @@ -316,7 +316,7 @@ LIBC_VENDOR_SRCS := $(shell find vendor/mes-libc -type f \( -name '*.c' -o -name $(wildcard vendor/mes-libc/patches/*.after) $(LIBC_FLATS): build/%/vendor/mes-libc/libc.flat.c: \ - scripts/libc-flatten.sh build/tcc/stdarg-bridge.h $(LIBC_VENDOR_SRCS) + scripts/libc-flatten.sh build/%/vendor/tcc/stdarg-bridge.h $(LIBC_VENDOR_SRCS) sh scripts/libc-flatten.sh --arch $* # libc and tcc.flat are both compiled with --lib= so they omit the @@ -340,11 +340,11 @@ $(LIBC_P1PPS): build/%/vendor/mes-libc/libc.P1pp: \ sh scripts/boot-build-cc.sh $< $@ $(TCC_BOOT2_P1PPS): build/%/tcc-boot2/tcc.flat.P1pp: \ - $(TCC_FLAT) build/%/scheme1/scheme1 build/%/cc/cc.scm \ + build/%/vendor/tcc/tcc.flat.c build/%/scheme1/scheme1 build/%/cc/cc.scm \ scripts/boot-build-cc.sh build/%/.image $(call PODMAN,$*) env CC_LIB=tcc__ \ CC_DEBUG=$(CC_DEBUG) CC_TRACE_EMIT=$(CC_TRACE_EMIT) \ - sh scripts/boot-build-cc.sh $(TCC_FLAT) $@ + sh scripts/boot-build-cc.sh build/$*/vendor/tcc/tcc.flat.c $@ # tcc-boot2 link: pure catm chain — entry stub, libc, client TU, # elf terminator. boot-build-p1pp.sh concatenates them in order @@ -426,8 +426,8 @@ endif TCC_CC_START := build/$(ARCH)/tcc-cc/start.o TCC_CC_MEM := build/$(ARCH)/tcc-cc/mem.o -TCC_CC_TCC_INCLUDE = build/tcc/$(TCC_TARGET)/tcc-0.9.26-1147-gee75a10c/include -TCC_CC_TCC_LIBDIR = build/tcc/$(TCC_TARGET)/tcc-0.9.26-1147-gee75a10c/lib +TCC_CC_TCC_INCLUDE = $(TCC_VENDOR)/tcc-0.9.26-1147-gee75a10c/include +TCC_CC_TCC_LIBDIR = $(TCC_VENDOR)/tcc-0.9.26-1147-gee75a10c/lib # x86_64-only: tcc emits calls to __va_start / __va_arg for variadic # functions. Upstream tcc supplies these via lib/va_list.c → va_list.o @@ -494,13 +494,13 @@ $(TCC_CC_MEM): tcc-cc/mem.c \ # rule is gated on amd64 via TCC_CC_VA_LIST being non-empty above. build/amd64/tcc-cc/va_list.o: \ build/amd64/tcc-boot2/tcc-boot2 \ - build/amd64/.image $(TCC_FLAT) + build/amd64/.image build/amd64/vendor/tcc/tcc.flat.c mkdir -p $(@D) $(call PODMAN,amd64) \ build/amd64/tcc-boot2/tcc-boot2 \ - -nostdlib -I build/tcc/X86_64/tcc-0.9.26-1147-gee75a10c/include \ + -nostdlib -I build/amd64/vendor/tcc/tcc-0.9.26-1147-gee75a10c/include \ -D TCC_TARGET_X86_64=1 \ - -c -o $@ build/tcc/X86_64/tcc-0.9.26-1147-gee75a10c/lib/va_list.c + -c -o $@ build/amd64/vendor/tcc/tcc-0.9.26-1147-gee75a10c/lib/va_list.c # --- tcc-libc test harness inputs ---------------------------------------- # diff --git a/docs/LIBC.md b/docs/LIBC.md @@ -354,7 +354,7 @@ Add `scripts/boot-build-libtcc1.sh`: ```sh TCC_BOOT2=build/$ARCH/tcc-boot2/tcc-boot2 -TCC_SRC=build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c +TCC_SRC=build/$ARCH/vendor/tcc/tcc-0.9.26-1147-gee75a10c $TCC_BOOT2 -c -D HAVE_CONFIG_H=1 -D HAVE_LONG_LONG=1 -D HAVE_FLOAT=1 \ -I vendor/mes-libc/include \ -I vendor/mes-libc/include/linux/$MES_ARCH \ diff --git a/docs/MUSL.md b/docs/MUSL.md @@ -36,7 +36,7 @@ scripts/boot5.sh <amd64|aarch64|riscv64> | `vendor/upstream/musl-1.2.5-deletes.txt` | upstream files removed by the same patch set, one path per line | | `vendor/upstream/musl-1.2.5-generated/$MUSL_ARCH/{alltypes,syscall}.h` | per-arch headers pre-generated at vendor time (replaces musl's mkalltypes.sed + `__NR_`→`SYS_` rewrite, so the container needs no awk) | | `vendor/upstream/musl-1.2.5-skip-$ARCH.txt` | per-arch calibration list — sources tcc 0.9.26 cannot compile, produced by `scripts/boot5-calibrate.sh` | -| `build/tcc/stdarg-bridge.h` | shared `__builtin_va_list` bridge (one file, three arches gated by `#ifdef`; produced by `scripts/stage1-flatten.sh`) | +| `build/$ARCH/vendor/tcc/stdarg-bridge.h` | per-arch `__builtin_va_list` bridge (byte-identical across arches, three arches gated by `#ifdef`; produced by `scripts/stage1-flatten.sh`) | | `scripts/boot-hello.c` | smoke-test source (shared with boot4) | Architecture mapping: @@ -125,7 +125,7 @@ surfaces tcc 0.9.26 cannot compile: | C99 array parameters | remove `[static N]` qualifiers tcc does not parse | | `_Complex` | stub `complex.h` and remove complex sources | | arch asm overrides | delete unsupported fenv, signal, setjmp, thread, string, math overrides as needed | -| varargs | pre-include `build/tcc/stdarg-bridge.h` (the post-patch tcc `<stdarg.h>`) for C translation units | +| varargs | pre-include `build/$ARCH/vendor/tcc/stdarg-bridge.h` (the post-patch tcc `<stdarg.h>`) for C translation units | Required tcc fixes live under `scripts/simple-patches/tcc-0.9.26/`. The musl build depends on the aarch64 literal-address load/store fixes diff --git a/docs/TCC.md b/docs/TCC.md @@ -45,7 +45,7 @@ tcc-0.9.26-1147-gee75a10c.tar.gz live-bootstrap source │ • apply 2 simple-patches │ • host cc -E -nostdinc with mes headers + tcc-mes defines ▼ -build/tcc/X86_64/tcc.flat.c 608 KB single-file C +build/amd64/vendor/tcc/tcc.flat.c 608 KB single-file C │ │ stage2-alpine.sh (alpine:latest) │ • apk add gcc musl-dev @@ -55,18 +55,18 @@ build/tcc/X86_64/tcc.flat.c 608 KB single-file C │ • tcc-host -static compiles+links real tcc.c -> tcc-boot0-mes │ (mirrors live-bootstrap's tcc-boot0 invocation) ▼ -build/tcc/X86_64/tcc-boot0-mes ~750 KB tcc-0.9.26 ELF +build/amd64/vendor/tcc/tcc-boot0-mes ~750 KB tcc-0.9.26 ELF │ │ stage3-rebuild.sh (busybox:musl) │ • tcc-boot0-mes rebuilds libc, then compiles real tcc.c -> tcc-boot1 │ • tcc-boot1 rebuilds libc, then compiles real tcc.c -> tcc-boot2 ▼ -build/tcc/X86_64/tcc-boot2 final tcc-0.9.26 +build/amd64/vendor/tcc/tcc-boot2 final tcc-0.9.26 ``` Two containers, three scripts, one host-side step. Stage 1's `tcc.flat.c` is a portable artifact; stage 2's `tcc-boot0-mes` plus -mes libc bits cross into stage 3 via `build/tcc/<arch>/stage3-input/`. +mes libc bits cross into stage 3 via `build/amd64/vendor/tcc/stage3-input/`. ## Stage 1 — flatten tcc.c into tcc.flat.c @@ -85,7 +85,7 @@ regardless of where stage 1 ran. ### Sub-steps -1. **Unpack** `tcc-0.9.26.tar.gz` into `build/tcc/X86_64/`. +1. **Unpack** `tcc-0.9.26.tar.gz` into `build/amd64/vendor/tcc/`. 2. **Apply simple-patches**: `remove-fileopen.before/.after` then `addback-fileopen.before/.after` against `tcctools.c`. Implemented as an `awk` literal-block replacer (live-bootstrap's `simple-patch` @@ -190,7 +190,7 @@ libc, where errno is the plain global mes expects. live-bootstrap-style direct invocation doesn't already validate. ~750 KB output. 7. **Stage out** mes libc + libtcc1 + crt1.o + headers into - `build/tcc/X86_64/stage3-input/`, so stage 3 can mount + `build/amd64/vendor/tcc/stage3-input/`, so stage 3 can mount them without re-running stage 2. 8. **Smoke test**: `tcc-boot0-mes -version`. **Expected to SEGV under QEMU on macOS arm64** (Issue §3); native x86_64 needed to verify @@ -283,7 +283,7 @@ scripts/stage2-alpine.sh --arch X86_64 scripts/stage3-rebuild.sh --arch X86_64 # blocked on Issue §3 today ``` -Artifacts in `build/tcc/X86_64/`: +Artifacts in `build/amd64/vendor/tcc/`: | File | Stage | Size | Built by | What it is | |-------------------|-------|---------|-----------------------|-------------------------------------------| diff --git a/scripts/boot-build-tcc-tcc.sh b/scripts/boot-build-tcc-tcc.sh @@ -38,14 +38,15 @@ OUT=$1 CC=${2:-build/$ARCH/tcc-boot2/tcc-boot2} case "$ARCH" in - aarch64) TCC_TARGET=ARM64; LIB_TARGET_DEFINES="-D TCC_TARGET_ARM64=1 -D TCC_TARGET_ARM=1" ;; - amd64) TCC_TARGET=X86_64; LIB_TARGET_DEFINES= ;; - riscv64) TCC_TARGET=RISCV64; LIB_TARGET_DEFINES="-D TCC_TARGET_RISCV64=1" ;; + aarch64) LIB_TARGET_DEFINES="-D TCC_TARGET_ARM64=1 -D TCC_TARGET_ARM=1" ;; + amd64) LIB_TARGET_DEFINES= ;; + riscv64) LIB_TARGET_DEFINES="-D TCC_TARGET_RISCV64=1" ;; *) echo "boot-build-tcc-tcc.sh: unsupported ARCH '$ARCH'" >&2; exit 2 ;; esac -TCC_INC=build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/include -TCC_FLAT=build/tcc/$TCC_TARGET/tcc.flat.c +TCC_VENDOR=build/$ARCH/vendor/tcc +TCC_INC=$TCC_VENDOR/tcc-0.9.26-1147-gee75a10c/include +TCC_FLAT=$TCC_VENDOR/tcc.flat.c LIBC_O=build/$ARCH/tcc-libc/libc.o MEM_O=build/$ARCH/tcc-libc/mem.o SYS_O=build/$ARCH/tcc-libc/sys_stubs.o @@ -54,7 +55,7 @@ WORK=$(dirname "$OUT") mkdir -p "$WORK" -TCC_LIB_DIR=build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/lib +TCC_LIB_DIR=$TCC_VENDOR/tcc-0.9.26-1147-gee75a10c/lib LIB_OBJS= if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "riscv64" ]; then diff --git a/scripts/boot-run-tests.sh b/scripts/boot-run-tests.sh @@ -625,7 +625,7 @@ run_tcc_cc_suite() { esac start=build/$ARCH/tcc-cc/start.o mem=build/$ARCH/tcc-cc/mem.o - tcc_include=build/tcc/$tcc_target/tcc-0.9.26-1147-gee75a10c/include + tcc_include=build/$ARCH/vendor/tcc/tcc-0.9.26-1147-gee75a10c/include # x86_64 only: __va_start / __va_arg intrinsics for variadic # functions. Other arches lower va_arg without out-of-line helpers. if [ "$ARCH" = "amd64" ]; then @@ -736,7 +736,7 @@ run_tcc_libc_suite() { sys_stubs=build/$ARCH/tcc-libc/sys_stubs.o mem=build/$ARCH/tcc-libc/mem.o libc=build/$ARCH/tcc-libc/libc.o - tcc_include=build/tcc/$tcc_target/tcc-0.9.26-1147-gee75a10c/include + tcc_include=build/$ARCH/vendor/tcc/tcc-0.9.26-1147-gee75a10c/include # x86_64 only: __va_start / __va_arg intrinsics for variadic # functions. mes-libc's printf family hits this directly. if [ "$ARCH" = "amd64" ]; then diff --git a/scripts/boot3.sh b/scripts/boot3.sh @@ -13,11 +13,11 @@ ## tcc3 = tcc-source compiled by tcc2 ← boot4 ## ## ─── Inputs (host-side, auto-built if missing) ──────────────────────── -## build/tcc/$TCC_TARGET/tcc.flat.c -## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/{include,lib} +## build/$ARCH/vendor/tcc/tcc.flat.c +## build/$ARCH/vendor/tcc/tcc-0.9.26-1147-gee75a10c/{include,lib} ## — flattened tcc TU + unpacked tree; built ## via scripts/stage1-flatten.sh --arch -## $TCC_TARGET (host cc -E, no container) +## $ARCH (host cc -E, no container) ## build/$ARCH/vendor/mes-libc/libc.flat.c ## — flattened mes-libc TU; built via ## scripts/libc-flatten.sh --arch $ARCH @@ -56,9 +56,9 @@ usage() { echo "usage: $0 <aarch64|amd64|riscv64>" >&2; exit 2; } ARCH=$1 case "$ARCH" in - aarch64) PLATFORM=linux/arm64; TCC_TARGET=ARM64 ;; - amd64) PLATFORM=linux/amd64; TCC_TARGET=X86_64 ;; - riscv64) PLATFORM=linux/riscv64; TCC_TARGET=RISCV64 ;; + aarch64) PLATFORM=linux/arm64 ;; + amd64) PLATFORM=linux/amd64 ;; + riscv64) PLATFORM=linux/riscv64 ;; *) usage ;; esac @@ -72,8 +72,9 @@ BOOT2=build/$ARCH/boot2 OUT=build/$ARCH/boot3 STAGE=build/$ARCH/.boot3-stage -TCC_DIR=build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c -TCC_FLAT=build/tcc/$TCC_TARGET/tcc.flat.c +TCC_VENDOR=build/$ARCH/vendor/tcc +TCC_DIR=$TCC_VENDOR/tcc-0.9.26-1147-gee75a10c +TCC_FLAT=$TCC_VENDOR/tcc.flat.c LIBC_FLAT=build/$ARCH/vendor/mes-libc/libc.flat.c # ── ensure container image exists (podman driver only) ──────────────── @@ -101,9 +102,9 @@ export IMAGE PLATFORM DRIVER # tcc.flat.c + the unpacked $TCC_DIR/{include,lib} tree are produced # together by stage1-flatten.sh; libc.flat.c by libc-flatten.sh. Both # run on the host (cc -E), no container — auto-invoke if missing. -if [ ! -e "$TCC_FLAT" ] || [ ! -d "$TCC_DIR/include" ] || [ ! -e build/tcc/stdarg-bridge.h ]; then +if [ ! -e "$TCC_FLAT" ] || [ ! -d "$TCC_DIR/include" ] || [ ! -e "$TCC_VENDOR/stdarg-bridge.h" ]; then echo "[boot3 $ARCH] flatten tcc.flat.c (host)" - scripts/stage1-flatten.sh --arch "$TCC_TARGET" + scripts/stage1-flatten.sh --arch "$ARCH" fi if [ ! -e "$LIBC_FLAT" ]; then echo "[boot3 $ARCH] flatten libc.flat.c (host)" diff --git a/scripts/boot4.sh b/scripts/boot4.sh @@ -20,11 +20,11 @@ ## tcc3 = tcc-source compiled by tcc2 ← produced here ## ## ─── Inputs (host-side, auto-built if missing) ──────────────────────── -## build/tcc/$TCC_TARGET/tcc.flat.c -## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/{include,lib} +## build/$ARCH/vendor/tcc/tcc.flat.c +## build/$ARCH/vendor/tcc/tcc-0.9.26-1147-gee75a10c/{include,lib} ## — flattened tcc TU + unpacked tree; built ## via scripts/stage1-flatten.sh --arch -## $TCC_TARGET (host cc -E, no container) +## $ARCH (host cc -E, no container) ## build/$ARCH/vendor/mes-libc/libc.flat.c ## — flattened mes-libc TU; built via ## scripts/libc-flatten.sh --arch $ARCH @@ -34,17 +34,17 @@ ## tcc-libc/$ARCH/start.S — _start, calls __libc_init+main ## tcc-libc/$ARCH/sys_stubs.S — sys_* syscall wrappers ## tcc-cc/mem.c — memcpy/memmove/memset/memcmp -## build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c/lib/libtcc1.c +## build/$ARCH/vendor/tcc/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 +## build/$ARCH/vendor/tcc/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 +## build/$ARCH/vendor/tcc/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 +## build/$ARCH/vendor/tcc/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 -## scripts/boot-hello.c — smoke binary +## build/$ARCH/vendor/tcc/tcc.flat.c — flattened tcc TU +## build/$ARCH/vendor/mes-libc/libc.flat.c — flattened mes-libc TU +## scripts/boot-hello.c — smoke binary ## ## ─── Inputs (binaries from prior stages) ────────────────────────────── ## build/$ARCH/boot3/tcc0 — built by scripts/boot3.sh @@ -87,15 +87,12 @@ ARCH=$1 case "$ARCH" in aarch64) PLATFORM=linux/arm64; - TCC_TARGET=ARM64; LIBTCC1_C_SRCS="lib-arm64.c"; LIBTCC1_ASM_SRCS="" ;; amd64) PLATFORM=linux/amd64; - TCC_TARGET=X86_64; LIBTCC1_C_SRCS="libtcc1.c va_list.c"; LIBTCC1_ASM_SRCS="alloca86_64.S alloca86_64-bt.S" ;; riscv64) PLATFORM=linux/riscv64; - TCC_TARGET=RISCV64; LIBTCC1_C_SRCS="lib-arm64.c"; LIBTCC1_ASM_SRCS="" ;; *) usage ;; @@ -111,8 +108,9 @@ BOOT3=build/$ARCH/boot3 OUT=build/$ARCH/boot4 STAGE=build/$ARCH/.boot4-stage -TCC_DIR=build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c -TCC_FLAT=build/tcc/$TCC_TARGET/tcc.flat.c +TCC_VENDOR=build/$ARCH/vendor/tcc +TCC_DIR=$TCC_VENDOR/tcc-0.9.26-1147-gee75a10c +TCC_FLAT=$TCC_VENDOR/tcc.flat.c LIBC_FLAT=build/$ARCH/vendor/mes-libc/libc.flat.c # ── ensure container image exists (podman driver only) ──────────────── @@ -138,10 +136,10 @@ export IMAGE PLATFORM DRIVER # ── prerequisite: host-flattened sources + unpacked tcc tree ────────── # Normally these were produced by boot3 (auto-invoked by stage1-flatten # / libc-flatten there). Re-check here so boot4 runs standalone if a -# user has tcc0 but blew away build/tcc/. -if [ ! -e "$TCC_FLAT" ] || [ ! -d "$TCC_DIR/include" ] || [ ! -e "$TCC_DIR/lib/lib-arm64.c" ] || [ ! -e build/tcc/stdarg-bridge.h ]; then +# user has tcc0 but blew away build/$ARCH/vendor/tcc/. +if [ ! -e "$TCC_FLAT" ] || [ ! -d "$TCC_DIR/include" ] || [ ! -e "$TCC_DIR/lib/lib-arm64.c" ] || [ ! -e "$TCC_VENDOR/stdarg-bridge.h" ]; then echo "[boot4 $ARCH] flatten tcc.flat.c (host)" - scripts/stage1-flatten.sh --arch "$TCC_TARGET" + scripts/stage1-flatten.sh --arch "$ARCH" fi if [ ! -e "$LIBC_FLAT" ]; then echo "[boot4 $ARCH] flatten libc.flat.c (host)" diff --git a/scripts/boot5-calibrate.sh b/scripts/boot5-calibrate.sh @@ -45,7 +45,7 @@ MUSL_TARBALL=vendor/upstream/musl-1.2.5.tar.gz MUSL_OVERRIDES=vendor/upstream/musl-1.2.5-overrides MUSL_DELETES=vendor/upstream/musl-1.2.5-deletes.txt MUSL_GENERATED=vendor/upstream/musl-1.2.5-generated/$MUSL_ARCH -BRIDGE_FILE=build/tcc/stdarg-bridge.h +BRIDGE_FILE=build/$ARCH/vendor/tcc/stdarg-bridge.h SKIP_OUT=vendor/upstream/musl-1.2.5-skip-$ARCH.txt [ -x "$BOOT4/tcc3" ] || { echo "missing $BOOT4/tcc3 (run scripts/boot4.sh $ARCH)" >&2; exit 1; } diff --git a/scripts/boot5.sh b/scripts/boot5.sh @@ -24,11 +24,14 @@ ## — list of upstream files removed by the ## same patch set (one path per line, ## relative to musl-1.2.5/). -## build/tcc/stdarg-bridge.h +## build/$ARCH/vendor/tcc/stdarg-bridge.h ## — per-arch __builtin_va_list bridge, ## generated by scripts/stage1-flatten.sh -## (shared with boot3/boot4; one file, -## three arches gated by #ifdef inside) +## (shared with boot3/boot4; the file is +## byte-identical across arches but a +## per-arch copy is written so every +## artifact under build/$ARCH/ comes from +## a single boot.sh $ARCH invocation) ## ## ─── Tools ──────────────────────────────────────────────────────────── ## In container: scratch + busybox (no libc, no /etc, no resolver). @@ -80,7 +83,7 @@ MUSL_OVERRIDES=vendor/upstream/musl-1.2.5-overrides MUSL_DELETES=vendor/upstream/musl-1.2.5-deletes.txt MUSL_GENERATED=vendor/upstream/musl-1.2.5-generated/$MUSL_ARCH MUSL_SKIP=vendor/upstream/musl-1.2.5-skip-$ARCH.txt -BRIDGE_FILE=build/tcc/stdarg-bridge.h +BRIDGE_FILE=build/$ARCH/vendor/tcc/stdarg-bridge.h # ── prerequisites ───────────────────────────────────────────────────── [ -x "$BOOT4/tcc3" ] || { echo "[boot5 $ARCH] missing $BOOT4/tcc3 (run scripts/boot4.sh $ARCH)" >&2; exit 1; } diff --git a/scripts/libc-flatten.sh b/scripts/libc-flatten.sh @@ -166,7 +166,7 @@ HOST_CC=${HOST_CC:-cc} # per-arch va_list typedef + __builtin_va_* → tcc __va_* mapping into # libc.flat.c, eliminating the need for `-I /work/in/tcc-include # -include /work/in/tcc-include/stdarg.h` on every in-container compile. -BRIDGE=$ROOT/build/tcc/stdarg-bridge.h +BRIDGE=$ROOT/build/$ARCH/vendor/tcc/stdarg-bridge.h [ -e "$BRIDGE" ] || { echo "missing $BRIDGE — run scripts/stage1-flatten.sh first" >&2; exit 1; } # -I order matters: vendor/boot2-include first so our stdarg.h shim diff --git a/scripts/run-gcc-libc-flat-tcc.sh b/scripts/run-gcc-libc-flat-tcc.sh @@ -29,7 +29,7 @@ TCC=${TCC:-build/$ARCH/tcc-gcc/tcc-gcc} START=build/$ARCH/tcc-cc/start.o OUT_ROOT=build/$ARCH/tests/gcc-libc-flat-tcc WORK_ROOT=build/$ARCH/.work/tests/gcc-libc-flat-tcc -TCC_SRC=build/tcc/$TCC_TARGET/tcc-0.9.26-1147-gee75a10c +TCC_SRC=build/$ARCH/vendor/tcc/tcc-0.9.26-1147-gee75a10c MES_INC=vendor/mes-libc/include case "$ARCH" in aarch64) MES_LINUX_INC=vendor/mes-libc/include/linux/riscv64 ;; @@ -38,7 +38,7 @@ esac RUNTIME=$WORK_ROOT/runtime.a [ -x "$TCC" ] || { - echo "missing $TCC; build it with scripts/build-tcc-gcc.sh and build/tcc/$TCC_TARGET/tcc.flat.c" >&2 + echo "missing $TCC; build it with scripts/build-tcc-gcc.sh and $TCC_SRC/../tcc.flat.c" >&2 exit 2 } [ -r "$START" ] || { echo "missing $START" >&2; exit 2; } diff --git a/scripts/stage1-flatten.sh b/scripts/stage1-flatten.sh @@ -9,7 +9,7 @@ ## 1. unpack tcc-0.9.26-1147-gee75a10c.tar.gz ## 2. apply live-bootstrap simple-patches (tcctools.c file-open reorder) ## 3. host cc -E -nostdinc with mes-bundled headers + tcc-mes defines -## 4. emit build/tcc/<arch>/tcc.flat.c +## 4. emit build/<arch>/vendor/tcc/tcc.flat.c ## 5. (--verify) compile tcc.flat.c with host cc to confirm well-formedness ## ## Stage 1 deliberately stays on the host: it is just text manipulation @@ -34,11 +34,11 @@ while [ $# -gt 0 ]; do done case "$ARCH" in - X86_64|x86_64) MES_ARCH=x86_64; HAVE_LL=1; TCC_TARGET_DEFINE=X86_64; CPP_ARCH=x86_64 ;; - I386|i386) MES_ARCH=x86; HAVE_LL=0; TCC_TARGET_DEFINE=I386; CPP_ARCH=x86 ;; - RISCV64|riscv64) MES_ARCH=riscv64; HAVE_LL=1; TCC_TARGET_DEFINE=RISCV64; CPP_ARCH=riscv64 ;; + X86_64|x86_64|amd64) BOOT_ARCH=amd64; MES_ARCH=x86_64; HAVE_LL=1; TCC_TARGET_DEFINE=X86_64; CPP_ARCH=x86_64 ;; + I386|i386) BOOT_ARCH=i386; MES_ARCH=x86; HAVE_LL=0; TCC_TARGET_DEFINE=I386; CPP_ARCH=x86 ;; + RISCV64|riscv64) BOOT_ARCH=riscv64; MES_ARCH=riscv64; HAVE_LL=1; TCC_TARGET_DEFINE=RISCV64; CPP_ARCH=riscv64 ;; ARM64|arm64|AARCH64|aarch64) - MES_ARCH=riscv64; HAVE_LL=1; TCC_TARGET_DEFINE=ARM64; CPP_ARCH=aarch64 ;; + BOOT_ARCH=aarch64; MES_ARCH=riscv64; HAVE_LL=1; TCC_TARGET_DEFINE=ARM64; CPP_ARCH=aarch64 ;; *) echo "unknown ARCH: $ARCH" >&2; exit 2 ;; esac @@ -57,7 +57,7 @@ esac # vendor/boot2-include/ — our own header shim, wins # -I priority for stdarg.h ROOT=$(cd "$(dirname "$0")/.." && pwd) -WORK=$ROOT/build/tcc/$ARCH +WORK=$ROOT/build/$BOOT_ARCH/vendor/tcc DISTFILES=$ROOT/vendor/upstream LB_PATCHES=$ROOT/scripts/simple-patches/tcc-0.9.26-lb OUR_PATCHES=$ROOT/scripts/simple-patches/tcc-0.9.26 @@ -273,15 +273,15 @@ FLAT=$WORK/tcc.flat.c -D TCC_TARGET_${TCC_TARGET_DEFINE}=1 \ "$SRC/tcc.c" > "$FLAT.body" -# Publish the post-patch tcc <stdarg.h> as a shared bridge file. -# libc-flatten.sh prepends the same bridge to libc.flat.c, so the -# boot3/boot4 container compiles no longer need `-I /work/in/tcc-include -# -include /work/in/tcc-include/stdarg.h`. The patched stdarg.h is -# byte-identical across X86_64 / ARM64 / RISCV64 (per-arch logic lives -# inside its #ifdefs), so a cross-arch shared path is fine — whichever -# arch's stage1-flatten.sh runs last wins, idempotently. -BRIDGE=$ROOT/build/tcc/stdarg-bridge.h -mkdir -p "$ROOT/build/tcc" +# Publish the post-patch tcc <stdarg.h> as a per-arch bridge file +# alongside tcc.flat.c. libc-flatten.sh prepends the same bridge to +# libc.flat.c, so the boot3/boot4 container compiles no longer need +# `-I /work/in/tcc-include -include /work/in/tcc-include/stdarg.h`. +# The patched stdarg.h is byte-identical across X86_64 / ARM64 / RISCV64 +# (per-arch logic lives inside its #ifdefs); we still write a per-arch +# copy so every artifact under build/<arch>/ comes from a single +# `boot.sh <arch>` invocation, with nothing shared across arches. +BRIDGE=$WORK/stdarg-bridge.h cp "$SRC/include/stdarg.h" "$BRIDGE" # Prepend the bridge into tcc.flat.c, guarded by !CCSCM so cc.scm diff --git a/scripts/stage2-alpine.sh b/scripts/stage2-alpine.sh @@ -14,7 +14,7 @@ ## definition and links cleanly. ## ## Pre-condition: -## build/tcc/<arch>/tcc.flat.c (run scripts/stage1-flatten.sh) +## build/amd64/vendor/tcc/tcc.flat.c (run scripts/stage1-flatten.sh) ## ## Inside alpine:latest (linux/amd64): ## 1. apk add gcc musl-dev @@ -35,7 +35,7 @@ ## Expected to segfault under QEMU x86_64 emulation on macOS arm64 ## (Issue §3); native x86_64 needed to verify cleanly. ## -## Output: build/tcc/<arch>/tcc-boot0-mes (static, mes-libc-linked). +## Output: build/amd64/vendor/tcc/tcc-boot0-mes (static, mes-libc-linked). ## This artifact is what stage 3 (busybox) consumes to drive the ## tcc-boot1 / tcc-boot2 chain. ## @@ -58,9 +58,10 @@ if [ "$ARCH" != "X86_64" ]; then exit 2 fi MES_ARCH=x86_64 +BOOT_ARCH=amd64 ROOT=$(cd "$(dirname "$0")/.." && pwd) -WORK=$ROOT/build/tcc/$ARCH +WORK=$ROOT/build/$BOOT_ARCH/vendor/tcc # This is the legacy gcc-driven path the cc.scm tcc-boot2 chain # replaces (see docs/TCC.md). Not on the main `make tcc-boot2` # build path; kept around as a verification fallback. Requires the @@ -98,13 +99,14 @@ echo "(slow on macOS arm64 — runs under QEMU linux/amd64)" TCC_PKG=tcc-0.9.26-1147-gee75a10c podman run --rm -i --platform linux/amd64 \ - -v "$ROOT":/work -w /work alpine:latest sh -s "$ARCH" "$MES_ARCH" "$MES_PKG" "$TCC_PKG" <<'CONTAINER_SCRIPT' + -v "$ROOT":/work -w /work alpine:latest sh -s "$ARCH" "$MES_ARCH" "$MES_PKG" "$TCC_PKG" "$BOOT_ARCH" <<'CONTAINER_SCRIPT' set -eu ARCH=$1 MES_ARCH=$2 MES_PKG=$3 TCC_PKG=$4 -WORK=/work/build/tcc/$ARCH +BOOT_ARCH=$5 +WORK=/work/build/$BOOT_ARCH/vendor/tcc # --- (1) install gcc + musl-dev (provides libc.a for -static) -------- apk add --no-cache gcc musl-dev >/dev/null diff --git a/scripts/stage3-rebuild.sh b/scripts/stage3-rebuild.sh @@ -14,10 +14,10 @@ ## flatten set). ## ## Pre-condition: -## build/tcc/<arch>/tcc-boot0-mes -## build/tcc/<arch>/stage3-input/ (staged by stage 2) -## build/tcc/<arch>/tcc-0.9.26-1147-gee75a10c/ (patched, from stage 1) -## build/tcc/<arch>/mes-0.27.1/ (from stage 2) +## build/amd64/vendor/tcc/tcc-boot0-mes +## build/amd64/vendor/tcc/stage3-input/ (staged by stage 2) +## build/amd64/vendor/tcc/tcc-0.9.26-1147-gee75a10c/ (patched, from stage 1) +## build/amd64/vendor/tcc/mes-0.27.1/ (from stage 2) ## ## Container: docker.io/library/busybox:musl on linux/amd64. ## Tools used inside: busybox sh + tcc-boot0-mes (which provides its own @@ -49,9 +49,10 @@ if [ "$ARCH" != "X86_64" ]; then exit 2 fi MES_ARCH=x86_64 +BOOT_ARCH=amd64 ROOT=$(cd "$(dirname "$0")/.." && pwd) -WORK=$ROOT/build/tcc/$ARCH +WORK=$ROOT/build/$BOOT_ARCH/vendor/tcc TCC_PKG=tcc-0.9.26-1147-gee75a10c MES_PKG=mes-0.27.1 @@ -66,13 +67,14 @@ echo "(Issue §3 may block on macOS arm64 / QEMU; native x86_64 expected to succ podman run --rm -i --platform linux/amd64 \ -v "$ROOT":/work -w /work \ - docker.io/library/busybox:musl sh -s "$ARCH" "$MES_ARCH" "$TCC_PKG" "$MES_PKG" <<'CONTAINER_SCRIPT' + docker.io/library/busybox:musl sh -s "$ARCH" "$MES_ARCH" "$TCC_PKG" "$MES_PKG" "$BOOT_ARCH" <<'CONTAINER_SCRIPT' set -eu ARCH=$1 MES_ARCH=$2 TCC_PKG=$3 MES_PKG=$4 -WORK=/work/build/tcc/$ARCH +BOOT_ARCH=$5 +WORK=/work/build/$BOOT_ARCH/vendor/tcc # --- install tcc-boot0-mes + mes libc bits at baked-in paths -------- mkdir -p /lib/tcc /include/mes /bin