boot2

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

commit c26c833c509d82ff3c9c7023792eab3cb1745e23
parent 3a02008437033476d122af2f3ec92dcd6c61235e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed,  6 May 2026 15:20:20 -0700

reorg PR2: generate run.scm at prep, finish scripts/ → bootprep/ move

run.scm files are now host-generated once at prep time and read at
runtime, instead of regenerated inside each bootN.sh. Removes the
runtime/prep boundary blur that motivated the reorg.

- scripts/{boot4,boot5,boot6}-gen-runscm.sh -> bootprep/
- scripts/{boot3-run.scm,boot-hello.c}     -> bootprep/assets/
- scripts/ removed entirely.

- New bootprep/boot5-enumerate.sh extracts the musl source-enumeration
  block from boot5.sh (base/arch/replaced/keep_base, crt-mode). Runs
  at prep against the post-prep-musl tree; writes
  build/<arch>/src/run/boot5/{build-srcs.txt,crt-mode}.

- bootprep/{boot4,boot5,boot6}-gen-runscm.sh take a single <arch> arg
  and write build/<arch>/src/run/bootN.scm. boot5-gen reads the
  enumerator's outputs from the same run/ tree.

- prep-src.sh now copies boot3-run.scm + invokes boot4/6-gen-runscm.
  prep-musl.sh invokes boot5-enumerate + boot5-gen-runscm after
  applying the skip filter.

- boot/boot{3,4,5,6}.sh: drop runscm_gen (and boot5's enumeration
  block); each just calls runscm_runscm "$SRC/run/bootN.scm". boot5
  keeps the per-source obj-dir mkdir (writes into the per-driver
  build dir, not src/) but reads the source list from the prep-time
  build-srcs.txt.

- boot/lib-runscm.sh: runscm_gen deleted; the special-case
  self-copy in runscm_run goes with it.

Verified: ./boot/boot.sh aarch64 runs clean to boot6/Image (51s).

Diffstat:
MMakefile | 14++++++--------
Mboot/boot3.sh | 7++++---
Mboot/boot4.sh | 7++++---
Mboot/boot5.sh | 100++++++++++++-------------------------------------------------------------------
Mboot/boot6.sh | 7++++---
Mboot/lib-runscm.sh | 22++--------------------
Rscripts/boot-hello.c -> bootprep/assets/boot-hello.c | 0
Rscripts/boot3-run.scm -> bootprep/assets/boot3-run.scm | 0
Abootprep/boot4-gen-runscm.sh | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abootprep/boot5-enumerate.sh | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abootprep/boot5-gen-runscm.sh | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abootprep/boot6-gen-runscm.sh | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbootprep/prep-musl.sh | 7+++++++
Mbootprep/prep-src.sh | 17++++++++++++++++-
Mdocs/MUSL.md | 4++--
Dscripts/boot4-gen-runscm.sh | 142-------------------------------------------------------------------------------
Dscripts/boot5-gen-runscm.sh | 149-------------------------------------------------------------------------------
Dscripts/boot6-gen-runscm.sh | 100-------------------------------------------------------------------------------
18 files changed, 559 insertions(+), 516 deletions(-)

diff --git a/Makefile b/Makefile @@ -97,6 +97,8 @@ clean: PREP_SRC_COMMON_SRCS := \ bootprep/prep-src.sh boot/lib-arch.sh \ bootprep/stage1-flatten.sh bootprep/libc-flatten.sh \ + bootprep/boot4-gen-runscm.sh bootprep/boot6-gen-runscm.sh \ + bootprep/assets/boot3-run.scm bootprep/assets/boot-hello.c \ M1pp/M1pp.P1 hex2pp/hex2pp.P1 \ P1/P1.M1pp P1/P1pp.P1pp \ P1/entry-libc.P1pp P1/entry-plain.P1pp P1/elf-end.P1pp \ @@ -104,7 +106,6 @@ PREP_SRC_COMMON_SRCS := \ scheme1/scheme1.P1pp scheme1/prelude.scm \ cc/cc.scm cc/main.scm \ tcc/cc/mem.c \ - scripts/boot-hello.c \ seed-kernel/kernel.c \ vendor/musl/1.2.5.tar.gz \ vendor/musl/deletes.txt @@ -144,6 +145,7 @@ build/$1/src/.stamp: $$(PREP_SRC_COMMON_SRCS) $$(call prep_src_arch_srcs,$1) # its own boot4 build outside this make graph — keep make's deps simple. build/$1/src/musl/.stamp: build/$1/src/.stamp \ bootprep/prep-musl.sh boot/lib-arch.sh \ + bootprep/boot5-enumerate.sh bootprep/boot5-gen-runscm.sh \ $$(wildcard vendor/musl/skip-$1.txt) bootprep/prep-musl.sh $1 @mkdir -p $$(@D) && touch $$@ @@ -196,7 +198,6 @@ build/$1/$2/boot2/catm build/$1/$2/boot2/scheme1: build/$1/$2/boot2/.stamp ; build/$1/$2/boot3/.stamp: \ build/$1/$2/boot2/.stamp build/$1/$2/boot1/.stamp \ boot/boot3.sh boot/lib-arch.sh boot/lib-runscm.sh \ - scripts/boot3-run.scm \ $$(call seed_kernel_dep,$1,$2) DRIVER=$2 boot/boot3.sh $1 @touch $$@ @@ -207,8 +208,7 @@ build/$1/$2/boot3/tcc.flat.P1pp: build/$1/$2/boot3/.stamp ; # boot4: tcc0 -> tcc1 -> tcc2 -> tcc3 self-host chain (+ libc.a, libtcc1.a, hello) build/$1/$2/boot4/.stamp: \ build/$1/$2/boot3/.stamp build/$1/$2/boot2/.stamp \ - boot/boot4.sh scripts/boot4-gen-runscm.sh \ - boot/lib-arch.sh boot/lib-runscm.sh \ + boot/boot4.sh boot/lib-arch.sh boot/lib-runscm.sh \ $$(call seed_kernel_dep,$1,$2) DRIVER=$2 boot/boot4.sh $1 @touch $$@ @@ -222,8 +222,7 @@ build/$1/$2/boot4/libtcc1.a: build/$1/$2/boot4/.stamp ; build/$1/$2/boot5/.stamp: \ build/$1/$2/boot4/.stamp build/$1/$2/boot2/.stamp \ build/$1/src/musl/.stamp \ - boot/boot5.sh scripts/boot5-gen-runscm.sh \ - boot/lib-arch.sh boot/lib-runscm.sh \ + boot/boot5.sh boot/lib-arch.sh boot/lib-runscm.sh \ $$(call seed_kernel_dep,$1,$2) DRIVER=$2 boot/boot5.sh $1 @touch $$@ @@ -235,8 +234,7 @@ build/$1/$2/boot5/hello: build/$1/$2/boot5/.stamp ; # boot6: seed-kernel ELF/Image, built with boot4's tcc3 build/$1/$2/boot6/.stamp: \ build/$1/$2/boot4/.stamp build/$1/$2/boot2/.stamp \ - boot/boot6.sh scripts/boot6-gen-runscm.sh \ - boot/lib-arch.sh boot/lib-runscm.sh \ + boot/boot6.sh boot/lib-arch.sh boot/lib-runscm.sh \ $$(call seed_kernel_dep,$1,$2) DRIVER=$2 boot/boot6.sh $1 @touch $$@ diff --git a/boot/boot3.sh b/boot/boot3.sh @@ -25,8 +25,9 @@ ## build/$ARCH/$DRIVER/boot2/{catm, scheme1} ## ## ─── Tools ──────────────────────────────────────────────────────────── -## scheme1 evaluates scripts/boot3-run.scm against the flat staging -## root. Same run.scm drives both DRIVER=podman (cwd=/work) and +## scheme1 evaluates build/$ARCH/src/run/boot3.scm against the flat +## staging root (copied from bootprep/assets/boot3-run.scm by +## prep-src). Same run.scm drives both DRIVER=podman (cwd=/work) and ## DRIVER=seed (cwd=/). Stage A is pure scheme1 + M1pp + hex2pp; no ## asm step. ## @@ -59,7 +60,7 @@ require_prev "$BOOT2" catm scheme1 runscm_init "$STAGE" "$OUT" runscm_scheme1 "$BOOT2/scheme1" runscm_prelude "$SRC/src/scheme1/prelude.scm" -runscm_runscm scripts/boot3-run.scm +runscm_runscm "$SRC/run/boot3.scm" runscm_input catm "$BOOT2/catm" runscm_input M1pp "$BOOT1/M1pp" diff --git a/boot/boot4.sh b/boot/boot4.sh @@ -26,8 +26,9 @@ ## build/$ARCH/$DRIVER/boot2/{catm, scheme1} ## ## ─── Tools ──────────────────────────────────────────────────────────── -## scheme1 evaluates a host-generated run.scm (from boot4-gen-runscm.sh) -## against the flat staging root. Every arch has CONFIG_TCC_ASM and +## scheme1 evaluates the prep-time run.scm at +## build/$ARCH/src/run/boot4.scm (generated by +## bootprep/boot4-gen-runscm.sh) against the flat staging root. Every arch has CONFIG_TCC_ASM and ## assembles .S inputs (start.S, sys_stubs.S) directly inside the ## container; no host asm step. The aarch64 assembler is the phase-1 ## arm64-asm.c that flatten patches into tcc-0.9.26 (see @@ -87,7 +88,7 @@ done # ── stage inputs and run scheme1 + boot4 run.scm under $DRIVER ──────── . boot/lib-runscm.sh runscm_init "$STAGE" "$OUT" -runscm_gen scripts/boot4-gen-runscm.sh "$ARCH" +runscm_runscm "$SRC/run/boot4.scm" runscm_scheme1 "$BOOT2/scheme1" runscm_prelude "$SRC/src/scheme1/prelude.scm" diff --git a/boot/boot5.sh b/boot/boot5.sh @@ -19,8 +19,10 @@ ## build/$ARCH/src/src/test-fixtures/boot-hello.c ## ## ─── Tools ──────────────────────────────────────────────────────────── -## scheme1 evaluates a host-generated run.scm (from boot5-gen-runscm.sh) -## against the flat staging root. +## scheme1 evaluates the prep-time run.scm at +## build/$ARCH/src/run/boot5.scm (generated by +## bootprep/boot5-gen-runscm.sh after prep-musl) against the flat +## staging root. ## ## ─── Outputs ───────────────────────────────────────────────────────── ## build/$ARCH/$DRIVER/boot5/libc.a @@ -51,99 +53,27 @@ require_file "$MUSL_DIR/skip.txt" "run bootprep/prep-musl.sh $ARCH" require_file "$SRC/src/tcc/stdarg-bridge.h" "run bootprep/prep-src.sh $ARCH" # ── prepare staging dirs ────────────────────────────────────────────── -# $STAGE/in/ — read-only inputs (becomes /work/in or in/ in tmpfs) -# $STAGE/out/ — writable outputs (becomes /work/out or out/ in tmpfs) -# $STAGE/_host/ — host-side scratch (enumeration outputs); never -# visible to the container/kernel. +# $STAGE/in/ — read-only inputs (becomes /work/in or in/ in tmpfs) +# $STAGE/out/ — writable outputs (becomes /work/out or out/ in tmpfs) . boot/lib-runscm.sh runscm_init "$STAGE" "$OUT" -mkdir -p "$STAGE/_host" +runscm_runscm "$SRC/run/boot5.scm" -# ── enumerate musl sources from the canonical tree ──────────────────── -# Mirrors musl's Makefile rule: a per-arch override (under -# $d/$MUSL_ARCH/) replaces the same-stem base file (under $d/). The -# canonical tree already had the per-arch skip filter applied by -# prep-musl.sh, so no skip subtraction is needed here. -SRC_TOP="src/aio src/conf src/crypt src/ctype src/dirent - src/env src/errno src/exit src/fcntl src/fenv src/internal - src/ipc src/legacy src/linux src/locale src/malloc - src/malloc/mallocng src/math src/misc src/mman src/mq - src/multibyte src/network src/passwd src/prng src/process - src/regex src/sched src/search src/select src/setjmp src/signal - src/stat src/stdio src/stdlib src/string src/temp src/termios - src/thread src/time src/unistd" - -( - cd "$MUSL_DIR" - for d in $SRC_TOP; do - [ -d "$d" ] || continue - for f in $d/*.c; do [ -f "$f" ] && echo "$f"; done - done -) > "$STAGE/_host/base.txt" - -( - cd "$MUSL_DIR" - for d in $SRC_TOP; do - [ -d "$d/$MUSL_ARCH" ] || continue - for f in $d/$MUSL_ARCH/*.c $d/$MUSL_ARCH/*.s $d/$MUSL_ARCH/*.S; do - [ -f "$f" ] && echo "$f" - done - done -) > "$STAGE/_host/arch.txt" - -# REPLACED: bases that have arch-specific overrides (drop them from -# BASE). KEEP = (BASE - REPLACED) ∪ ARCH. -awk -v ARCH="$MUSL_ARCH" ' - { - sub(/\.[^.]*$/, "") # strip extension - slot = "/" ARCH "/" - i = index($0, slot) - head = substr($0, 1, i - 1) - tail = substr($0, i + length(slot)) - print head "/" tail - } -' "$STAGE/_host/arch.txt" | sort -u > "$STAGE/_host/replaced.txt" - -# Filter base by removing stems that appear in replaced. -awk -v REPF="$STAGE/_host/replaced.txt" ' - BEGIN { while ((getline l < REPF) > 0) rep[l] = 1 } - { - stem = $0 - sub(/\.c$/, "", stem) - if (!(stem in rep)) print - } -' "$STAGE/_host/base.txt" > "$STAGE/_host/keep_base.txt" - -cat "$STAGE/_host/keep_base.txt" "$STAGE/_host/arch.txt" | sort -u > "$STAGE/_host/build-srcs.txt" - -n_src=$(wc -l < "$STAGE/_host/build-srcs.txt") -n_skip=$(grep -cv '^[[:space:]]*\(#\|$\)' "$MUSL_DIR/skip.txt" || true) -echo "[$BOOT_TAG] keep=$n_src skip=$n_skip (calibrated)" - -# Record CRT mode (asm vs c) so the gen-runscm step picks the right -# crti/crtn source set without re-checking $MUSL_DIR. -if [ -f "$MUSL_DIR/crt/$MUSL_ARCH/crti.s" ]; then - echo asm > "$STAGE/_host/crt-mode" -else - echo c > "$STAGE/_host/crt-mode" -fi - -# Pre-create per-source obj/ directories under $STAGE/out/obj/musl/ so +# Pre-create per-source obj/ dirs under $STAGE/out/obj/musl/ so # scheme1's (run "in/tcc" -c …) doesn't need to mkdir at runtime (tcc # errors out if the parent dir is missing, and scheme1 has no mkdir -# primitive). +# primitive). Per-source list is the build-srcs.txt produced by +# bootprep/boot5-enumerate.sh at prep time. +SRCS=$SRC/run/boot5/build-srcs.txt +require_file "$SRCS" "run bootprep/prep-musl.sh $ARCH" +COBJ=$STAGE/out/obj/musl +mkdir -p "$COBJ/crt" awk ' { sub(/\.[^.]*$/, "") if (match($0, /\/[^\/]*$/)) print substr($0, 1, RSTART - 1) } -' "$STAGE/_host/build-srcs.txt" | sort -u > "$STAGE/_host/build-objdirs.txt" -COBJ=$STAGE/out/obj/musl -mkdir -p "$COBJ/crt" -while read -r d; do mkdir -p "$COBJ/$d"; done < "$STAGE/_host/build-objdirs.txt" - -# ── generate run.scm and stage chain binaries ───────────────────────── -runscm_gen scripts/boot5-gen-runscm.sh "$MUSL_ARCH" "$STAGE/_host" +' "$SRCS" | sort -u | while read -r d; do mkdir -p "$COBJ/$d"; done runscm_scheme1 "$BOOT2/scheme1" runscm_prelude "$SRC/src/scheme1/prelude.scm" diff --git a/boot/boot6.sh b/boot/boot6.sh @@ -15,8 +15,9 @@ ## build/$ARCH/$DRIVER/boot2/scheme1 ## ## ─── Tools ──────────────────────────────────────────────────────────── -## scheme1 evaluates a host-generated run.scm (from boot6-gen-runscm.sh) -## against the flat staging root. +## scheme1 evaluates the prep-time run.scm at +## build/$ARCH/src/run/boot6.scm (generated by +## bootprep/boot6-gen-runscm.sh) against the flat staging root. ## ## ─── Outputs ───────────────────────────────────────────────────────── ## build/$ARCH/$DRIVER/boot6/$KERNEL_NAME @@ -51,7 +52,7 @@ done # ── stage inputs and run scheme1 + run.scm under $DRIVER ────────────── . boot/lib-runscm.sh runscm_init "$STAGE" "$OUT" -runscm_gen scripts/boot6-gen-runscm.sh "$ARCH" +runscm_runscm "$SRC/run/boot6.scm" runscm_scheme1 "$BOOT2/scheme1" runscm_prelude "$SRC/src/scheme1/prelude.scm" diff --git a/boot/lib-runscm.sh b/boot/lib-runscm.sh @@ -20,9 +20,7 @@ # runscm_init <staging-dir> <out-dir> # runscm_scheme1 <path> # init=scheme1 (boot2) # runscm_prelude <path> # scheme1/prelude.scm -# runscm_runscm <path> # static driver script -# runscm_gen <gen-script> <args...> # OR generate run.scm, -# # log size, register it. +# runscm_runscm <path> # driver script (run.scm) # runscm_input <name> <host-path> # repeatable; staged at in/<name> # runscm_input_tree <prefix> <src-root> # repeatable; tree under in/<prefix> # runscm_export <name>... # one or more output names @@ -52,18 +50,6 @@ runscm_scheme1() { S_SCHEME1=$1; } runscm_prelude() { S_PRELUDE=$1; } runscm_runscm() { S_RUNSCM=$1; } -# runscm_gen <gen-script> <args...> -# Run a host-side generator that emits run.scm to $S_STAGE_DIR/run.scm, -# log its size, and register it as the driver script. Used by -# boot4/5/6 which build their run.scm dynamically. -runscm_gen() { - _gen=$1; shift - _runscm=$S_STAGE_DIR/run.scm - "$_gen" "$@" "$_runscm" - echo "[$BOOT_TAG] generated run.scm: $(wc -l <"$_runscm") lines, $(wc -c <"$_runscm") bytes" - S_RUNSCM=$_runscm -} - runscm_input() { name=$1; src=$2 case "$name" in @@ -116,11 +102,7 @@ runscm_run() { chmod +x "$S_STAGE_DIR/in/scheme1" cat "$S_PRELUDE" "$S_RUNSCM" > "$S_STAGE_DIR/in/combined.scm" # Top-level reference copy of run.scm for human inspection. - # boot4/5 gen scripts already write here; skip the self-copy. - case "$S_RUNSCM" in - "$S_STAGE_DIR/run.scm") : ;; - *) cp "$S_RUNSCM" "$S_STAGE_DIR/run.scm" ;; - esac + cp "$S_RUNSCM" "$S_STAGE_DIR/run.scm" case "${DRIVER:-podman}" in podman) _runscm_run_podman "$timeout" ;; diff --git a/scripts/boot-hello.c b/bootprep/assets/boot-hello.c diff --git a/scripts/boot3-run.scm b/bootprep/assets/boot3-run.scm diff --git a/bootprep/boot4-gen-runscm.sh b/bootprep/boot4-gen-runscm.sh @@ -0,0 +1,145 @@ +#!/bin/sh +## boot4-gen-runscm.sh — emit run.scm driving boot4's tcc0→tcc1→tcc2→tcc3 +## chain inside the seed kernel. Mirrors boot/boot4.sh's per-stage shell +## emission; per-arch values resolved on the host so the .scm body is +## straight-line (run …) calls. +## +## Reads use in/<name>; writes (intermediates and exports) use out/<name>. +## +## Usage: bootprep/boot4-gen-runscm.sh <arch> +## Writes build/<arch>/src/run/boot4.scm. + +set -eu +[ "$#" -eq 1 ] || { echo "usage: $0 <arch>" >&2; exit 2; } +ARCH=$1 +OUT=build/$ARCH/src/run/boot4.scm +mkdir -p "$(dirname "$OUT")" + +case "$ARCH" in + aarch64) LIB_HELPER_SRC=lib-arm64.c; LIB_HELPER_OBJ=lib-arm64.o + LIB_HELPER_DEFS='"-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) LIB_HELPER_SRC=va_list.c; LIB_HELPER_OBJ=va_list.o + LIB_HELPER_DEFS='"-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) LIB_HELPER_SRC=lib-arm64.c; LIB_HELPER_OBJ=lib-arm64.o + LIB_HELPER_DEFS='"-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="" ;; + *) echo "boot4-gen: unknown arch $ARCH" >&2; exit 2 ;; +esac + +# Per-arch link base for user binaries. tcc 0.9.26's riscv64-link.c +# defaults to ELF_START_ADDR=0x10000, which lives below the seed +# kernel's USER_VA_LO (0x200000). amd64 (0x400000) and aarch64 +# (0x400000) defaults already sit inside the user window, so we leave +# them alone. Everywhere else in the chain (M0/hex2pp -B, boot6 +# -Wl,-Ttext) we link riscv64 user binaries at 0x600000; do the same +# here so tcc-built outputs are loadable inside the seed kernel. +case "$ARCH" in + riscv64) LINK_TTEXT='"-Wl,-Ttext=0x600000"' ;; + *) LINK_TTEXT= ;; +esac + +# emit_helpers — cc reads .S/.c sources from in/, writes .o to out/. +# cc_path is the cwd-relative path to the spawned compiler binary (in/tcc0 +# for round B; out/tcc1, out/tcc2 in later rounds). +emit_helpers() { + cc_path=$1; tag=$2 + cat <<EOF +(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/start.o" "in/start.S") "$tag start.o") +(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/sys_stubs.o" "in/sys_stubs.S") "$tag sys_stubs.o") +(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/mem.o" "in/mem.c") "$tag mem.o") +(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/libc.o" "in/libc.flat.c") "$tag libc.o") +(must (run "$cc_path" "-nostdlib" $LIB_HELPER_DEFS "-c" "-o" "out/$LIB_HELPER_OBJ" "in/$LIB_HELPER_SRC") "$tag $LIB_HELPER_OBJ") +EOF +} + +# emit_archive — uses prefix to namespace output object names per stage. +# pfx="s2-"/"s3-" for stage2/3. The .o objects archived into libtcc1.a +# keep their bare basenames (lib-arm64.o, …) — tcc -ar stores basenames +# only, so this matches podman's archive members exactly. They overwrite +# the stage's helper-named .o files; nothing post-archive in the same +# stage reads them as standalone .o. +emit_archive() { + cc_path=$1; tag=$2; pfx=$3 + echo "(must (run \"in/catm\" \"out/${pfx}crt1.o\" \"out/start.o\") \"copy crt1.o $pfx\")" + echo "(must (run \"$cc_path\" \"-ar\" \"rcs\" \"out/${pfx}libc.a\" \"out/sys_stubs.o\" \"out/mem.o\" \"out/libc.o\") \"$tag ${pfx}libc.a\")" + libtcc1_objs="" + for src in $LIBTCC1_C_SRCS; do + obj=${src%.c}.o + echo "(must (run \"$cc_path\" \"-nostdlib\" $LIBTCC1_C_DEFS \"-c\" \"-o\" \"out/${obj}\" \"in/$src\") \"$tag lt ${obj}\")" + libtcc1_objs="$libtcc1_objs \"out/${obj}\"" + done + for src in $LIBTCC1_ASM_SRCS; do + obj=${src%.S}.o + echo "(must (run \"$cc_path\" \"-nostdlib\" \"-c\" \"-o\" \"out/${obj}\" \"in/$src\") \"$tag lt ${obj}\")" + libtcc1_objs="$libtcc1_objs \"out/${obj}\"" + done + echo "(must (run \"$cc_path\" \"-ar\" \"rcs\" \"out/${pfx}libtcc1.a\"$libtcc1_objs) \"$tag ${pfx}libtcc1.a\")" +} + +emit_link_tcc() { + cc_path=$1; tag=$2; pfx=$3; out=$4 + echo "(must (run \"$cc_path\" \"-nostdlib\" $LINK_TTEXT \"out/${pfx}crt1.o\" \"in/tcc.flat.c\" \"out/${pfx}libc.a\" \"out/${pfx}libtcc1.a\" \"out/${pfx}libc.a\" \"-o\" \"out/$out\") \"$tag -> $out\")" +} + +{ +cat <<'PROLOGUE' +;; boot4 run.scm — drive tcc0 -> tcc1 -> tcc2 -> tcc3 inside seed kernel. +;; Generated by bootprep/boot4-gen-runscm.sh; mirrors boot/boot4.sh's +;; podman path stage-for-stage. Reads use in/; writes (intermediates and +;; exports) use out/. tcc0 is staged as in/tcc0; tcc1/tcc2/tcc3 are +;; produced and exported under out/. + +(define (must r tag) + (if (and (car r) (= 0 (cdr r))) + r + (begin + (write-string stderr "boot4: step failed: ") + (write-string stderr tag) + (write-string stderr "\n") + (exit 1)))) + +(write-string stdout "boot4: stage B (tcc0 helpers)\n") +PROLOGUE + +# Stage B: tcc0 builds helper objects (no archive). +emit_helpers in/tcc0 tcc0 + +cat <<EOF + +(write-string stdout "boot4: stage C (tcc0 -> tcc1)\n") +(must (run "in/tcc0" "-nostdlib" $LINK_TTEXT "out/start.o" "out/sys_stubs.o" "out/mem.o" "out/libc.o" "out/$LIB_HELPER_OBJ" "in/tcc.flat.c" "-o" "out/tcc1") "tcc0 -> tcc1") + +(write-string stdout "boot4: stage D (tcc1 -> tcc2)\n") +EOF + +# Stage D: tcc1 rebuilds helpers + archive, links tcc2. +emit_helpers out/tcc1 tcc1 +emit_archive out/tcc1 tcc1 "s2-" +emit_link_tcc out/tcc1 tcc1 "s2-" tcc2 + +cat <<EOF + +(write-string stdout "boot4: stage E (tcc2 -> tcc3)\n") +EOF + +# Stage E: tcc2 rebuilds helpers + archive, links tcc3. +emit_helpers out/tcc2 tcc2 +emit_archive out/tcc2 tcc2 "s3-" +emit_link_tcc out/tcc2 tcc2 "s3-" tcc3 + +cat <<EOF + +(write-string stdout "boot4: linking hello\n") +(must (run "out/tcc2" "-nostdlib" $LINK_TTEXT "out/s3-crt1.o" "in/hello.c" "out/s3-libc.a" "out/s3-libtcc1.a" "out/s3-libc.a" "-o" "out/hello") "tcc2 -> hello") +(write-string stdout "boot4: ALL-OK\n") +(exit 0) +EOF +} > "$OUT" diff --git a/bootprep/boot5-enumerate.sh b/bootprep/boot5-enumerate.sh @@ -0,0 +1,95 @@ +#!/bin/sh +## boot5-enumerate.sh — enumerate musl sources from the canonical +## post-prep-musl tree and emit the build-srcs / crt-mode files that +## boot5-gen-runscm.sh consumes. +## +## Mirrors musl's Makefile rule: a per-arch override (under +## $d/$MUSL_ARCH/) replaces the same-stem base file (under $d/). The +## canonical tree already had the per-arch skip filter applied by +## prep-musl.sh, so no skip subtraction is needed here. +## +## Inputs: build/<arch>/src/src/musl/ (post-prep-musl tree) +## Outputs: build/<arch>/src/run/boot5/build-srcs.txt +## build/<arch>/src/run/boot5/crt-mode ("asm" or "c") +## +## Usage: bootprep/boot5-enumerate.sh <arch> + +set -eu +[ "$#" -eq 1 ] || { echo "usage: $0 <arch>" >&2; exit 2; } +ARCH=$1 + +case "$ARCH" in + aarch64) MUSL_ARCH=aarch64 ;; + amd64) MUSL_ARCH=x86_64 ;; + riscv64) MUSL_ARCH=riscv64 ;; + *) echo "boot5-enumerate: unsupported arch '$ARCH'" >&2; exit 2 ;; +esac + +MUSL_DIR=build/$ARCH/src/src/musl +OUT_DIR=build/$ARCH/src/run/boot5 +[ -d "$MUSL_DIR" ] || { echo "missing $MUSL_DIR (run bootprep/prep-musl.sh $ARCH)" >&2; exit 1; } +mkdir -p "$OUT_DIR" + +SRC_TOP="src/aio src/conf src/crypt src/ctype src/dirent + src/env src/errno src/exit src/fcntl src/fenv src/internal + src/ipc src/legacy src/linux src/locale src/malloc + src/malloc/mallocng src/math src/misc src/mman src/mq + src/multibyte src/network src/passwd src/prng src/process + src/regex src/sched src/search src/select src/setjmp src/signal + src/stat src/stdio src/stdlib src/string src/temp src/termios + src/thread src/time src/unistd" + +( + cd "$MUSL_DIR" + for d in $SRC_TOP; do + [ -d "$d" ] || continue + for f in $d/*.c; do [ -f "$f" ] && echo "$f"; done + done +) > "$OUT_DIR/base.txt" + +( + cd "$MUSL_DIR" + for d in $SRC_TOP; do + [ -d "$d/$MUSL_ARCH" ] || continue + for f in $d/$MUSL_ARCH/*.c $d/$MUSL_ARCH/*.s $d/$MUSL_ARCH/*.S; do + [ -f "$f" ] && echo "$f" + done + done +) > "$OUT_DIR/arch.txt" + +# REPLACED: bases that have arch-specific overrides (drop them from +# BASE). KEEP = (BASE - REPLACED) ∪ ARCH. +awk -v ARCH="$MUSL_ARCH" ' + { + sub(/\.[^.]*$/, "") # strip extension + slot = "/" ARCH "/" + i = index($0, slot) + head = substr($0, 1, i - 1) + tail = substr($0, i + length(slot)) + print head "/" tail + } +' "$OUT_DIR/arch.txt" | sort -u > "$OUT_DIR/replaced.txt" + +# Filter base by removing stems that appear in replaced. +awk -v REPF="$OUT_DIR/replaced.txt" ' + BEGIN { while ((getline l < REPF) > 0) rep[l] = 1 } + { + stem = $0 + sub(/\.c$/, "", stem) + if (!(stem in rep)) print + } +' "$OUT_DIR/base.txt" > "$OUT_DIR/keep_base.txt" + +cat "$OUT_DIR/keep_base.txt" "$OUT_DIR/arch.txt" | sort -u > "$OUT_DIR/build-srcs.txt" + +n_src=$(wc -l < "$OUT_DIR/build-srcs.txt") +n_skip=$(grep -cv '^[[:space:]]*\(#\|$\)' "$MUSL_DIR/skip.txt" || true) +echo "[boot5-enumerate/$ARCH] keep=$n_src skip=$n_skip (calibrated)" + +# Record CRT mode (asm vs c) so boot5-gen-runscm picks the right +# crti/crtn source set without re-checking $MUSL_DIR. +if [ -f "$MUSL_DIR/crt/$MUSL_ARCH/crti.s" ]; then + echo asm > "$OUT_DIR/crt-mode" +else + echo c > "$OUT_DIR/crt-mode" +fi diff --git a/bootprep/boot5-gen-runscm.sh b/bootprep/boot5-gen-runscm.sh @@ -0,0 +1,156 @@ +#!/bin/sh +## boot5-gen-runscm.sh — emit run.scm driving boot5's musl + hello build +## inside the seed kernel. Mirrors boot/boot5.sh's podman-path script +## generation step-for-step: per-source `tcc -c`, per-arch CRT, archive, +## link hello. Source enumeration done by bootprep/boot5-enumerate.sh +## at prep time; this script consumes the resulting build-srcs.txt and +## emits one `(run "in/tcc" …)` form per TU. +## +## Usage: bootprep/boot5-gen-runscm.sh <arch> +## Reads build/<arch>/src/run/boot5/{build-srcs.txt, crt-mode} +## Writes build/<arch>/src/run/boot5.scm +## +## Conventions (cwd-relative; resolves to / under seed init, /work under +## podman bind-mount): +## musl tree in/musl/<rel-path> (read-only; canonical +## tree from prep-src/ +## prep-musl) +## pre-gen hdrs in/musl/obj/include/bits/{alltypes,syscall}.h, +## in/musl/obj/src/internal/version.h +## .o outputs out/obj/musl/<src-with-.o> (rw; pre-mkdir'd by host) +## tcc binary in/tcc (input) +## libtcc1.a in/libtcc1.a (input) +## stdarg bridge in/tcc-stdarg-bridge.h +## hello.c in/hello.c +## exports out/{libc.a,crt1.o,crti.o,crtn.o,hello} +## (flat at out/ root so runscm_export pulls by basename) + +set -eu +[ "$#" -eq 1 ] || { echo "usage: $0 <arch>" >&2; exit 2; } +ARCH=$1 +case "$ARCH" in + aarch64) MUSL_ARCH=aarch64 ;; + amd64) MUSL_ARCH=x86_64 ;; + riscv64) MUSL_ARCH=riscv64 ;; + *) echo "boot5-gen: unsupported arch '$ARCH'" >&2; exit 2 ;; +esac + +RUN_DIR=build/$ARCH/src/run +SRCS=$RUN_DIR/boot5/build-srcs.txt +CRT_MODE=$(cat "$RUN_DIR/boot5/crt-mode") +OUT=$RUN_DIR/boot5.scm +[ -e "$SRCS" ] || { echo "missing $SRCS (run bootprep/boot5-enumerate.sh $ARCH)" >&2; exit 1; } +mkdir -p "$(dirname "$OUT")" + +CIN=in/musl +COUT=out/obj/musl + +# Mirrors boot5.sh's CFLAGS_BASE exactly; the only difference is that +# every per-arg token is quoted as its own scheme bytevector. The leading +# "in/tcc" is the spawned binary; everything after is its argv. +CFLAGS_BASE_QUOTED='"-std=c99" "-nostdinc" "-ffreestanding" "-fno-strict-aliasing" "-D_XOPEN_SOURCE=700"' +CFLAGS_BASE_QUOTED="$CFLAGS_BASE_QUOTED \"-I$CIN/arch/$MUSL_ARCH\" \"-I$CIN/arch/generic\" \"-I$CIN/obj/src/internal\" \"-I$CIN/src/include\" \"-I$CIN/src/internal\" \"-I$CIN/obj/include\" \"-I$CIN/include\"" +CFLAGS_BASE_QUOTED="$CFLAGS_BASE_QUOTED \"-O2\" \"-fomit-frame-pointer\" \"-Werror=implicit-function-declaration\" \"-Werror=implicit-int\" \"-Werror=pointer-sign\" \"-Werror=pointer-arith\"" +CFLAGS_C_QUOTED="$CFLAGS_BASE_QUOTED \"-include\" \"in/tcc-stdarg-bridge.h\"" +CFLAGS_ASM_QUOTED="$CFLAGS_BASE_QUOTED" +CRTFLAGS_C_QUOTED="$CFLAGS_C_QUOTED \"-fno-stack-protector\" \"-DCRT\"" +CRTFLAGS_ASM_QUOTED="$CFLAGS_ASM_QUOTED \"-fno-stack-protector\" \"-DCRT\"" + +# tcc 0.9.26's riscv64-link.c default ELF_START_ADDR=0x10000 sits below +# the seed kernel's USER_VA_LO (0x200000); land riscv64 user binaries +# in the same 0x600000 window the rest of the chain uses. amd64 +# (0x400000) and aarch64 (0x400000) defaults already fit the window. +case "$MUSL_ARCH" in + riscv64) LINK_TTEXT='"-Wl,-Ttext=0x600000"' ;; + *) LINK_TTEXT= ;; +esac + +{ +cat <<'PROLOGUE' +;; boot5 run.scm — drive musl-1.2.5 (~500 TUs) + hello. +;; Generated by bootprep/boot5-gen-runscm.sh; consumed by both DRIVER=podman +;; (cwd=/work bind mount) and DRIVER=seed (cwd=/, cpio rootfs). The musl +;; source tree is staged read-only at in/tmp/musl-1.2.5/...; per-source .o +;; outputs go to out/obj/musl-1.2.5/...; final artefacts (libc.a, crt1.o, +;; crti.o, crtn.o, hello) land at flat out/ paths so runscm_export can +;; pull them by basename. + +(define (must r tag) + (if (and (car r) (= 0 (cdr r))) + r + (begin + (write-string stderr "boot5: step failed: ") + (write-string stderr tag) + (write-string stderr "\n") + (exit 1)))) + +(write-string stdout "boot5: stage A (compile sources)\n") +PROLOGUE + +# Stage A: per-source compile. Each line of build-srcs.txt is a path +# relative to musl-1.2.5/; choose flags by extension. +awk -v CFLAGS_C="$CFLAGS_C_QUOTED" -v CFLAGS_ASM="$CFLAGS_ASM_QUOTED" -v CIN="$CIN" -v COUT="$COUT" ' +{ + src = $0 + obj = src + sub(/\.[^.]*$/, ".o", obj) + if (src ~ /\.c$/) flags = CFLAGS_C + else if (src ~ /\.[sS]$/) flags = CFLAGS_ASM + else flags = CFLAGS_C + printf "(must (run \"in/tcc\" %s \"-c\" \"%s/%s\" \"-o\" \"%s/%s\") \"%s\")\n", \ + flags, CIN, src, COUT, obj, src +}' "$SRCS" + +cat <<EOF + +(write-string stdout "boot5: stage B (CRT)\n") +;; Position-independent + non-PIC CRT helpers. -fPIC objects are needed +;; for shared-binding tools, even though our hello is fully static. +(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-fPIC" "-c" "$CIN/crt/Scrt1.c" "-o" "$COUT/crt/Scrt1.o") "Scrt1.o") +(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-c" "$CIN/crt/crt1.c" "-o" "$COUT/crt/crt1.o") "crt1.o") +(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-fPIC" "-c" "$CIN/crt/rcrt1.c" "-o" "$COUT/crt/rcrt1.o") "rcrt1.o") +EOF + +if [ "$CRT_MODE" = asm ]; then + cat <<EOF +(must (run "in/tcc" $CRTFLAGS_ASM_QUOTED "-c" "$CIN/crt/$MUSL_ARCH/crti.s" "-o" "$COUT/crt/crti.o") "crti.o") +(must (run "in/tcc" $CRTFLAGS_ASM_QUOTED "-c" "$CIN/crt/$MUSL_ARCH/crtn.s" "-o" "$COUT/crt/crtn.o") "crtn.o") +EOF +else + cat <<EOF +(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-c" "$CIN/crt/crti.c" "-o" "$COUT/crt/crti.o") "crti.o") +(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-c" "$CIN/crt/crtn.c" "-o" "$COUT/crt/crtn.o") "crtn.o") +EOF +fi + +# Stage C: archive libc.a. tcc -ar accepts many obj args; assemble the +# full list inline. The list is enormous (~1500 paths × ~40 chars = +# ~60 KB on a single line) but the prelude reader handles it fine. +{ + printf '\n(write-string stdout "boot5: stage C (libc.a)\\n")\n' + printf '(must (run "in/tcc" "-ar" "rcs" "out/libc.a"' + awk -v COUT="$COUT" '{ + obj = $0 + sub(/\.[^.]*$/, ".o", obj) + printf " \"%s/%s\"", COUT, obj + }' "$SRCS" + printf ') "libc.a")\n' +} + +cat <<EOF + +;; Publish CRT objects at flat out/ paths so runscm_export can pull them. +(must (run "in/catm" "out/crt1.o" "$COUT/crt/crt1.o") "crt1.o publish") +(must (run "in/catm" "out/crti.o" "$COUT/crt/crti.o") "crti.o publish") +(must (run "in/catm" "out/crtn.o" "$COUT/crt/crtn.o") "crtn.o publish") + +(write-string stdout "boot5: stage D (link hello)\n") +;; -Lout pulls libc.a (just built); -Lin pulls libtcc1.a (input). +(must (run "in/tcc" "-static" "-nostdinc" "-nostdlib" "-include" "in/tcc-stdarg-bridge.h" $LINK_TTEXT + "-I$CIN/include" "-I$CIN/arch/$MUSL_ARCH" "-I$CIN/arch/generic" "-I$CIN/obj/include" + "out/crt1.o" "in/hello.c" "-Lout" "-lc" "-Lin" "-ltcc1" "-Lout" "-lc" "-o" "out/hello") "link hello") + +(write-string stdout "boot5: ALL-OK\n") +(exit 0) +EOF +} > "$OUT" diff --git a/bootprep/boot6-gen-runscm.sh b/bootprep/boot6-gen-runscm.sh @@ -0,0 +1,103 @@ +#!/bin/sh +## boot6-gen-runscm.sh — emit run.scm driving tcc3 to build seed-kernel. +## +## tcc3 -c each translation unit (kernel.S, kernel.c, mem.c) → .o, then +## tcc3 links + emits a flat arm64 boot Image directly (no `ld -T +## kernel.lds`, no objcopy). The link line is three tcc flags: +## +## -nostdlib -static freestanding link, no startfiles +## -Wl,-Ttext=0x40080000 base address (arm64 RAM_BASE + +## text_offset; QEMU's `-kernel` loads +## us here) +## -Wl,--oformat=binary flat-bytes output (objcopy -O binary +## equivalent). Required because QEMU's +## `-kernel <ELF>` path doesn't run the +## arm64 boot wrapper that puts DTB in +## x0 — only the flat-Image path does. +## Detection is by `ARM\x64` magic at +## file offset 0x38, which is in +## kernel.S's Image header at the top of +## `.text`. +## +## Everything else the kernel needs from the linker is supplied by +## conventions tcc3 already honors: +## +## _start hard-coded entry symbol; kernel.S's arm64 Image +## header is named `_start` (not `_head`) so this works +## out of the box. +## __bss_start auto-defined at the start of merged .bss via the +## `bss-start-symbol` simple-patch. +## _end auto-defined by stock tcc at the end of merged .bss +## (tcc_add_linker_symbols). kernel.S uses it to +## bracket the bss-zero loop and to compute the +## Image-header `image_size` field; kernel.c uses it +## to place the kernel heap above the loaded image. +## +## Usage: bootprep/boot6-gen-runscm.sh <arch> +## Writes build/<arch>/src/run/boot6.scm. + +set -eu +[ "$#" -eq 1 ] || { echo "usage: $0 <arch>" >&2; exit 2; } +ARCH=$1 +OUT=build/$ARCH/src/run/boot6.scm +mkdir -p "$(dirname "$OUT")" + +# Per-arch link parameters. aarch64 alone needs --oformat=binary because +# QEMU's `-kernel <ELF>` path skips the arm64 boot wrapper that puts DTB +# in x0 — only the flat-Image path honors it (detected via the `ARM\x64` +# magic at file offset 0x38 in kernel.S's Image header). amd64/riscv64 +# stay as ELF: QEMU's `-kernel` path on those arches consumes ELF +# directly (PVH note for amd64, OpenSBI for riscv64). +case "$ARCH" in + aarch64) TTEXT=0x40080000; OUT_FILE=Image; LINK_OFORMAT='"-Wl,--oformat=binary"' ;; + amd64) TTEXT=0x40000000; OUT_FILE=kernel.elf; LINK_OFORMAT= ;; + riscv64) TTEXT=0x80200000; OUT_FILE=kernel.elf; LINK_OFORMAT= ;; + *) echo "boot6-gen: unsupported arch '$ARCH'" >&2; exit 2 ;; +esac + +# Kernel CFLAGS — freestanding, static, no host startfiles. We omit gcc's +# -mgeneral-regs-only (no tcc equivalent); kernel.S enables CPACR_EL1.FPEN +# in stext before the first `bl kmain`, so tcc's SIMD callee-saves in +# function prologues don't trap. +KCFLAGS='"-nostdlib" "-ffreestanding" "-static"' + +cat > "$OUT" <<EOF +;; boot6 run.scm — build seed-kernel ELF with tcc3. +;; Generated by bootprep/boot6-gen-runscm.sh; reads use in/, writes out/. + +(define (must r tag) + (if (and (car r) (= 0 (cdr r))) + r + (begin + (write-string stderr "boot6: step failed: ") + (write-string stderr tag) + (write-string stderr "\n") + (exit 1)))) + +(write-string stdout "boot6: tcc3 -c kernel.S\n") +(must (run "in/tcc3" $KCFLAGS "-c" "-o" "out/kernel-asm.o" "in/kernel.S") + "kernel.S -> kernel-asm.o") + +(write-string stdout "boot6: tcc3 -c kernel.c\n") +(must (run "in/tcc3" $KCFLAGS "-Iin" "-c" "-o" "out/kernel.o" "in/kernel.c") + "kernel.c -> kernel.o") + +(write-string stdout "boot6: tcc3 -c mmu.c\n") +(must (run "in/tcc3" $KCFLAGS "-Iin" "-c" "-o" "out/mmu.o" "in/mmu.c") + "mmu.c -> mmu.o") + +(write-string stdout "boot6: tcc3 -c mem.c\n") +(must (run "in/tcc3" $KCFLAGS "-c" "-o" "out/mem.o" "in/mem.c") + "mem.c -> mem.o") + +(write-string stdout "boot6: tcc3 link $OUT_FILE\n") +(must (run "in/tcc3" "-nostdlib" "-static" + "-Wl,-Ttext=$TTEXT" + $LINK_OFORMAT + "-o" "out/$OUT_FILE" + "out/kernel-asm.o" "out/kernel.o" "out/mmu.o" "out/mem.o") + "link $OUT_FILE") + +(write-string stdout "boot6: ALL-OK\n") +(exit 0) +EOF diff --git a/bootprep/prep-musl.sh b/bootprep/prep-musl.sh @@ -71,3 +71,10 @@ fi n_remaining=$(find "$DST_MUSL" -type f | wc -l | tr -d ' ') echo "$TAG OK filtered=$n_skip remaining=$n_remaining files in $DST_MUSL" + +# ── (3) enumerate musl sources + generate boot5 run.scm ─────────────── +# The filtered tree is now stable; emit the build-srcs + crt-mode that +# boot5-gen-runscm consumes, then emit boot5.scm itself. boot5.sh just +# reads the result. +bootprep/boot5-enumerate.sh "$ARCH" +bootprep/boot5-gen-runscm.sh "$ARCH" diff --git a/bootprep/prep-src.sh b/bootprep/prep-src.sh @@ -31,6 +31,12 @@ ## skip filter on top. ## kernel/ seed-kernel sources for this arch ## test-fixtures/ boot-hello.c smoke binary +## run/ run.scm files driving each bootN stage +## boot3.scm static (copied from bootprep/assets/) +## boot4.scm generated by bootprep/boot4-gen-runscm.sh +## boot5.scm generated by bootprep/boot5-gen-runscm.sh +## (after prep-musl.sh) +## boot6.scm generated by bootprep/boot6-gen-runscm.sh ## ## A0 is split: prep-src.sh runs before boot0 and produces everything ## that doesn't need a working compiler. prep-musl.sh runs after boot4 @@ -105,7 +111,7 @@ cp tcc/cc/mem.c "$DST_SRC/tcc/cc/mem.c" # Smoke binary linked by boot4 + boot5. mkdir -p "$DST_SRC/test-fixtures" -cp scripts/boot-hello.c "$DST_SRC/test-fixtures/boot-hello.c" +cp bootprep/assets/boot-hello.c "$DST_SRC/test-fixtures/boot-hello.c" # ── (3) seed-kernel sources for this arch ───────────────────────────── mkdir -p "$DST_SRC/kernel/arch/$ARCH" "$DST_SRC/kernel/user" @@ -191,6 +197,15 @@ if [ -e "$SKIP_COMMITTED" ]; then cp "$SKIP_COMMITTED" "$DST_SRC/musl/skip.txt" fi +# ── (7) run.scm files ───────────────────────────────────────────────── +# Static run.scm (boot3) copied verbatim; boot4/boot6 generated here. +# boot5.scm needs the post-prep-musl tree, so it's generated by +# prep-musl.sh. +mkdir -p "$DST/run" +cp bootprep/assets/boot3-run.scm "$DST/run/boot3.scm" +bootprep/boot4-gen-runscm.sh "$ARCH" +bootprep/boot6-gen-runscm.sh "$ARCH" + # ── summary ─────────────────────────────────────────────────────────── n_files=$(find "$DST" -type f | wc -l | tr -d ' ') echo "$TAG OK -> $DST ($n_files files)" diff --git a/docs/MUSL.md b/docs/MUSL.md @@ -37,7 +37,7 @@ boot/boot5.sh <amd64|aarch64|riscv64> | `vendor/musl/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/musl/skip-$ARCH.txt` | per-arch calibration list — sources tcc 0.9.26 cannot compile, produced by `bootprep/boot5-calibrate.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 `bootprep/stage1-flatten.sh`) | -| `scripts/boot-hello.c` | smoke-test source (shared with boot4) | +| `bootprep/assets/boot-hello.c` | smoke-test source (shared with boot4) | Architecture mapping: @@ -174,6 +174,6 @@ hello from tcc-built libc; argc=4 strdup: works, strlen: 5 ``` -(The same `hello` source, `scripts/boot-hello.c`, is also linked and +(The same `hello` source, `bootprep/assets/boot-hello.c`, is also linked and run by boot4 against the mes-libc closure — proving both libc closures are exec-correct under their respective build systems.) diff --git a/scripts/boot4-gen-runscm.sh b/scripts/boot4-gen-runscm.sh @@ -1,142 +0,0 @@ -#!/bin/sh -## boot4-gen-runscm.sh — emit run.scm driving boot4's tcc0→tcc1→tcc2→tcc3 -## chain inside the seed kernel. Mirrors boot/boot4.sh's per-stage shell -## emission; per-arch values resolved on the host so the .scm body is -## straight-line (run …) calls. -## -## Reads use in/<name>; writes (intermediates and exports) use out/<name>. -## -## Usage: boot4-gen-runscm.sh <arch> <out.scm> - -set -eu -[ "$#" -eq 2 ] || { echo "usage: $0 <arch> <out.scm>" >&2; exit 2; } -ARCH=$1; OUT=$2 - -case "$ARCH" in - aarch64) LIB_HELPER_SRC=lib-arm64.c; LIB_HELPER_OBJ=lib-arm64.o - LIB_HELPER_DEFS='"-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) LIB_HELPER_SRC=va_list.c; LIB_HELPER_OBJ=va_list.o - LIB_HELPER_DEFS='"-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) LIB_HELPER_SRC=lib-arm64.c; LIB_HELPER_OBJ=lib-arm64.o - LIB_HELPER_DEFS='"-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="" ;; - *) echo "boot4-gen: unknown arch $ARCH" >&2; exit 2 ;; -esac - -# Per-arch link base for user binaries. tcc 0.9.26's riscv64-link.c -# defaults to ELF_START_ADDR=0x10000, which lives below the seed -# kernel's USER_VA_LO (0x200000). amd64 (0x400000) and aarch64 -# (0x400000) defaults already sit inside the user window, so we leave -# them alone. Everywhere else in the chain (M0/hex2pp -B, boot6 -# -Wl,-Ttext) we link riscv64 user binaries at 0x600000; do the same -# here so tcc-built outputs are loadable inside the seed kernel. -case "$ARCH" in - riscv64) LINK_TTEXT='"-Wl,-Ttext=0x600000"' ;; - *) LINK_TTEXT= ;; -esac - -# emit_helpers — cc reads .S/.c sources from in/, writes .o to out/. -# cc_path is the cwd-relative path to the spawned compiler binary (in/tcc0 -# for round B; out/tcc1, out/tcc2 in later rounds). -emit_helpers() { - cc_path=$1; tag=$2 - cat <<EOF -(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/start.o" "in/start.S") "$tag start.o") -(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/sys_stubs.o" "in/sys_stubs.S") "$tag sys_stubs.o") -(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/mem.o" "in/mem.c") "$tag mem.o") -(must (run "$cc_path" "-nostdlib" "-c" "-o" "out/libc.o" "in/libc.flat.c") "$tag libc.o") -(must (run "$cc_path" "-nostdlib" $LIB_HELPER_DEFS "-c" "-o" "out/$LIB_HELPER_OBJ" "in/$LIB_HELPER_SRC") "$tag $LIB_HELPER_OBJ") -EOF -} - -# emit_archive — uses prefix to namespace output object names per stage. -# pfx="s2-"/"s3-" for stage2/3. The .o objects archived into libtcc1.a -# keep their bare basenames (lib-arm64.o, …) — tcc -ar stores basenames -# only, so this matches podman's archive members exactly. They overwrite -# the stage's helper-named .o files; nothing post-archive in the same -# stage reads them as standalone .o. -emit_archive() { - cc_path=$1; tag=$2; pfx=$3 - echo "(must (run \"in/catm\" \"out/${pfx}crt1.o\" \"out/start.o\") \"copy crt1.o $pfx\")" - echo "(must (run \"$cc_path\" \"-ar\" \"rcs\" \"out/${pfx}libc.a\" \"out/sys_stubs.o\" \"out/mem.o\" \"out/libc.o\") \"$tag ${pfx}libc.a\")" - libtcc1_objs="" - for src in $LIBTCC1_C_SRCS; do - obj=${src%.c}.o - echo "(must (run \"$cc_path\" \"-nostdlib\" $LIBTCC1_C_DEFS \"-c\" \"-o\" \"out/${obj}\" \"in/$src\") \"$tag lt ${obj}\")" - libtcc1_objs="$libtcc1_objs \"out/${obj}\"" - done - for src in $LIBTCC1_ASM_SRCS; do - obj=${src%.S}.o - echo "(must (run \"$cc_path\" \"-nostdlib\" \"-c\" \"-o\" \"out/${obj}\" \"in/$src\") \"$tag lt ${obj}\")" - libtcc1_objs="$libtcc1_objs \"out/${obj}\"" - done - echo "(must (run \"$cc_path\" \"-ar\" \"rcs\" \"out/${pfx}libtcc1.a\"$libtcc1_objs) \"$tag ${pfx}libtcc1.a\")" -} - -emit_link_tcc() { - cc_path=$1; tag=$2; pfx=$3; out=$4 - echo "(must (run \"$cc_path\" \"-nostdlib\" $LINK_TTEXT \"out/${pfx}crt1.o\" \"in/tcc.flat.c\" \"out/${pfx}libc.a\" \"out/${pfx}libtcc1.a\" \"out/${pfx}libc.a\" \"-o\" \"out/$out\") \"$tag -> $out\")" -} - -{ -cat <<'PROLOGUE' -;; boot4 run.scm — drive tcc0 -> tcc1 -> tcc2 -> tcc3 inside seed kernel. -;; Generated by scripts/boot4-gen-runscm.sh; mirrors boot/boot4.sh's -;; podman path stage-for-stage. Reads use in/; writes (intermediates and -;; exports) use out/. tcc0 is staged as in/tcc0; tcc1/tcc2/tcc3 are -;; produced and exported under out/. - -(define (must r tag) - (if (and (car r) (= 0 (cdr r))) - r - (begin - (write-string stderr "boot4: step failed: ") - (write-string stderr tag) - (write-string stderr "\n") - (exit 1)))) - -(write-string stdout "boot4: stage B (tcc0 helpers)\n") -PROLOGUE - -# Stage B: tcc0 builds helper objects (no archive). -emit_helpers in/tcc0 tcc0 - -cat <<EOF - -(write-string stdout "boot4: stage C (tcc0 -> tcc1)\n") -(must (run "in/tcc0" "-nostdlib" $LINK_TTEXT "out/start.o" "out/sys_stubs.o" "out/mem.o" "out/libc.o" "out/$LIB_HELPER_OBJ" "in/tcc.flat.c" "-o" "out/tcc1") "tcc0 -> tcc1") - -(write-string stdout "boot4: stage D (tcc1 -> tcc2)\n") -EOF - -# Stage D: tcc1 rebuilds helpers + archive, links tcc2. -emit_helpers out/tcc1 tcc1 -emit_archive out/tcc1 tcc1 "s2-" -emit_link_tcc out/tcc1 tcc1 "s2-" tcc2 - -cat <<EOF - -(write-string stdout "boot4: stage E (tcc2 -> tcc3)\n") -EOF - -# Stage E: tcc2 rebuilds helpers + archive, links tcc3. -emit_helpers out/tcc2 tcc2 -emit_archive out/tcc2 tcc2 "s3-" -emit_link_tcc out/tcc2 tcc2 "s3-" tcc3 - -cat <<EOF - -(write-string stdout "boot4: linking hello\n") -(must (run "out/tcc2" "-nostdlib" $LINK_TTEXT "out/s3-crt1.o" "in/hello.c" "out/s3-libc.a" "out/s3-libtcc1.a" "out/s3-libc.a" "-o" "out/hello") "tcc2 -> hello") -(write-string stdout "boot4: ALL-OK\n") -(exit 0) -EOF -} > "$OUT" diff --git a/scripts/boot5-gen-runscm.sh b/scripts/boot5-gen-runscm.sh @@ -1,149 +0,0 @@ -#!/bin/sh -## boot5-gen-runscm.sh — emit run.scm driving boot5's musl + hello build -## inside the seed kernel. Mirrors boot/boot5.sh's podman-path script -## generation step-for-step: per-source `tcc -c`, per-arch CRT, archive, -## link hello. Source enumeration done by boot5.sh; this script consumes -## the resulting build-srcs.txt and emits one `(run "in/tcc" …)` form per TU. -## -## Usage: -## boot5-gen-runscm.sh <musl-arch> <stage-host-dir> <out.scm> -## -## stage-host-dir is the boot5 _host/ directory containing: -## build-srcs.txt one path per line, relative to musl-1.2.5/ -## crt-mode "asm" or "c" — picked by boot5.sh from $MUSL_DIR -## -## Conventions (cwd-relative; resolves to / under seed init, /work under -## podman bind-mount): -## musl tree in/musl/<rel-path> (read-only; canonical -## tree from prep-src/ -## prep-musl) -## pre-gen hdrs in/musl/obj/include/bits/{alltypes,syscall}.h, -## in/musl/obj/src/internal/version.h -## .o outputs out/obj/musl/<src-with-.o> (rw; pre-mkdir'd by host) -## tcc binary in/tcc (input) -## libtcc1.a in/libtcc1.a (input) -## stdarg bridge in/tcc-stdarg-bridge.h -## hello.c in/hello.c -## exports out/{libc.a,crt1.o,crti.o,crtn.o,hello} -## (flat at out/ root so runscm_export pulls by basename) - -set -eu -[ "$#" -eq 3 ] || { echo "usage: $0 <musl-arch> <stage-host-dir> <out.scm>" >&2; exit 2; } - -MUSL_ARCH=$1; STAGE_HOST=$2; OUT=$3 -SRCS=$STAGE_HOST/build-srcs.txt -CRT_MODE=$(cat "$STAGE_HOST/crt-mode") -[ -e "$SRCS" ] || { echo "missing $SRCS" >&2; exit 1; } - -CIN=in/musl -COUT=out/obj/musl - -# Mirrors boot5.sh's CFLAGS_BASE exactly; the only difference is that -# every per-arg token is quoted as its own scheme bytevector. The leading -# "in/tcc" is the spawned binary; everything after is its argv. -CFLAGS_BASE_QUOTED='"-std=c99" "-nostdinc" "-ffreestanding" "-fno-strict-aliasing" "-D_XOPEN_SOURCE=700"' -CFLAGS_BASE_QUOTED="$CFLAGS_BASE_QUOTED \"-I$CIN/arch/$MUSL_ARCH\" \"-I$CIN/arch/generic\" \"-I$CIN/obj/src/internal\" \"-I$CIN/src/include\" \"-I$CIN/src/internal\" \"-I$CIN/obj/include\" \"-I$CIN/include\"" -CFLAGS_BASE_QUOTED="$CFLAGS_BASE_QUOTED \"-O2\" \"-fomit-frame-pointer\" \"-Werror=implicit-function-declaration\" \"-Werror=implicit-int\" \"-Werror=pointer-sign\" \"-Werror=pointer-arith\"" -CFLAGS_C_QUOTED="$CFLAGS_BASE_QUOTED \"-include\" \"in/tcc-stdarg-bridge.h\"" -CFLAGS_ASM_QUOTED="$CFLAGS_BASE_QUOTED" -CRTFLAGS_C_QUOTED="$CFLAGS_C_QUOTED \"-fno-stack-protector\" \"-DCRT\"" -CRTFLAGS_ASM_QUOTED="$CFLAGS_ASM_QUOTED \"-fno-stack-protector\" \"-DCRT\"" - -# tcc 0.9.26's riscv64-link.c default ELF_START_ADDR=0x10000 sits below -# the seed kernel's USER_VA_LO (0x200000); land riscv64 user binaries -# in the same 0x600000 window the rest of the chain uses. amd64 -# (0x400000) and aarch64 (0x400000) defaults already fit the window. -case "$MUSL_ARCH" in - riscv64) LINK_TTEXT='"-Wl,-Ttext=0x600000"' ;; - *) LINK_TTEXT= ;; -esac - -{ -cat <<'PROLOGUE' -;; boot5 run.scm — drive musl-1.2.5 (~500 TUs) + hello. -;; Generated by scripts/boot5-gen-runscm.sh; consumed by both DRIVER=podman -;; (cwd=/work bind mount) and DRIVER=seed (cwd=/, cpio rootfs). The musl -;; source tree is staged read-only at in/tmp/musl-1.2.5/...; per-source .o -;; outputs go to out/obj/musl-1.2.5/...; final artefacts (libc.a, crt1.o, -;; crti.o, crtn.o, hello) land at flat out/ paths so runscm_export can -;; pull them by basename. - -(define (must r tag) - (if (and (car r) (= 0 (cdr r))) - r - (begin - (write-string stderr "boot5: step failed: ") - (write-string stderr tag) - (write-string stderr "\n") - (exit 1)))) - -(write-string stdout "boot5: stage A (compile sources)\n") -PROLOGUE - -# Stage A: per-source compile. Each line of build-srcs.txt is a path -# relative to musl-1.2.5/; choose flags by extension. -awk -v CFLAGS_C="$CFLAGS_C_QUOTED" -v CFLAGS_ASM="$CFLAGS_ASM_QUOTED" -v CIN="$CIN" -v COUT="$COUT" ' -{ - src = $0 - obj = src - sub(/\.[^.]*$/, ".o", obj) - if (src ~ /\.c$/) flags = CFLAGS_C - else if (src ~ /\.[sS]$/) flags = CFLAGS_ASM - else flags = CFLAGS_C - printf "(must (run \"in/tcc\" %s \"-c\" \"%s/%s\" \"-o\" \"%s/%s\") \"%s\")\n", \ - flags, CIN, src, COUT, obj, src -}' "$SRCS" - -cat <<EOF - -(write-string stdout "boot5: stage B (CRT)\n") -;; Position-independent + non-PIC CRT helpers. -fPIC objects are needed -;; for shared-binding tools, even though our hello is fully static. -(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-fPIC" "-c" "$CIN/crt/Scrt1.c" "-o" "$COUT/crt/Scrt1.o") "Scrt1.o") -(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-c" "$CIN/crt/crt1.c" "-o" "$COUT/crt/crt1.o") "crt1.o") -(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-fPIC" "-c" "$CIN/crt/rcrt1.c" "-o" "$COUT/crt/rcrt1.o") "rcrt1.o") -EOF - -if [ "$CRT_MODE" = asm ]; then - cat <<EOF -(must (run "in/tcc" $CRTFLAGS_ASM_QUOTED "-c" "$CIN/crt/$MUSL_ARCH/crti.s" "-o" "$COUT/crt/crti.o") "crti.o") -(must (run "in/tcc" $CRTFLAGS_ASM_QUOTED "-c" "$CIN/crt/$MUSL_ARCH/crtn.s" "-o" "$COUT/crt/crtn.o") "crtn.o") -EOF -else - cat <<EOF -(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-c" "$CIN/crt/crti.c" "-o" "$COUT/crt/crti.o") "crti.o") -(must (run "in/tcc" $CRTFLAGS_C_QUOTED "-c" "$CIN/crt/crtn.c" "-o" "$COUT/crt/crtn.o") "crtn.o") -EOF -fi - -# Stage C: archive libc.a. tcc -ar accepts many obj args; assemble the -# full list inline. The list is enormous (~1500 paths × ~40 chars = -# ~60 KB on a single line) but the prelude reader handles it fine. -{ - printf '\n(write-string stdout "boot5: stage C (libc.a)\\n")\n' - printf '(must (run "in/tcc" "-ar" "rcs" "out/libc.a"' - awk -v COUT="$COUT" '{ - obj = $0 - sub(/\.[^.]*$/, ".o", obj) - printf " \"%s/%s\"", COUT, obj - }' "$SRCS" - printf ') "libc.a")\n' -} - -cat <<EOF - -;; Publish CRT objects at flat out/ paths so runscm_export can pull them. -(must (run "in/catm" "out/crt1.o" "$COUT/crt/crt1.o") "crt1.o publish") -(must (run "in/catm" "out/crti.o" "$COUT/crt/crti.o") "crti.o publish") -(must (run "in/catm" "out/crtn.o" "$COUT/crt/crtn.o") "crtn.o publish") - -(write-string stdout "boot5: stage D (link hello)\n") -;; -Lout pulls libc.a (just built); -Lin pulls libtcc1.a (input). -(must (run "in/tcc" "-static" "-nostdinc" "-nostdlib" "-include" "in/tcc-stdarg-bridge.h" $LINK_TTEXT - "-I$CIN/include" "-I$CIN/arch/$MUSL_ARCH" "-I$CIN/arch/generic" "-I$CIN/obj/include" - "out/crt1.o" "in/hello.c" "-Lout" "-lc" "-Lin" "-ltcc1" "-Lout" "-lc" "-o" "out/hello") "link hello") - -(write-string stdout "boot5: ALL-OK\n") -(exit 0) -EOF -} > "$OUT" diff --git a/scripts/boot6-gen-runscm.sh b/scripts/boot6-gen-runscm.sh @@ -1,100 +0,0 @@ -#!/bin/sh -## boot6-gen-runscm.sh — emit run.scm driving tcc3 to build seed-kernel. -## -## tcc3 -c each translation unit (kernel.S, kernel.c, mem.c) → .o, then -## tcc3 links + emits a flat arm64 boot Image directly (no `ld -T -## kernel.lds`, no objcopy). The link line is three tcc flags: -## -## -nostdlib -static freestanding link, no startfiles -## -Wl,-Ttext=0x40080000 base address (arm64 RAM_BASE + -## text_offset; QEMU's `-kernel` loads -## us here) -## -Wl,--oformat=binary flat-bytes output (objcopy -O binary -## equivalent). Required because QEMU's -## `-kernel <ELF>` path doesn't run the -## arm64 boot wrapper that puts DTB in -## x0 — only the flat-Image path does. -## Detection is by `ARM\x64` magic at -## file offset 0x38, which is in -## kernel.S's Image header at the top of -## `.text`. -## -## Everything else the kernel needs from the linker is supplied by -## conventions tcc3 already honors: -## -## _start hard-coded entry symbol; kernel.S's arm64 Image -## header is named `_start` (not `_head`) so this works -## out of the box. -## __bss_start auto-defined at the start of merged .bss via the -## `bss-start-symbol` simple-patch. -## _end auto-defined by stock tcc at the end of merged .bss -## (tcc_add_linker_symbols). kernel.S uses it to -## bracket the bss-zero loop and to compute the -## Image-header `image_size` field; kernel.c uses it -## to place the kernel heap above the loaded image. -## -## Usage: boot6-gen-runscm.sh <arch> <out.scm> - -set -eu -[ "$#" -eq 2 ] || { echo "usage: $0 <arch> <out.scm>" >&2; exit 2; } -ARCH=$1; OUT=$2 - -# Per-arch link parameters. aarch64 alone needs --oformat=binary because -# QEMU's `-kernel <ELF>` path skips the arm64 boot wrapper that puts DTB -# in x0 — only the flat-Image path honors it (detected via the `ARM\x64` -# magic at file offset 0x38 in kernel.S's Image header). amd64/riscv64 -# stay as ELF: QEMU's `-kernel` path on those arches consumes ELF -# directly (PVH note for amd64, OpenSBI for riscv64). -case "$ARCH" in - aarch64) TTEXT=0x40080000; OUT_FILE=Image; LINK_OFORMAT='"-Wl,--oformat=binary"' ;; - amd64) TTEXT=0x40000000; OUT_FILE=kernel.elf; LINK_OFORMAT= ;; - riscv64) TTEXT=0x80200000; OUT_FILE=kernel.elf; LINK_OFORMAT= ;; - *) echo "boot6-gen: unsupported arch '$ARCH'" >&2; exit 2 ;; -esac - -# Kernel CFLAGS — freestanding, static, no host startfiles. We omit gcc's -# -mgeneral-regs-only (no tcc equivalent); kernel.S enables CPACR_EL1.FPEN -# in stext before the first `bl kmain`, so tcc's SIMD callee-saves in -# function prologues don't trap. -KCFLAGS='"-nostdlib" "-ffreestanding" "-static"' - -cat > "$OUT" <<EOF -;; boot6 run.scm — build seed-kernel ELF with tcc3. -;; Generated by scripts/boot6-gen-runscm.sh; reads use in/, writes out/. - -(define (must r tag) - (if (and (car r) (= 0 (cdr r))) - r - (begin - (write-string stderr "boot6: step failed: ") - (write-string stderr tag) - (write-string stderr "\n") - (exit 1)))) - -(write-string stdout "boot6: tcc3 -c kernel.S\n") -(must (run "in/tcc3" $KCFLAGS "-c" "-o" "out/kernel-asm.o" "in/kernel.S") - "kernel.S -> kernel-asm.o") - -(write-string stdout "boot6: tcc3 -c kernel.c\n") -(must (run "in/tcc3" $KCFLAGS "-Iin" "-c" "-o" "out/kernel.o" "in/kernel.c") - "kernel.c -> kernel.o") - -(write-string stdout "boot6: tcc3 -c mmu.c\n") -(must (run "in/tcc3" $KCFLAGS "-Iin" "-c" "-o" "out/mmu.o" "in/mmu.c") - "mmu.c -> mmu.o") - -(write-string stdout "boot6: tcc3 -c mem.c\n") -(must (run "in/tcc3" $KCFLAGS "-c" "-o" "out/mem.o" "in/mem.c") - "mem.c -> mem.o") - -(write-string stdout "boot6: tcc3 link $OUT_FILE\n") -(must (run "in/tcc3" "-nostdlib" "-static" - "-Wl,-Ttext=$TTEXT" - $LINK_OFORMAT - "-o" "out/$OUT_FILE" - "out/kernel-asm.o" "out/kernel.o" "out/mmu.o" "out/mem.o") - "link $OUT_FILE") - -(write-string stdout "boot6: ALL-OK\n") -(exit 0) -EOF