kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

run.sh (70355B)


      1 #!/bin/sh
      2 # Driver-level behavior checks for the kit multitool CLI.
      3 
      4 set -u
      5 
      6 script_dir=$(cd "$(dirname "$0")" && pwd)
      7 repo_root=$(cd "$script_dir/../.." && pwd)
      8 
      9 KIT="${KIT:-$repo_root/build/kit}"
     10 
     11 if [ ! -x "$KIT" ]; then
     12     echo "driver: kit binary not found at $KIT" >&2
     13     exit 2
     14 fi
     15 
     16 work=$(mktemp -d "${TMPDIR:-/tmp}/kit-driver-test.XXXXXX")
     17 trap 'rm -rf "$work"' EXIT
     18 
     19 # Type-K mode-P kit: ok/run_ok/run_fail/contains/same_file/is_executable/
     20 # assert_file_exists/check_mode, all recording through the unified kit_* counters
     21 # over $work. check_mode replaces the inline stat -f/-c permission probe; the
     22 # driver-specific scenarios that need shell control flow (umask, cd, stdin
     23 # piping, runtime auto-build via nm) stay inline but route verdicts through
     24 # ok/not_ok. Mode-P suites are SERIAL — fixtures under $work are shared/mutated.
     25 KIT_KIT_DIR="$repo_root/test/lib"
     26 . "$repo_root/test/lib/kit_sh_kit.sh"
     27 kit_report_init
     28 
     29 driver_now_ms() {
     30     perl -MTime::HiRes=time -e 'printf "%.0f\n", time() * 1000' 2>/dev/null ||
     31         printf '%s000\n' "$(date +%s 2>/dev/null || printf '0')"
     32 }
     33 
     34 driver_section_id=
     35 driver_section_start=0
     36 
     37 driver_slow_timing_enabled() {
     38     case "${KIT_TIER1_SLOW_TIMING:-0}" in
     39         1|yes|true|on) return 0 ;;
     40         *) return 1 ;;
     41     esac
     42 }
     43 
     44 driver_section_done() {
     45     [ -n "$driver_section_id" ] || return 0
     46     if ! driver_slow_timing_enabled; then
     47         driver_section_id=
     48         return 0
     49     fi
     50     driver_section_end=$(driver_now_ms)
     51     driver_section_ms=$((driver_section_end - driver_section_start))
     52     [ "$driver_section_ms" -lt 0 ] && driver_section_ms=0
     53     kit_time "$driver_section_id" "$driver_section_ms"
     54     driver_section_id=
     55 }
     56 
     57 driver_section() {
     58     driver_slow_timing_enabled || return 0
     59     driver_section_done
     60     driver_section_id=$1
     61     driver_section_start=$(driver_now_ms)
     62 }
     63 
     64 cat > "$work/main.c" <<'SRC'
     65 int main(void) { return 0; }
     66 int _start(void) { return 0; }
     67 SRC
     68 
     69 cat > "$work/other.c" <<'SRC'
     70 int other(void) { return 0; }
     71 SRC
     72 
     73 driver_section cc_ld_modes
     74 
     75 # ---- executable permission bits (cc/ld) ----
     76 if (umask 077; "$KIT" cc "$work/main.c" -o "$work/cc-exe") \
     77     > "$work/cc.out" 2> "$work/cc.err"; then
     78     check_mode "cc-executable-mode" "$work/cc-exe" 700
     79 else
     80     not_ok "cc-executable-mode" "$work/cc.err"
     81 fi
     82 
     83 rm -f "$work/a.out"
     84 if (cd "$work" && umask 077 && "$KIT" cc main.c) \
     85     > "$work/cc-link-default.out" 2> "$work/cc-link-default.err"; then
     86     if [ -f "$work/a.out" ]; then
     87         check_mode "cc-link-default-output" "$work/a.out" 700
     88     else
     89         echo "a.out not created" > "$work/cc-link-default.diag"
     90         not_ok "cc-link-default-output" "$work/cc-link-default.diag"
     91     fi
     92 else
     93     not_ok "cc-link-default-output" "$work/cc-link-default.err"
     94 fi
     95 
     96 if (umask 077; "$KIT" cc -c "$work/main.c" -o "$work/main.o") \
     97     > "$work/cc-c.out" 2> "$work/cc-c.err"; then
     98     : > "$work/ld-exe"
     99     chmod 0644 "$work/ld-exe"
    100     if (umask 077; "$KIT" ld "$work/main.o" -o "$work/ld-exe") \
    101         > "$work/ld.out" 2> "$work/ld.err"; then
    102         check_mode "ld-executable-mode" "$work/ld-exe" 700
    103     else
    104         not_ok "ld-executable-mode" "$work/ld.err"
    105     fi
    106 else
    107     not_ok "ld-executable-mode" "$work/cc-c.err"
    108 fi
    109 
    110 # ---- stdin piping: -x asm/s/asm-cpp/S → object ----
    111 for xlang in asm s asm-cpp S; do
    112     if printf '.globl asm_stdin\nasm_stdin:\n  ret\n' |
    113             "$KIT" cc -target x86_64-linux -x "$xlang" -c - \
    114                 -o "$work/stdin-$xlang.o" \
    115                 > "$work/stdin-$xlang.out" 2> "$work/stdin-$xlang.err" &&
    116        [ -f "$work/stdin-$xlang.o" ]; then
    117         ok "cc-stdin-x-$xlang"
    118     else
    119         not_ok "cc-stdin-x-$xlang" "$work/stdin-$xlang.err"
    120     fi
    121 done
    122 
    123 # ---- stdin piping: -x wasm/wat → emitted C ----
    124 for xlang in wasm wat; do
    125     if printf '(module (func (export "test_main") (result i32) i32.const 0))\n' |
    126             "$KIT" cc --emit=c -x "$xlang" - \
    127                 -o "$work/stdin-$xlang.c" \
    128                 > "$work/stdin-$xlang.out" 2> "$work/stdin-$xlang.err" &&
    129        [ -s "$work/stdin-$xlang.c" ]; then
    130         ok "cc-stdin-x-$xlang"
    131     else
    132         not_ok "cc-stdin-x-$xlang" "$work/stdin-$xlang.err"
    133     fi
    134 done
    135 
    136 # Portable-C accepts the public optimization spellings by normalizing them
    137 # before optimizer construction. Recompile every emitted translation unit to
    138 # prove the result is usable for both the native target and representative
    139 # cross-target ABI selections.
    140 for opt in 0 1 2; do
    141     out="$work/portable-native-O$opt.c"
    142     if "$KIT" cc "-O$opt" --emit=c "$work/main.c" -o "$out" \
    143             > "$work/portable-native-O$opt.out" \
    144             2> "$work/portable-native-O$opt.err" &&
    145        "$KIT" cc -c "$out" -o "$work/portable-native-O$opt.o" \
    146             >> "$work/portable-native-O$opt.out" \
    147             2>> "$work/portable-native-O$opt.err"; then
    148         ok "cc-portable-native-O$opt"
    149     else
    150         not_ok "cc-portable-native-O$opt" "$work/portable-native-O$opt.err"
    151     fi
    152 done
    153 for target in aarch64-none-elf x86_64-none-elf riscv64-none-elf; do
    154     for opt in 0 1 2; do
    155         tag=$(printf '%s' "$target" | tr - _)
    156         out="$work/portable-$tag-O$opt.c"
    157         if "$KIT" cc -target "$target" -ffreestanding "-O$opt" --emit=c \
    158                 "$work/main.c" -o "$out" \
    159                 > "$work/portable-$tag-O$opt.out" \
    160                 2> "$work/portable-$tag-O$opt.err" &&
    161            "$KIT" cc -target "$target" -ffreestanding -c "$out" \
    162                 -o "$work/portable-$tag-O$opt.o" \
    163                 >> "$work/portable-$tag-O$opt.out" \
    164                 2>> "$work/portable-$tag-O$opt.err"; then
    165             ok "cc-portable-$tag-O$opt"
    166         else
    167             not_ok "cc-portable-$tag-O$opt" "$work/portable-$tag-O$opt.err"
    168         fi
    169     done
    170 done
    171 
    172 # ---- multi-source dependency generation ----
    173 cat > "$work/dep common.h" <<'SRC'
    174 #define DEP_VALUE 19
    175 SRC
    176 cat > "$work/dep-a.c" <<'SRC'
    177 #include "dep common.h"
    178 int dep_a(void) { return DEP_VALUE; }
    179 SRC
    180 cat > "$work/dep-b.c" <<'SRC'
    181 #include "dep common.h"
    182 int dep_b(void) { return DEP_VALUE + 4; }
    183 SRC
    184 cat > "$work/dep-main.c" <<'SRC'
    185 int dep_a(void);
    186 int dep_b(void);
    187 int main(void) { return dep_a() + dep_b() == 42 ? 0 : 1; }
    188 SRC
    189 
    190 run_ok "cc-deps-mm-multi" "$KIT" cc -MM -I"$work" \
    191     "$work/dep-a.c" "$work/dep-b.c"
    192 contains "cc-deps-mm-first" "$work/cc-deps-mm-multi.out" "dep-a.o:"
    193 contains "cc-deps-mm-second" "$work/cc-deps-mm-multi.out" "dep-b.o:"
    194 contains "cc-deps-path-escaping" "$work/cc-deps-mm-multi.out" \
    195     "dep\\ common.h"
    196 dep_a_line=$(grep -n '^dep-a\.o:' "$work/cc-deps-mm-multi.out" | \
    197     sed -n '1s/:.*//p')
    198 dep_b_line=$(grep -n '^dep-b\.o:' "$work/cc-deps-mm-multi.out" | \
    199     sed -n '1s/:.*//p')
    200 if [ -n "$dep_a_line" ] && [ -n "$dep_b_line" ] && \
    201         [ "$dep_a_line" -lt "$dep_b_line" ]; then
    202     ok "cc-deps-input-order"
    203 else
    204     echo "dependency rules are not in source input order" > \
    205         "$work/cc-deps-input-order.diag"
    206     not_ok "cc-deps-input-order" "$work/cc-deps-input-order.diag"
    207 fi
    208 
    209 # A stdin source between file sources must stay in argv order even though the
    210 # driver's owning storage for file and memory sources is separate.
    211 if printf 'int dep_stdin(void) { return 0; }\n' | \
    212         "$KIT" cc -MM -I"$work" "$work/dep-a.c" - "$work/dep-b.c" \
    213             > "$work/cc-deps-stdin-order.out" \
    214             2> "$work/cc-deps-stdin-order.err"; then
    215     stdin_a=$(grep -n '^dep-a\.o:' "$work/cc-deps-stdin-order.out" | \
    216         sed -n '1s/:.*//p')
    217     stdin_mid=$(grep -n '^<stdin>\.o:' "$work/cc-deps-stdin-order.out" | \
    218         sed -n '1s/:.*//p')
    219     stdin_b=$(grep -n '^dep-b\.o:' "$work/cc-deps-stdin-order.out" | \
    220         sed -n '1s/:.*//p')
    221     if [ -n "$stdin_a" ] && [ -n "$stdin_mid" ] && [ -n "$stdin_b" ] && \
    222             [ "$stdin_a" -lt "$stdin_mid" ] && \
    223             [ "$stdin_mid" -lt "$stdin_b" ]; then
    224         ok "cc-deps-stdin-input-order"
    225     else
    226         not_ok "cc-deps-stdin-input-order" "$work/cc-deps-stdin-order.out"
    227     fi
    228 else
    229     not_ok "cc-deps-stdin-input-order" "$work/cc-deps-stdin-order.err"
    230 fi
    231 
    232 run_ok "cc-deps-mf-multi" "$KIT" cc -M -I"$work" \
    233     -MF "$work/all-deps.mk" "$work/dep-a.c" "$work/dep-b.c"
    234 contains "cc-deps-mf-first" "$work/all-deps.mk" "dep-a.o:"
    235 contains "cc-deps-mf-second" "$work/all-deps.mk" "dep-b.o:"
    236 if [ ! -s "$work/cc-deps-mf-multi.out" ]; then
    237     ok "cc-deps-mf-no-stdout"
    238 else
    239     not_ok "cc-deps-mf-no-stdout" "$work/cc-deps-mf-multi.out"
    240 fi
    241 
    242 # Explicit -MF is committed only after every source succeeds.
    243 printf 'keep-existing-dependency-file\n' > "$work/transaction.mk"
    244 run_fail "cc-deps-mf-transaction-failure" "$KIT" cc -M -I"$work" \
    245     -MF "$work/transaction.mk" "$work/dep-a.c" "$work/missing-dep.c"
    246 contains "cc-deps-mf-transaction-preserved" "$work/transaction.mk" \
    247     "keep-existing-dependency-file"
    248 
    249 run_ok "cc-deps-target-spellings" "$KIT" cc -MM -I"$work" \
    250     -MT raw-target -MT second-raw -MQ 'quoted target#$\\name' \
    251     "$work/dep-a.c" "$work/dep-b.c"
    252 contains "cc-deps-repeated-mt" "$work/cc-deps-target-spellings.out" \
    253     "raw-target second-raw"
    254 contains "cc-deps-mq-escaping" "$work/cc-deps-target-spellings.out" \
    255     'quoted\ target\#$$\\\\name'
    256 
    257 mkdir -p "$work/deps-compile"
    258 if (cd "$work/deps-compile" && "$KIT" cc -I"$work" -c -MD \
    259         "$work/dep-a.c" "$work/dep-b.c") \
    260         > "$work/cc-deps-md-compile.out" \
    261         2> "$work/cc-deps-md-compile.err" &&
    262    [ -s "$work/deps-compile/dep-a.o" ] &&
    263    [ -s "$work/deps-compile/dep-b.o" ] &&
    264    [ -s "$work/deps-compile/dep-a.d" ] &&
    265    [ -s "$work/deps-compile/dep-b.d" ]; then
    266     ok "cc-deps-md-compile-multi"
    267 else
    268     not_ok "cc-deps-md-compile-multi" "$work/cc-deps-md-compile.err"
    269 fi
    270 
    271 mkdir -p "$work/deps-compile-mf"
    272 if (cd "$work/deps-compile-mf" && "$KIT" cc -I"$work" -c -MMD \
    273         -MF "$work/compile-deps.mk" "$work/dep-a.c" "$work/dep-b.c") \
    274         > "$work/cc-deps-mmd-compile-mf.out" \
    275         2> "$work/cc-deps-mmd-compile-mf.err"; then
    276     ok "cc-deps-mmd-compile-mf"
    277 else
    278     not_ok "cc-deps-mmd-compile-mf" "$work/cc-deps-mmd-compile-mf.err"
    279 fi
    280 contains "cc-deps-mmd-compile-mf-first" "$work/compile-deps.mk" "dep-a.o:"
    281 contains "cc-deps-mmd-compile-mf-second" "$work/compile-deps.mk" "dep-b.o:"
    282 
    283 mkdir -p "$work/deps-link"
    284 if (cd "$work/deps-link" && "$KIT" cc -I"$work" -MD \
    285         "$work/dep-main.c" "$work/dep-a.c" "$work/dep-b.c" -o dep-app) \
    286         > "$work/cc-deps-md-link.out" 2> "$work/cc-deps-md-link.err" &&
    287    [ -x "$work/deps-link/dep-app" ] &&
    288    [ -s "$work/deps-link/dep-main.d" ] &&
    289    [ -s "$work/deps-link/dep-a.d" ] &&
    290    [ -s "$work/deps-link/dep-b.d" ]; then
    291     ok "cc-deps-md-compile-link"
    292 else
    293     not_ok "cc-deps-md-compile-link" "$work/cc-deps-md-link.err"
    294 fi
    295 
    296 # ---- ld -r partial link: output mode + relinkability ----
    297 cat > "$work/partial-main.c" <<'SRC'
    298 int foo(void);
    299 int _start(void) { return foo(); }
    300 SRC
    301 cat > "$work/partial-foo.c" <<'SRC'
    302 int foo(void) { return 0; }
    303 SRC
    304 
    305 if "$KIT" cc -target x86_64-linux -c "$work/partial-main.c" \
    306         -o "$work/partial-main.o" > "$work/partial-main.out" \
    307         2> "$work/partial-main.err" &&
    308    "$KIT" cc -target x86_64-linux -c "$work/partial-foo.c" \
    309         -o "$work/partial-foo.o" > "$work/partial-foo.out" \
    310         2> "$work/partial-foo.err"; then
    311     : > "$work/partial.o"
    312     chmod 0644 "$work/partial.o"
    313     if (umask 077; "$KIT" ld -r "$work/partial-main.o" \
    314             "$work/partial-foo.o" -o "$work/partial.o") \
    315         > "$work/ld-r.out" 2> "$work/ld-r.err"; then
    316         check_mode "ld-r-output-mode" "$work/partial.o" 644
    317         if "$KIT" ld "$work/partial.o" -o "$work/partial-exe" \
    318             > "$work/ld-r-final.out" 2> "$work/ld-r-final.err"; then
    319             ok "ld-r-final-link"
    320         else
    321             not_ok "ld-r-final-link" "$work/ld-r-final.err"
    322         fi
    323     else
    324         not_ok "ld-r-output-mode" "$work/ld-r.err"
    325     fi
    326 else
    327     { sed 's/^/main: /' "$work/partial-main.err"
    328       sed 's/^/foo:  /' "$work/partial-foo.err"; } > "$work/ld-r-setup.diag"
    329     not_ok "ld-r-output-mode" "$work/ld-r-setup.diag"
    330 fi
    331 
    332 # ---- default output names (cc -c) ----
    333 rm -f "$work/main.o"
    334 if (cd "$work" && "$KIT" cc -c main.c) \
    335     > "$work/cc-c-default.out" 2> "$work/cc-c-default.err"; then
    336     if [ -f "$work/main.o" ]; then
    337         ok "cc-c-default-output"
    338     else
    339         echo "main.o not created" > "$work/cc-c-default.diag"
    340         not_ok "cc-c-default-output" "$work/cc-c-default.diag"
    341     fi
    342 else
    343     not_ok "cc-c-default-output" "$work/cc-c-default.err"
    344 fi
    345 
    346 rm -f "$work/main.o" "$work/other.o"
    347 if (cd "$work" && "$KIT" cc -c main.c other.c) \
    348     > "$work/cc-c-multi.out" 2> "$work/cc-c-multi.err"; then
    349     if [ -f "$work/main.o" ] && [ -f "$work/other.o" ]; then
    350         ok "cc-c-multi-default-output"
    351     else
    352         echo "expected main.o and other.o" > "$work/cc-c-multi.diag"
    353         not_ok "cc-c-multi-default-output" "$work/cc-c-multi.diag"
    354     fi
    355 else
    356     not_ok "cc-c-multi-default-output" "$work/cc-c-multi.err"
    357 fi
    358 
    359 # ---- cc -E → stdout ----
    360 if "$KIT" cc -E "$work/main.c" > "$work/cc-E-default.out" 2> "$work/cc-E-default.err"; then
    361     if [ -s "$work/cc-E-default.out" ]; then
    362         ok "cc-E-default-stdout"
    363     else
    364         echo "stdout was empty" > "$work/cc-E-default.diag"
    365         not_ok "cc-E-default-stdout" "$work/cc-E-default.diag"
    366     fi
    367 else
    368     not_ok "cc-E-default-stdout" "$work/cc-E-default.err"
    369 fi
    370 
    371 # ---- cc -dumpmachine probe ----
    372 if "$KIT" cc -target riscv64-linux -dumpmachine \
    373     > "$work/cc-dumpmachine.out" 2> "$work/cc-dumpmachine.err" &&
    374    [ "$(cat "$work/cc-dumpmachine.out")" = "riscv64-linux" ]; then
    375     ok "cc-dumpmachine-probe"
    376 else
    377     not_ok "cc-dumpmachine-probe" "$work/cc-dumpmachine.err"
    378 fi
    379 
    380 # ---- shared-library creation is ELF-only for v1 ----
    381 if "$KIT" cc -target x86_64-macos -shared -nostdlib "$work/main.c" \
    382         -o "$work/libbad.dylib" > "$work/cc-shared-macho.out" \
    383         2> "$work/cc-shared-macho.err"; then
    384     echo "expected non-ELF -shared to fail" > "$work/cc-shared-macho.diag"
    385     not_ok "cc-rejects-nonelf-shared" "$work/cc-shared-macho.diag"
    386 elif grep -q "only for ELF" "$work/cc-shared-macho.err"; then
    387     ok "cc-rejects-nonelf-shared"
    388 else
    389     not_ok "cc-rejects-nonelf-shared" "$work/cc-shared-macho.err"
    390 fi
    391 
    392 if "$KIT" cc -target x86_64-macos -c "$work/main.c" \
    393         -o "$work/main-macho.o" > "$work/main-macho.out" \
    394         2> "$work/main-macho.err"; then
    395     if "$KIT" ld -target x86_64-macos -shared "$work/main-macho.o" \
    396             -o "$work/libbad2.dylib" > "$work/ld-shared-macho.out" \
    397             2> "$work/ld-shared-macho.err"; then
    398         echo "expected non-ELF ld -shared to fail" \
    399             > "$work/ld-shared-macho.diag"
    400         not_ok "ld-rejects-nonelf-shared" "$work/ld-shared-macho.diag"
    401     elif grep -q "only for ELF" "$work/ld-shared-macho.err"; then
    402         ok "ld-rejects-nonelf-shared"
    403     else
    404         not_ok "ld-rejects-nonelf-shared" "$work/ld-shared-macho.err"
    405     fi
    406 else
    407     not_ok "ld-rejects-nonelf-shared" "$work/main-macho.err"
    408 fi
    409 
    410 if "$KIT" build-lib -target x86_64-macos -dynamic "$work/main.c" \
    411         -o "$work/libbad3.dylib" > "$work/buildlib-shared-macho.out" \
    412         2> "$work/buildlib-shared-macho.err"; then
    413     echo "expected non-ELF build-lib -dynamic to fail" \
    414         > "$work/buildlib-shared-macho.diag"
    415     not_ok "build-lib-rejects-nonelf-shared" \
    416         "$work/buildlib-shared-macho.diag"
    417 elif grep -q "only for ELF" "$work/buildlib-shared-macho.err"; then
    418     ok "build-lib-rejects-nonelf-shared"
    419 else
    420     not_ok "build-lib-rejects-nonelf-shared" \
    421         "$work/buildlib-shared-macho.err"
    422 fi
    423 
    424 # ---- WebAssembly v1 boundary: wasm32 source batches only, no wasm64/link ----
    425 if "$KIT" cc -target wasm64-none -c "$work/main.c" \
    426         -o "$work/bad64.wasm" > "$work/cc-wasm64.out" \
    427         2> "$work/cc-wasm64.err"; then
    428     echo "expected wasm64 compile to fail" > "$work/cc-wasm64.diag"
    429     not_ok "cc-rejects-wasm64" "$work/cc-wasm64.diag"
    430 elif grep -q "wasm64 is not supported" "$work/cc-wasm64.err"; then
    431     ok "cc-rejects-wasm64"
    432 else
    433     not_ok "cc-rejects-wasm64" "$work/cc-wasm64.err"
    434 fi
    435 
    436 if "$KIT" cc -target x86_64-linux -c "$work/main.c" \
    437         -o "$work/wasm-boundary-main.o" > "$work/wasm-boundary-cc.out" \
    438         2> "$work/wasm-boundary-cc.err"; then
    439     if "$KIT" build-exe -target wasm32-none "$work/wasm-boundary-main.o" \
    440             -o "$work/bad-linked.wasm" > "$work/wasm-object-link.out" \
    441             2> "$work/wasm-object-link.err"; then
    442         echo "expected wasm32 object-link attempt to fail" \
    443             > "$work/wasm-object-link.diag"
    444         not_ok "build-exe-rejects-wasm-object-link" \
    445             "$work/wasm-object-link.diag"
    446     elif grep -q "accepts only source files" "$work/wasm-object-link.err"; then
    447         ok "build-exe-rejects-wasm-object-link"
    448     else
    449         not_ok "build-exe-rejects-wasm-object-link" \
    450             "$work/wasm-object-link.err"
    451     fi
    452 
    453     if "$KIT" ar rc "$work/libwasm-boundary.a" \
    454             "$work/wasm-boundary-main.o" > "$work/wasm-boundary-ar.out" \
    455             2> "$work/wasm-boundary-ar.err"; then
    456         if "$KIT" build-exe -target wasm32-none \
    457                 "$work/libwasm-boundary.a" -o "$work/bad-archive.wasm" \
    458                 > "$work/wasm-archive-link.out" \
    459                 2> "$work/wasm-archive-link.err"; then
    460             echo "expected wasm32 archive-link attempt to fail" \
    461                 > "$work/wasm-archive-link.diag"
    462             not_ok "build-exe-rejects-wasm-archive-link" \
    463                 "$work/wasm-archive-link.diag"
    464         elif grep -q "accepts only source files" \
    465                 "$work/wasm-archive-link.err"; then
    466             ok "build-exe-rejects-wasm-archive-link"
    467         else
    468             not_ok "build-exe-rejects-wasm-archive-link" \
    469                 "$work/wasm-archive-link.err"
    470         fi
    471     else
    472         not_ok "build-exe-rejects-wasm-archive-link" \
    473             "$work/wasm-boundary-ar.err"
    474     fi
    475 else
    476     not_ok "build-exe-rejects-wasm-object-link" \
    477         "$work/wasm-boundary-cc.err"
    478     not_ok "build-exe-rejects-wasm-archive-link" \
    479         "$work/wasm-boundary-cc.err"
    480 fi
    481 
    482 # ---- cc -print-file-name probe ----
    483 if "$KIT" cc -print-file-name=crt1.o \
    484     > "$work/cc-print-file-name.out" 2> "$work/cc-print-file-name.err" &&
    485    [ "$(cat "$work/cc-print-file-name.out")" = "crt1.o" ]; then
    486     ok "cc-print-file-name-probe"
    487 else
    488     not_ok "cc-print-file-name-probe" "$work/cc-print-file-name.err"
    489 fi
    490 
    491 # ---- cc -print-resource-dir is non-empty (the freestanding header root) ----
    492 if "$KIT" cc -print-resource-dir \
    493     > "$work/cc-resdir.out" 2> "$work/cc-resdir.err" &&
    494    [ -s "$work/cc-resdir.out" ]; then
    495     ok "cc-print-resource-dir"
    496 else
    497     { echo "expected a non-empty resource dir"; cat "$work/cc-resdir.err"; } \
    498         > "$work/cc-resdir.diag"
    499     not_ok "cc-print-resource-dir" "$work/cc-resdir.diag"
    500 fi
    501 
    502 # ---- cc -print-search-dirs surfaces the hosted sysroot dirs ----
    503 # KIT_SYSROOT + a cross target makes the output deterministic on any host
    504 # (independent of a native SDK). The Linux expansion uses the standard FHS
    505 # sysroot layout: libc/kernel headers under <sysroot>/usr/include (plus the
    506 # glibc multiarch subdir) and libraries under <sysroot>/usr/lib.
    507 mkdir -p "$work/sr"
    508 if KIT_SYSROOT="$work/sr" "$KIT" cc -print-search-dirs -lc -target x86_64-linux \
    509     > "$work/cc-searchdirs.out" 2> "$work/cc-searchdirs.err" &&
    510    grep -q "$work/sr/usr/lib" "$work/cc-searchdirs.out" &&
    511    grep -q "$work/sr/usr/include" "$work/cc-searchdirs.out" &&
    512    grep -q "x86_64-linux-gnu" "$work/cc-searchdirs.out"; then
    513     ok "cc-print-search-dirs-hosted"
    514 else
    515     cp "$work/cc-searchdirs.out" "$work/cc-searchdirs.diag"
    516     not_ok "cc-print-search-dirs-hosted" "$work/cc-searchdirs.diag"
    517 fi
    518 
    519 # ---- cc -print-sysroot echoes the effective explicit sysroot ----
    520 mkdir -p "$work/sdkx" "$work/sdky"
    521 if [ "$("$KIT" cc -print-sysroot --sysroot "$work/sdkx" 2>/dev/null)" = "$work/sdkx" ] &&
    522    [ "$(KIT_SYSROOT="$work/sdky" "$KIT" cc -print-sysroot 2>/dev/null)" = "$work/sdky" ]; then
    523     ok "cc-print-sysroot"
    524 else
    525     "$KIT" cc -print-sysroot --sysroot "$work/sdkx" > "$work/cc-psysroot.diag" 2>&1
    526     not_ok "cc-print-sysroot" "$work/cc-psysroot.diag"
    527 fi
    528 
    529 # ---- accepted-but-ignored compatibility flags are silently accepted ----
    530 if "$KIT" cc -Wall -Wextra -std=c11 -ffreestanding -c "$work/main.c" \
    531     -o "$work/ignored.o" > "$work/cc-ignored.out" 2> "$work/cc-ignored.err" &&
    532    [ -f "$work/ignored.o" ] && [ ! -s "$work/cc-ignored.err" ]; then
    533     ok "cc-ignored-compat-flags"
    534 else
    535     cp "$work/cc-ignored.err" "$work/cc-ignored.diag"
    536     not_ok "cc-ignored-compat-flags" "$work/cc-ignored.diag"
    537 fi
    538 
    539 # ---- --output= long form (cc) ----
    540 if "$KIT" cc -c "$work/main.c" --output="$work/cc-long-output.o" \
    541     > "$work/cc-long-output.out" 2> "$work/cc-long-output.err" &&
    542    [ -f "$work/cc-long-output.o" ]; then
    543     ok "cc-long-output"
    544 else
    545     not_ok "cc-long-output" "$work/cc-long-output.err"
    546 fi
    547 
    548 # ---- -iquote include path ----
    549 mkdir -p "$work/quote-inc"
    550 cat > "$work/quote-inc/q.h" <<'SRC'
    551 #define Q_VALUE 7
    552 SRC
    553 cat > "$work/quote-include.c" <<'SRC'
    554 #include "q.h"
    555 int q(void) { return Q_VALUE; }
    556 SRC
    557 run_ok "cc-iquote-include" "$KIT" cc -c "$work/quote-include.c" \
    558     -iquote "$work/quote-inc" -o "$work/quote-include.o"
    559 
    560 # ---- implicit freestanding headers ----
    561 cat > "$work/implicit-header.c" <<'SRC'
    562 #include <stddef.h>
    563 #include <stdint.h>
    564 int f(void) { return (int)sizeof(size_t) + (int)UINT8_MAX; }
    565 SRC
    566 run_ok "cc-implicit-freestanding-headers" "$KIT" cc -target aarch64-linux \
    567     -c "$work/implicit-header.c" -o "$work/implicit-header.o"
    568 
    569 # ---- asm-label rename emits the verbatim symbol (no doubled underscore) ----
    570 # `extern f __asm__("_name")` must produce the linker symbol "_name" exactly.
    571 # The Darwin headers spell the Mach-O leading underscore themselves (e.g.
    572 # realpath's __asm("_realpath$DARWIN_EXTSN")), so the C frontend must use the
    573 # asm label verbatim instead of re-running it through the underscore mangler.
    574 # Regression: doubling the underscore (__realpath$DARWIN_EXTSN) broke the
    575 # stage2 self-build link against libSystem. Always target Mach-O so the check
    576 # is host-independent.
    577 cat > "$work/asm-rename.c" <<'SRC'
    578 extern int probe(void) __asm__("_kit_rename_target");
    579 int caller(void) { return probe(); }
    580 SRC
    581 if "$KIT" cc -target arm64-apple-macos -c "$work/asm-rename.c" \
    582     -o "$work/asm-rename.o" 2> "$work/asm-rename.err" &&
    583    "$KIT" nm "$work/asm-rename.o" 2> "$work/asm-rename-nm.err" \
    584     | grep -qE '[[:space:]]_kit_rename_target$' &&
    585    ! "$KIT" nm "$work/asm-rename.o" 2>/dev/null | grep -q '__kit_rename_target'; then
    586     ok "cc-asm-label-rename-verbatim-symbol"
    587 else
    588     { cat "$work/asm-rename.err" 2>/dev/null
    589       "$KIT" nm "$work/asm-rename.o" 2>/dev/null | sed 's/^/nm: /'; } \
    590         > "$work/asm-rename.diag"
    591     not_ok "cc-asm-label-rename-verbatim-symbol" "$work/asm-rename.diag"
    592 fi
    593 
    594 # ---- runtime auto-build + link via nm (aarch64) ----
    595 driver_section runtime_link
    596 
    597 mkdir -p "$work/rt-support/rt"
    598 cp -R "$repo_root/rt/include" "$work/rt-support/rt/include"
    599 cp -R "$repo_root/rt/lib" "$work/rt-support/rt/lib"
    600 cat > "$work/rt-div.c" <<'SRC'
    601 #include <stdint.h>
    602 typedef unsigned __int128 u128;
    603 u128 div128(u128 a, u128 b) { return a / b; }
    604 void _start(void) {
    605     volatile u128 x = div128((u128)9, (u128)3);
    606     (void)x;
    607     for (;;) {}
    608 }
    609 SRC
    610 # Verify the runtime was auto-built AND linked by checking the linked
    611 # executable actually defines the runtime symbol the source needs
    612 # (__udivti3 for the u128 division). This is location-independent: the rt
    613 # archive is cached under DriverEnv's cache_dir, not the support-dir.
    614 if "$KIT" cc --support-dir "$work/rt-support" -target aarch64-linux \
    615     -e _start "$work/rt-div.c" -o "$work/rt-div" \
    616     > "$work/rt-div.out" 2> "$work/rt-div.err" &&
    617    "$KIT" nm "$work/rt-div" 2> "$work/rt-div-nm.err" \
    618     | grep -qE '[Tt] __udivti3'; then
    619     ok "cc-auto-builds-and-links-libkit-rt"
    620 else
    621     not_ok "cc-auto-builds-and-links-libkit-rt" "$work/rt-div.err"
    622 fi
    623 
    624 cat > "$work/rt-x64-start.c" <<'SRC'
    625 extern int test_main(void);
    626 void _start(void) {
    627     volatile int rc = test_main();
    628     (void)rc;
    629     for (;;) {}
    630 }
    631 SRC
    632 # freestanding_lib.c uses vsnprintf etc., so a defined `vsnprintf` in the
    633 # linked image proves the runtime's printf.c (a libc source, not just
    634 # compiler-rt) was auto-built and linked — what the old `ar t | grep printf.c`
    635 # member check verified, but location-independent of the rt cache dir.
    636 # The rt's libc symbols are weak (a user libc may override them), so accept a
    637 # weak (W/w) definition as well as a strong (T/t) one.
    638 if "$KIT" cc --support-dir "$work/rt-support" -target x86_64-linux \
    639     -e _start "$repo_root/test/rt/cases/freestanding_lib.c" \
    640     "$work/rt-x64-start.c" \
    641     -o "$work/rt-x64" > "$work/rt-x64.out" 2> "$work/rt-x64.err" &&
    642    "$KIT" nm "$work/rt-x64" 2> "$work/rt-x64-nm.err" \
    643     | grep -qE '[TtWw] vsnprintf'; then
    644     ok "cc-auto-builds-and-links-libkit-rt-x64"
    645 else
    646     { sed 's/^/cc: /' "$work/rt-x64.err"
    647       sed 's/^/nm: /' "$work/rt-x64-nm.err" 2>/dev/null; } > "$work/rt-x64.diag"
    648     not_ok "cc-auto-builds-and-links-libkit-rt-x64" "$work/rt-x64.diag"
    649 fi
    650 
    651 # ---- ld auto-builds + links the runtime (rt archive cached under support-dir) ----
    652 mkdir -p "$work/ld-rt-support/rt"
    653 cp -R "$repo_root/rt/include" "$work/ld-rt-support/rt/include"
    654 cp -R "$repo_root/rt/lib" "$work/ld-rt-support/rt/lib"
    655 if "$KIT" cc --support-dir "$work/ld-rt-support" -target aarch64-linux \
    656     -c "$work/rt-div.c" -o "$work/ld-rt-div.o" \
    657     > "$work/ld-rt-div-cc.out" 2> "$work/ld-rt-div-cc.err" &&
    658    "$KIT" ld --support-dir "$work/ld-rt-support" \
    659     -e _start "$work/ld-rt-div.o" -o "$work/ld-rt-div" \
    660     > "$work/ld-rt-div.out" 2> "$work/ld-rt-div.err" &&
    661    [ -f "$work/ld-rt-support/build/rt/aarch64-linux/libkit_rt.a" ]; then
    662     ok "ld-auto-builds-and-links-libkit-rt"
    663 else
    664     { sed 's/^/cc: /' "$work/ld-rt-div-cc.err"
    665       sed 's/^/ld: /' "$work/ld-rt-div.err" 2>/dev/null; } > "$work/ld-rt.diag"
    666     not_ok "ld-auto-builds-and-links-libkit-rt" "$work/ld-rt.diag"
    667 fi
    668 
    669 # ---- --output= long form (ld) ----
    670 if "$KIT" ld "$work/main.o" --output="$work/ld-long-output" \
    671     > "$work/ld-long-output.out" 2> "$work/ld-long-output.err" &&
    672    [ -f "$work/ld-long-output" ]; then
    673     ok "ld-long-output"
    674 else
    675     not_ok "ld-long-output" "$work/ld-long-output.err"
    676 fi
    677 
    678 # ---- ld -lc expands hosted CRT/libc from --sysroot ----
    679 mkdir -p "$work/ld-hosted-sr/lib" "$work/ld-hosted-sr/include"
    680 cat > "$work/ld-hosted-main.c" <<'SRC'
    681 int main(void) { return 0; }
    682 SRC
    683 cat > "$work/ld-hosted-crt.c" <<'SRC'
    684 extern int main(void);
    685 void _start(void) { (void)main(); for (;;) {} }
    686 SRC
    687 cat > "$work/ld-hosted-crti.c" <<'SRC'
    688 void __kit_fake_crti(void) {}
    689 SRC
    690 cat > "$work/ld-hosted-crtn.c" <<'SRC'
    691 void __kit_fake_crtn(void) {}
    692 SRC
    693 cat > "$work/ld-hosted-libc.c" <<'SRC'
    694 int libc_marker(void) { return 7; }
    695 SRC
    696 if "$KIT" cc -target x86_64-linux -fPIE -c "$work/ld-hosted-main.c" \
    697         -o "$work/ld-hosted-main.o" > "$work/ld-hosted-main.out" \
    698         2> "$work/ld-hosted-main.err" &&
    699    "$KIT" cc -target x86_64-linux -fPIE -c "$work/ld-hosted-crt.c" \
    700         -o "$work/ld-hosted-sr/lib/Scrt1.o" > "$work/ld-hosted-scrt.out" \
    701         2> "$work/ld-hosted-scrt.err" &&
    702    "$KIT" cc -target x86_64-linux -fno-PIC -c "$work/ld-hosted-crt.c" \
    703         -o "$work/ld-hosted-sr/lib/crt1.o" > "$work/ld-hosted-crt1.out" \
    704         2> "$work/ld-hosted-crt1.err" &&
    705    "$KIT" cc -target x86_64-linux -c "$work/ld-hosted-crti.c" \
    706         -o "$work/ld-hosted-sr/lib/crti.o" > "$work/ld-hosted-crti.out" \
    707         2> "$work/ld-hosted-crti.err" &&
    708    "$KIT" cc -target x86_64-linux -c "$work/ld-hosted-crtn.c" \
    709         -o "$work/ld-hosted-sr/lib/crtn.o" > "$work/ld-hosted-crtn.out" \
    710         2> "$work/ld-hosted-crtn.err" &&
    711    cp "$script_dir/fixtures/libc.so" "$work/ld-hosted-sr/lib/libc.so" &&
    712    "$KIT" cc -target x86_64-linux -fno-PIC -c "$work/ld-hosted-libc.c" \
    713         -o "$work/ld-hosted-libc-static.o" > "$work/ld-hosted-libc-static.out" \
    714         2> "$work/ld-hosted-libc-static.err" &&
    715    "$KIT" ar rc "$work/ld-hosted-sr/lib/libc.a" \
    716         "$work/ld-hosted-libc-static.o" > "$work/ld-hosted-ar.out" \
    717         2> "$work/ld-hosted-ar.err"; then
    718     if "$KIT" ld --support-dir "$work/ld-rt-support" \
    719             --sysroot "$work/ld-hosted-sr" -pie -lc \
    720             "$work/ld-hosted-main.o" -o "$work/ld-hosted" \
    721             > "$work/ld-hosted.out" 2> "$work/ld-hosted.err" &&
    722        "$KIT" objdump -p "$work/ld-hosted" > "$work/ld-hosted-p.out" \
    723             2> "$work/ld-hosted-p.err" &&
    724        grep -q "interpreter  /lib/ld-musl-x86_64.so.1" \
    725             "$work/ld-hosted-p.out" &&
    726        grep -q "NEEDED       libc.so" "$work/ld-hosted-p.out"; then
    727         ok "ld-hosted-lc-sysroot"
    728     else
    729         { sed 's/^/ld: /' "$work/ld-hosted.err" 2>/dev/null
    730           sed 's/^/dump: /' "$work/ld-hosted-p.err" 2>/dev/null
    731           sed 's/^/    | /' "$work/ld-hosted-p.out" 2>/dev/null; } \
    732             > "$work/ld-hosted.diag"
    733         not_ok "ld-hosted-lc-sysroot" "$work/ld-hosted.diag"
    734     fi
    735 else
    736     { sed 's/^/main: /' "$work/ld-hosted-main.err" 2>/dev/null
    737       sed 's/^/scrt: /' "$work/ld-hosted-scrt.err" 2>/dev/null
    738       sed 's/^/crt1: /' "$work/ld-hosted-crt1.err" 2>/dev/null
    739       sed 's/^/crti: /' "$work/ld-hosted-crti.err" 2>/dev/null
    740       sed 's/^/crtn: /' "$work/ld-hosted-crtn.err" 2>/dev/null
    741       sed 's/^/ar:   /' "$work/ld-hosted-ar.err" 2>/dev/null; } \
    742         > "$work/ld-hosted-setup.diag"
    743     not_ok "ld-hosted-lc-sysroot" "$work/ld-hosted-setup.diag"
    744 fi
    745 
    746 # ---- ld PIE without dynamic deps has no INTERP/DYNAMIC program headers ----
    747 driver_section pie_macho_darwin
    748 
    749 cat > "$work/ld-static-pie.c" <<'SRC'
    750 void _start(void) { for (;;) {} }
    751 SRC
    752 if "$KIT" cc -target x86_64-linux -fPIE -c "$work/ld-static-pie.c" \
    753         -o "$work/ld-static-pie.o" > "$work/ld-static-pie-cc.out" \
    754         2> "$work/ld-static-pie-cc.err" &&
    755    "$KIT" ld -pie -nostdlib "$work/ld-static-pie.o" \
    756         -o "$work/ld-static-pie" > "$work/ld-static-pie.out" \
    757         2> "$work/ld-static-pie.err" &&
    758    "$KIT" objdump -p "$work/ld-static-pie" \
    759         > "$work/ld-static-pie-p.out" 2> "$work/ld-static-pie-p.err"; then
    760     if ! grep -q "interpreter  " "$work/ld-static-pie-p.out" &&
    761        ! grep -q "^  DYNAMIC" "$work/ld-static-pie-p.out" &&
    762        ! grep -q "NEEDED" "$work/ld-static-pie-p.out"; then
    763         ok "ld-static-pie-no-dynamic-headers"
    764     else
    765         sed 's/^/    | /' "$work/ld-static-pie-p.out" \
    766             > "$work/ld-static-pie.diag"
    767         not_ok "ld-static-pie-no-dynamic-headers" \
    768             "$work/ld-static-pie.diag"
    769     fi
    770 else
    771     { sed 's/^/cc: /' "$work/ld-static-pie-cc.err" 2>/dev/null
    772       sed 's/^/ld: /' "$work/ld-static-pie.err" 2>/dev/null
    773       sed 's/^/dump: /' "$work/ld-static-pie-p.err" 2>/dev/null; } \
    774         > "$work/ld-static-pie-setup.diag"
    775     not_ok "ld-static-pie-no-dynamic-headers" \
    776         "$work/ld-static-pie-setup.diag"
    777 fi
    778 
    779 # ---- ld -no-pie cancels an earlier -pie and emits ET_EXEC ----
    780 if "$KIT" ld -pie -no-pie -nostdlib "$work/ld-static-pie.o" \
    781         -o "$work/ld-no-pie" > "$work/ld-no-pie.out" \
    782         2> "$work/ld-no-pie.err"; then
    783     e_type=$(od -An -tx1 -j 16 -N 2 "$work/ld-no-pie" | tr -d ' \n')
    784     if [ "$e_type" = "0200" ]; then
    785         ok "ld-no-pie-cancels-pie"
    786     else
    787         printf 'e_type bytes=%s want=0200\n' "$e_type" \
    788             > "$work/ld-no-pie.diag"
    789         not_ok "ld-no-pie-cancels-pie" "$work/ld-no-pie.diag"
    790     fi
    791 else
    792     not_ok "ld-no-pie-cancels-pie" "$work/ld-no-pie.err"
    793 fi
    794 
    795 # ---- -no-pie cancels an earlier -pie and emits ET_EXEC ----
    796 cat > "$work/cc-no-pie.c" <<'SRC'
    797 void _start(void) { for (;;) {} }
    798 SRC
    799 if "$KIT" cc -target x86_64-linux -nostdlib -pie -no-pie \
    800         "$work/cc-no-pie.c" -o "$work/cc-no-pie" \
    801         > "$work/cc-no-pie.out" 2> "$work/cc-no-pie.err"; then
    802     e_type=$(od -An -tx1 -j 16 -N 2 "$work/cc-no-pie" | tr -d ' \n')
    803     if [ "$e_type" = "0200" ]; then
    804         ok "cc-no-pie-cancels-pie"
    805     else
    806         printf 'e_type bytes=%s want=0200\n' "$e_type" \
    807             > "$work/cc-no-pie.diag"
    808         not_ok "cc-no-pie-cancels-pie" "$work/cc-no-pie.diag"
    809     fi
    810 else
    811     not_ok "cc-no-pie-cancels-pie" "$work/cc-no-pie.err"
    812 fi
    813 
    814 # ---- objdump -x aggregate (sections + symbol table) ----
    815 if "$KIT" objdump -x "$work/main.o" \
    816     > "$work/objdump-x.out" 2> "$work/objdump-x.err" &&
    817    grep -q "Sections:" "$work/objdump-x.out" &&
    818    grep -q "SYMBOL TABLE:" "$work/objdump-x.out"; then
    819     ok "objdump-x-aggregate"
    820 else
    821     not_ok "objdump-x-aggregate" "$work/objdump-x.err"
    822 fi
    823 
    824 # ---- Mach-O -ffunction-sections atom coalescing ----
    825 {
    826     printf 'int live(void) { return 0; }\n'
    827     i=0
    828     while [ "$i" -lt 300 ]; do
    829         printf 'int f%s(void) { return %s; }\n' "$i" "$i"
    830         i=$((i + 1))
    831     done
    832 } > "$work/macho-many.c"
    833 if "$KIT" cc -target arm64-apple-macos -O1 -ffunction-sections \
    834         -c "$work/macho-many.c" -o "$work/macho-many.o" \
    835         > "$work/macho-many-cc.out" 2> "$work/macho-many-cc.err" &&
    836    "$KIT" objdump -h -t "$work/macho-many.o" \
    837         > "$work/macho-many-dump.out" 2> "$work/macho-many-dump.err"; then
    838     macho_flags=$(od -An -tx4 -j 24 -N 4 "$work/macho-many.o" 2>/dev/null |
    839         tr -d '[:space:]')
    840     macho_sec_count=$(awk '
    841         /^SYMBOL TABLE:/ { in_sec = 0 }
    842         in_sec && /^ *[0-9]+ / { n++ }
    843         /^Sections:/ { in_sec = 1 }
    844         END { print n + 0 }
    845     ' "$work/macho-many-dump.out")
    846     if [ "$macho_sec_count" -le 8 ] &&
    847        ! grep -q '\*UND\*' "$work/macho-many-dump.out" &&
    848        [ "$macho_flags" = "00002000" ]; then
    849         ok "macho-function-sections-atoms"
    850     else
    851         { printf 'sections=%s flags=%s; unexpected UND/flags/fanout\n' \
    852             "$macho_sec_count" "$macho_flags"
    853           sed 's/^/    | /' "$work/macho-many-dump.out"; } \
    854             > "$work/macho-many.diag"
    855         not_ok "macho-function-sections-atoms" "$work/macho-many.diag"
    856     fi
    857 else
    858     { sed 's/^/cc:   /' "$work/macho-many-cc.err"
    859       sed 's/^/dump: /' "$work/macho-many-dump.err"; } > "$work/macho-many-setup.diag"
    860     not_ok "macho-function-sections-atoms" "$work/macho-many-setup.diag"
    861 fi
    862 
    863 # ---- Mach-O LC_BUILD_VERSION platform for iOS targets ----
    864 macho_build_platform() {
    865     obj=$1
    866     segcmd_size=$(od -An -tu4 -j 36 -N 4 "$obj" 2>/dev/null | tr -d '[:space:]')
    867     if [ -z "$segcmd_size" ]; then
    868         return 1
    869     fi
    870     od -An -tu4 -j $((32 + segcmd_size + 8)) -N 4 "$obj" 2>/dev/null |
    871         tr -d '[:space:]'
    872 }
    873 cat > "$work/macho-ios.c" <<'SRC'
    874 int f(void) { return 7; }
    875 SRC
    876 if "$KIT" cc -target arm64-apple-ios -c "$work/macho-ios.c" \
    877         -o "$work/macho-ios.o" \
    878         > "$work/macho-ios-cc.out" 2> "$work/macho-ios-cc.err" &&
    879    "$KIT" cc -target arm64-apple-ios-simulator -c "$work/macho-ios.c" \
    880         -o "$work/macho-ios-sim.o" \
    881         > "$work/macho-ios-sim-cc.out" 2> "$work/macho-ios-sim-cc.err"; then
    882     ios_platform=$(macho_build_platform "$work/macho-ios.o")
    883     sim_platform=$(macho_build_platform "$work/macho-ios-sim.o")
    884     if [ "$ios_platform" = "2" ] && [ "$sim_platform" = "7" ]; then
    885         ok "macho-ios-build-version-platforms"
    886     else
    887         { printf 'ios platform=%s (want 2)\n' "$ios_platform"
    888           printf 'sim platform=%s (want 7)\n' "$sim_platform"; } \
    889             > "$work/macho-ios-platform.diag"
    890         not_ok "macho-ios-build-version-platforms" \
    891             "$work/macho-ios-platform.diag"
    892     fi
    893 else
    894     { sed 's/^/ios: /' "$work/macho-ios-cc.err"
    895       sed 's/^/sim: /' "$work/macho-ios-sim-cc.err"; } \
    896         > "$work/macho-ios-setup.diag"
    897     not_ok "macho-ios-build-version-platforms" "$work/macho-ios-setup.diag"
    898 fi
    899 
    900 # ---- Darwin framework linking through cc and ld ----
    901 iossim_sdk=
    902 if [ "$(uname -s 2>/dev/null)" = "Darwin" ]; then
    903     iossim_sdk=$(xcrun --sdk iphonesimulator --show-sdk-path 2>/dev/null || true)
    904 fi
    905 if [ -z "$iossim_sdk" ]; then
    906     skip_test "darwin-framework-link" "no iPhoneSimulator SDK"
    907 else
    908     cat > "$work/framework-main.c" <<'SRC'
    909 int main(void) { return 0; }
    910 SRC
    911     if "$KIT" cc -target arm64-apple-ios-simulator --sysroot "$iossim_sdk" \
    912             -lc -framework Foundation "$work/framework-main.c" \
    913             -o "$work/framework-cc" \
    914             > "$work/framework-cc.out" 2> "$work/framework-cc.err" &&
    915        "$KIT" cc -target arm64-apple-ios-simulator --sysroot "$iossim_sdk" \
    916             -c "$work/framework-main.c" -o "$work/framework-main.o" \
    917             > "$work/framework-obj.out" 2> "$work/framework-obj.err" &&
    918        "$KIT" ld -target arm64-apple-ios-simulator --sysroot "$iossim_sdk" \
    919             -lc -framework Foundation "$work/framework-main.o" \
    920             -o "$work/framework-ld" \
    921             > "$work/framework-ld.out" 2> "$work/framework-ld.err" &&
    922        "$KIT" ld -arch arm64 -platform_version ios-simulator 12.0 17.0 \
    923             --sysroot "$iossim_sdk" -lc -framework Foundation \
    924             "$work/framework-main.o" -o "$work/framework-ld-platform" \
    925             > "$work/framework-ld-platform.out" \
    926             2> "$work/framework-ld-platform.err" &&
    927        "$KIT" build-exe -arch arm64 \
    928             -platform_version ios-simulator 12.0 17.0 \
    929             --sysroot "$iossim_sdk" -lc -framework Foundation \
    930             "$work/framework-main.c" -o "$work/framework-build" \
    931             > "$work/framework-build.out" 2> "$work/framework-build.err"; then
    932         ok "darwin-framework-link"
    933     else
    934         { sed 's/^/cc:  /' "$work/framework-cc.err"
    935           sed 's/^/obj: /' "$work/framework-obj.err"
    936           sed 's/^/ld:  /' "$work/framework-ld.err"
    937           sed 's/^/ldp: /' "$work/framework-ld-platform.err"
    938           sed 's/^/bld: /' "$work/framework-build.err"; } \
    939             > "$work/framework-link.diag"
    940         not_ok "darwin-framework-link" "$work/framework-link.diag"
    941     fi
    942 fi
    943 
    944 # ---- run: JIT compile a source + archive on demand, exit status is the result ----
    945 driver_section run_hosted
    946 
    947 cat > "$work/run-main.c" <<'SRC'
    948 int add42(int);
    949 int main(void) { return add42(0); }
    950 SRC
    951 cat > "$work/run-lib.c" <<'SRC'
    952 int add42(int x) { return x + 42; }
    953 SRC
    954 
    955 if "$KIT" cc -I"$repo_root/rt/include" \
    956         -c "$work/run-lib.c" -o "$work/run-lib.o" \
    957         > "$work/run-lib.out" 2> "$work/run-lib.err" &&
    958    "$KIT" ar rcs "$work/librun.a" "$work/run-lib.o" \
    959         > "$work/run-ar.out" 2> "$work/run-ar.err"; then
    960     "$KIT" run -I"$repo_root/rt/include" \
    961         "$work/run-main.c" "$work/librun.a" \
    962         > "$work/run-archive.out" 2> "$work/run-archive.err"
    963     run_status=$?
    964     if [ "$run_status" -eq 42 ]; then
    965         ok "run-source-archive-demand"
    966     else
    967         { printf 'status %s, want 42\n' "$run_status"
    968           sed 's/^/    | /' "$work/run-archive.err"; } > "$work/run-archive.diag"
    969         not_ok "run-source-archive-demand" "$work/run-archive.diag"
    970     fi
    971 else
    972     { sed 's/^/lib: /' "$work/run-lib.err"
    973       sed 's/^/ar:  /' "$work/run-ar.err"; } > "$work/run-setup.diag"
    974     not_ok "run-source-archive-demand" "$work/run-setup.diag"
    975 fi
    976 
    977 cat > "$work/run-fp.c" <<'SRC'
    978 int main(void) {
    979     volatile double a = 3.0;
    980     volatile double b = 2.0;
    981     double c = (a * b) + (a / b);
    982     int scaled = (int)(c * 10.0);
    983     return scaled == 75 ? 0 : 1;
    984 }
    985 SRC
    986 if "$KIT" run "$work/run-fp.c" > "$work/run-fp.out" 2> "$work/run-fp.err"; then
    987     ok "run-host-fp-uses-hardware-abi"
    988 else
    989     not_ok "run-host-fp-uses-hardware-abi" "$work/run-fp.err"
    990 fi
    991 
    992 # ---- run --script: #! shebang interpreter, argv passthrough, implicit -lc ----
    993 # Make a .c file executable with a `#!` line and run it directly. The kernel
    994 # launches the interpreter and appends the script path + the user's args, so
    995 # `--script` names the sole source and routes everything after it to the
    996 # program's argv. `--script` implies -lc; under the JIT that only needs a libc
    997 # sysroot for #include resolution, so probe for a usable one and skip if none.
    998 shebang_sysroot=""
    999 if command -v xcrun >/dev/null 2>&1; then
   1000     shebang_sysroot="$(xcrun --show-sdk-path 2>/dev/null || true)"
   1001 fi
   1002 shebang_sysroot="${KIT_TEST_SYSROOT:-$shebang_sysroot}"
   1003 
   1004 cat > "$work/shebang-probe.c" <<'SRC'
   1005 #include <stdio.h>
   1006 int main(void) { return 0; }
   1007 SRC
   1008 shebang_ok=0
   1009 if [ -n "$shebang_sysroot" ] &&
   1010    "$KIT" run --sysroot "$shebang_sysroot" --script "$work/shebang-probe.c" \
   1011        > "$work/shebang-probe.out" 2> "$work/shebang-probe.err"; then
   1012     shebang_ok=1
   1013 fi
   1014 
   1015 if [ "$shebang_ok" -eq 1 ]; then
   1016     cat > "$work/greet.c" <<SHEBANG
   1017 #!/usr/bin/env -S $KIT run --sysroot $shebang_sysroot --script
   1018 #include <stdio.h>
   1019 #include <stdlib.h>
   1020 int main(int argc, char** argv) {
   1021   if (argc < 2) { fprintf(stderr, "usage: greet N\n"); return 2; }
   1022   printf("greet:%d\n", atoi(argv[1]) + 1);
   1023   return 0;
   1024 }
   1025 SHEBANG
   1026     chmod +x "$work/greet.c"
   1027 
   1028     # Execute the C file directly. The arg "41" reaches the program (not
   1029     # `kit run`); -lc is implied so <stdio.h>/<stdlib.h> resolve.
   1030     if "$work/greet.c" 41 > "$work/greet.out" 2> "$work/greet.err"; then
   1031         contains "run-shebang-arg" "$work/greet.out" "greet:42"
   1032     else
   1033         not_ok "run-shebang-arg" "$work/greet.err"
   1034     fi
   1035 
   1036     # A flag-shaped program arg after the script must pass through to the
   1037     # program, not be parsed as a `kit run` option. atoi("-5")+1 = -4.
   1038     "$work/greet.c" -5 > "$work/greet-flag.out" 2> "$work/greet-flag.err"
   1039     greet_flag_rc=$?
   1040     if [ "$greet_flag_rc" -eq 0 ] && grep -q "greet:-4" "$work/greet-flag.out"; then
   1041         ok "run-shebang-flaglike-arg"
   1042     else
   1043         { printf 'rc=%s\n' "$greet_flag_rc"
   1044           sed 's/^/out: /' "$work/greet-flag.out"
   1045           sed 's/^/err: /' "$work/greet-flag.err"; } > "$work/greet-flag.diag"
   1046         not_ok "run-shebang-flaglike-arg" "$work/greet-flag.diag"
   1047     fi
   1048 else
   1049     skip_test "run-shebang-arg" "no usable libc sysroot (set KIT_TEST_SYSROOT)"
   1050     skip_test "run-shebang-flaglike-arg" "no usable libc sysroot (set KIT_TEST_SYSROOT)"
   1051 fi
   1052 
   1053 # ---- hosted sysroot defaulting: --sysroot is not always required ----------
   1054 # When hosted libc is engaged (-lc) but no --sysroot is given, the sysroot is
   1055 # resolved from KIT_SYSROOT (the single, portable env override — any target) or
   1056 # an on-disk host probe (auto-discovery; native target only, never cross).
   1057 cat > "$work/hosted-min.c" <<'SRC'
   1058 int main(void) { return 0; }
   1059 SRC
   1060 cat > "$work/hosted-hello.c" <<'SRC'
   1061 #include <stdio.h>
   1062 int main(void) { puts("hosted-autoprobe"); return 0; }
   1063 SRC
   1064 
   1065 # Tier 1 — KIT_SYSROOT supplies the sysroot. A Darwin cross-target with -c needs
   1066 # only the profile to resolve (defines/includes, no link inputs), so a bare
   1067 # existing directory suffices and this runs on every host.
   1068 mkdir -p "$work/fake-sdk/usr/include"
   1069 if KIT_SYSROOT="$work/fake-sdk" "$KIT" cc -c -target arm64-macos -lc \
   1070        "$work/hosted-min.c" -o "$work/hosted-kitsysroot.o" \
   1071        > "$work/hosted-kitsysroot.out" 2> "$work/hosted-kitsysroot.err"; then
   1072     ok "cc-hosted-kit-sysroot-env"
   1073 else
   1074     not_ok "cc-hosted-kit-sysroot-env" "$work/hosted-kitsysroot.err"
   1075 fi
   1076 
   1077 # The host probe must NOT fire for a cross-compile: a foreign-OS target with no
   1078 # --sysroot/KIT_SYSROOT must still error rather than borrow the host's libc.
   1079 # Pick a target whose OS differs from the host so the native-only gate always
   1080 # blocks the probe; scrub the env override so only an (incorrect) auto-probe
   1081 # could apply.
   1082 case "$(uname -s)" in
   1083   Linux) cross_target="arm64-macos" ;;   # macOS is never the Linux host's OS
   1084   *)     cross_target="x86_64-linux" ;;  # Linux is never the macOS/other host's OS
   1085 esac
   1086 if env -u KIT_SYSROOT "$KIT" cc -c -target "$cross_target" -lc \
   1087        "$work/hosted-min.c" -o "$work/hosted-cross.o" \
   1088        > "$work/hosted-cross.out" 2> "$work/hosted-cross.err"; then
   1089     { echo "cross-compile unexpectedly resolved a sysroot via auto-probe"
   1090       sed 's/^/out: /' "$work/hosted-cross.out"; } > "$work/hosted-cross.diag"
   1091     not_ok "cc-hosted-cross-no-autoprobe" "$work/hosted-cross.diag"
   1092 else
   1093     ok "cc-hosted-cross-no-autoprobe"
   1094 fi
   1095 
   1096 # Tier 2 — host probe end-to-end: no --sysroot, no KIT_SYSROOT, native target.
   1097 # Compile + link + run a real hosted program so crt/libc binding is exercised,
   1098 # not just header/define resolution. Runs where a host libc is discoverable
   1099 # (a macOS host with the CLT/Xcode SDK, or a Linux host with libc headers);
   1100 # skip otherwise (e.g. FreeBSD here is untested).
   1101 autoprobe_ok=0
   1102 case "$(uname -s)" in
   1103   Darwin)
   1104     { [ -d /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk ] ||
   1105       [ -d "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" ]; } &&
   1106       autoprobe_ok=1 ;;
   1107   Linux)
   1108     [ -e /usr/include/stdio.h ] && autoprobe_ok=1 ;;
   1109 esac
   1110 if [ "$autoprobe_ok" -eq 1 ]; then
   1111     if env -u KIT_SYSROOT "$KIT" cc -lc "$work/hosted-hello.c" \
   1112            -o "$work/hosted-autoprobe" \
   1113            > "$work/hosted-autoprobe.out" 2> "$work/hosted-autoprobe.err" &&
   1114        "$work/hosted-autoprobe" > "$work/hosted-autoprobe.run" 2>&1 &&
   1115        grep -q "hosted-autoprobe" "$work/hosted-autoprobe.run"; then
   1116         ok "cc-hosted-sysroot-autoprobe"
   1117     else
   1118         { sed 's/^/err: /' "$work/hosted-autoprobe.err"
   1119           [ -f "$work/hosted-autoprobe.run" ] &&
   1120               sed 's/^/run: /' "$work/hosted-autoprobe.run"; } \
   1121             > "$work/hosted-autoprobe.diag"
   1122         not_ok "cc-hosted-sysroot-autoprobe" "$work/hosted-autoprobe.diag"
   1123     fi
   1124 else
   1125     skip_test "cc-hosted-sysroot-autoprobe" "no discoverable host libc to probe"
   1126 fi
   1127 
   1128 # ---- archive link order is enforced (def after ref vs ref after def) ----
   1129 cat > "$work/order-main.c" <<'SRC'
   1130 int foo(void);
   1131 int _start(void) { return foo(); }
   1132 SRC
   1133 cat > "$work/order-foo.c" <<'SRC'
   1134 int foo(void) { return 0; }
   1135 SRC
   1136 
   1137 if "$KIT" cc -target x86_64-linux -c "$work/order-main.c" -o "$work/order-main.o" \
   1138     > "$work/order-main.out" 2> "$work/order-main.err" &&
   1139    "$KIT" cc -target x86_64-linux -c "$work/order-foo.c" -o "$work/order-foo.o" \
   1140     > "$work/order-foo.out" 2> "$work/order-foo.err" &&
   1141    "$KIT" ar rc "$work/libfoo.a" "$work/order-foo.o" \
   1142     > "$work/order-ar.out" 2> "$work/order-ar.err"; then
   1143     if "$KIT" cc -target x86_64-linux -L"$work" "$work/order-main.o" -lfoo \
   1144         -o "$work/order-right" > "$work/order-right.out" 2> "$work/order-right.err" &&
   1145        ! "$KIT" cc -target x86_64-linux -L"$work" -lfoo "$work/order-main.o" \
   1146         -o "$work/order-wrong" > "$work/order-wrong.out" 2> "$work/order-wrong.err"; then
   1147         ok "cc-link-archive-order"
   1148     else
   1149         { sed 's/^/right| /' "$work/order-right.err"
   1150           sed 's/^/wrong| /' "$work/order-wrong.err"; } > "$work/order.diag"
   1151         not_ok "cc-link-archive-order" "$work/order.diag"
   1152     fi
   1153 else
   1154     { sed 's/^/main: /' "$work/order-main.err"
   1155       sed 's/^/foo:  /' "$work/order-foo.err"
   1156       sed 's/^/ar:   /' "$work/order-ar.err"; } > "$work/order-setup.diag"
   1157     not_ok "cc-link-archive-order" "$work/order-setup.diag"
   1158 fi
   1159 
   1160 # ---- one-shot -O1 compile+link drops a deferred local .Lkit_ro (Bug B) ----
   1161 # At -O1, a function-local const is recorded as a DEFERRED local .Lkit_ro
   1162 # symbol (obj_symbol_defer leaves it a `removed` tombstone) and only
   1163 # materialized when its function is emitted. An unused static helper is never
   1164 # emitted, so its const stays a tombstone, and a discarded reference to the
   1165 # helper can leave a dead reloc against it. A file-format emitter sweeps these
   1166 # away before writing, so the separate compile-then-link path is clean; the
   1167 # one-shot path links the just-compiled object IN MEMORY (un-swept), so the
   1168 # linker must honor the same tombstones. Before the fix this aborted with
   1169 # "undefined reference to '.Lkit_ro.N'" or "reloc references unmapped symbol".
   1170 # Freestanding + _start so the link pulls no compiler runtime (host-portable,
   1171 # rt-independent); link success + an emitted executable is the assertion.
   1172 cat > "$work/lkitro-lib.c" <<'SRC'
   1173 int lkitro_lib(void) { return 0; }
   1174 SRC
   1175 cat > "$work/lkitro-drv.c" <<'SRC'
   1176 static const char* lkitro_helper(int i) {
   1177   static const char* const names[] = {"alpha", "beta", "gamma", "delta"};
   1178   return names[i & 3];
   1179 }
   1180 int lkitro_lib(void);
   1181 int _start(void) { (void)lkitro_helper; return lkitro_lib(); }
   1182 SRC
   1183 if "$KIT" cc -O1 -ffreestanding -nostdlib -c "$work/lkitro-lib.c" \
   1184        -o "$work/lkitro-lib.o" \
   1185        > "$work/lkitro-lib.out" 2> "$work/lkitro-lib.err" &&
   1186    "$KIT" ar rcs "$work/liblkitro.a" "$work/lkitro-lib.o" \
   1187        > "$work/lkitro-ar.out" 2> "$work/lkitro-ar.err" &&
   1188    "$KIT" cc -O1 -ffreestanding -nostdlib -e _start \
   1189        "$work/lkitro-drv.c" "$work/liblkitro.a" -o "$work/lkitro-app" \
   1190        > "$work/lkitro-link.out" 2> "$work/lkitro-link.err"; then
   1191     is_executable "cc-o1-oneshot-deferred-rodata" "$work/lkitro-app"
   1192 else
   1193     { sed 's/^/lib:  /' "$work/lkitro-lib.err"
   1194       sed 's/^/ar:   /' "$work/lkitro-ar.err"
   1195       sed 's/^/link| /' "$work/lkitro-link.err"; } > "$work/lkitro.diag"
   1196     not_ok "cc-o1-oneshot-deferred-rodata" "$work/lkitro.diag"
   1197 fi
   1198 
   1199 # ---- rv64 cross-target end-to-end (as, cc, ld, objdump) ----
   1200 driver_section rv64_check_tools
   1201 
   1202 # Exercises the rv64 lane of each tool the toolchain claims to support.
   1203 # Cross-compile-only; no qemu/native exec required.
   1204 cat > "$work/rv64-asm.S" <<'SRC'
   1205     .text
   1206     .globl rv64_entry
   1207 rv64_entry:
   1208     li      a0, 7
   1209     ret
   1210 SRC
   1211 if "$KIT" as -target riscv64-linux "$work/rv64-asm.S" -o "$work/rv64-asm.o" \
   1212     > "$work/rv64-as.out" 2> "$work/rv64-as.err"; then
   1213     if "$KIT" objdump -h "$work/rv64-asm.o" \
   1214         > "$work/rv64-as-h.out" 2> "$work/rv64-as-h.err" &&
   1215        grep -q "elf64-riscv64" "$work/rv64-as-h.out"; then
   1216         ok "rv64-as-cc-objdump-elf"
   1217     else
   1218         cp "$work/rv64-as-h.out" "$work/rv64-as-h.diag"
   1219         not_ok "rv64-as-cc-objdump-elf" "$work/rv64-as-h.diag"
   1220     fi
   1221 else
   1222     not_ok "rv64-as-cc-objdump-elf" "$work/rv64-as.err"
   1223 fi
   1224 
   1225 cat > "$work/rv64-cc.c" <<'SRC'
   1226 int rv64_main(int x) { return x + 1; }
   1227 SRC
   1228 if "$KIT" cc -target riscv64-linux -c "$work/rv64-cc.c" -o "$work/rv64-cc.o" \
   1229     > "$work/rv64-cc.out" 2> "$work/rv64-cc.err"; then
   1230     if "$KIT" objdump -d "$work/rv64-cc.o" \
   1231         > "$work/rv64-cc-d.out" 2> "$work/rv64-cc-d.err" &&
   1232        grep -q "ret" "$work/rv64-cc-d.out"; then
   1233         ok "rv64-cc-emits-ret"
   1234     else
   1235         cp "$work/rv64-cc-d.out" "$work/rv64-cc-d.diag"
   1236         not_ok "rv64-cc-emits-ret" "$work/rv64-cc-d.diag"
   1237     fi
   1238 else
   1239     not_ok "rv64-cc-emits-ret" "$work/rv64-cc.err"
   1240 fi
   1241 
   1242 cat > "$work/rv64-ld-start.c" <<'SRC'
   1243 void _start(void) { for (;;) {} }
   1244 SRC
   1245 if "$KIT" cc -target riscv64-linux -ffreestanding -fno-PIC \
   1246        -c "$work/rv64-ld-start.c" -o "$work/rv64-ld-start.o" \
   1247     > "$work/rv64-ld-cc.out" 2> "$work/rv64-ld-cc.err"; then
   1248     if "$KIT" ld -static -e _start "$work/rv64-ld-start.o" \
   1249            -o "$work/rv64-ld.exe" \
   1250         > "$work/rv64-ld.out" 2> "$work/rv64-ld.err"; then
   1251         # ELF e_machine == EM_RISCV (243 = 0xF3) at byte offset 0x12,
   1252         # little-endian 16-bit field. Validates the linker emitted an
   1253         # rv64 ELF executable without needing objdump to parse ET_EXEC.
   1254         em_byte=$(od -An -tx1 -j 18 -N 1 "$work/rv64-ld.exe" | tr -d ' \n')
   1255         if [ "$em_byte" = "f3" ]; then
   1256             ok "rv64-ld-static-exe"
   1257         else
   1258             printf 'e_machine byte=%s want=f3\n' "$em_byte" \
   1259                 > "$work/rv64-ld.diag"
   1260             not_ok "rv64-ld-static-exe" "$work/rv64-ld.diag"
   1261         fi
   1262     else
   1263         not_ok "rv64-ld-static-exe" "$work/rv64-ld.err"
   1264     fi
   1265 else
   1266     not_ok "rv64-ld-static-exe" "$work/rv64-ld-cc.err"
   1267 fi
   1268 
   1269 # ---- check: frontend-only, emits no output files ----
   1270 rm -f "$work/check.o" "$work/a.out"
   1271 if "$KIT" check "$work/main.c" > "$work/check.out" 2> "$work/check.err"; then
   1272     if [ ! -e "$work/check.o" ] && [ ! -e "$work/a.out" ]; then
   1273         ok "check-no-output"
   1274     else
   1275         echo "unexpected output file" > "$work/check.diag"
   1276         not_ok "check-no-output" "$work/check.diag"
   1277     fi
   1278 else
   1279     not_ok "check-no-output" "$work/check.err"
   1280 fi
   1281 
   1282 cat > "$work/check-bad.c" <<'SRC'
   1283 int broken( { return 0; }
   1284 SRC
   1285 if "$KIT" check "$work/check-bad.c" \
   1286         > "$work/check-bad.out" 2> "$work/check-bad.err"; then
   1287     echo "bad source passed" > "$work/check-bad.diag"
   1288     not_ok "check-reports-errors" "$work/check-bad.diag"
   1289 else
   1290     if grep -q "fatal:" "$work/check-bad.err"; then
   1291         ok "check-reports-errors"
   1292     else
   1293         cp "$work/check-bad.err" "$work/check-bad-missing.diag"
   1294         not_ok "check-reports-errors" "$work/check-bad-missing.diag"
   1295     fi
   1296 fi
   1297 
   1298 # ---- nm ----
   1299 # Compile a fresh object for the nm/size/addr2line tests.
   1300 # Use aarch64-linux ELF so DWARF works for addr2line and symbols
   1301 # have predictable names (no Mach-O underscore prefix).
   1302 rm -f "$work/nm-main.o"
   1303 nm_obj_ok=1
   1304 if ! "$KIT" cc -target aarch64-linux -c "$work/main.c" -o "$work/nm-main.o" \
   1305     > "$work/nm-cc.out" 2> "$work/nm-cc.err"; then
   1306     not_ok "nm-setup" "$work/nm-cc.err"
   1307     nm_obj_ok=0
   1308 fi
   1309 
   1310 # basic: symbols in an object file
   1311 if [ "$nm_obj_ok" = 1 ] &&
   1312    "$KIT" nm "$work/nm-main.o" > "$work/nm-basic.out" 2> "$work/nm-basic.err" &&
   1313    grep -q "main" "$work/nm-basic.out" &&
   1314    grep -q "_start" "$work/nm-basic.out"; then
   1315     ok "nm-basic"
   1316 elif [ "$nm_obj_ok" = 1 ]; then
   1317     not_ok "nm-basic" "$work/nm-basic.err"
   1318 else
   1319     not_ok "nm-basic"
   1320 fi
   1321 
   1322 # nm -g: only global symbols.
   1323 # Source with a local (static) function and a global one.
   1324 cat > "$work/nm-global.c" <<'SRC'
   1325 static int hidden(void) { return 1; }
   1326 int visible(void) { return hidden(); }
   1327 SRC
   1328 if [ "$nm_obj_ok" = 1 ] &&
   1329    "$KIT" cc -target aarch64-linux -c "$work/nm-global.c" \
   1330         -o "$work/nm-global.o" > "$work/nm-global-cc.out" \
   1331         2> "$work/nm-global-cc.err" &&
   1332    "$KIT" nm "$work/nm-global.o" > "$work/nm-global-all.out" 2>/dev/null &&
   1333    grep -q "hidden" "$work/nm-global-all.out" &&
   1334    "$KIT" nm -g "$work/nm-global.o" > "$work/nm-global-g.out" 2>/dev/null &&
   1335    grep -q "visible" "$work/nm-global-g.out" &&
   1336    ! grep -q "hidden" "$work/nm-global-g.out"; then
   1337     ok "nm-global-only"
   1338 else
   1339     not_ok "nm-global-only"
   1340 fi
   1341 
   1342 # nm -u: undefined only (ELF objects may have no undefined)
   1343 if [ "$nm_obj_ok" = 1 ] &&
   1344    "$KIT" nm -u "$work/nm-main.o" > "$work/nm-undef.out" 2>"$work/nm-undef.err"; then
   1345     ok "nm-undefined-only"
   1346 elif [ "$nm_obj_ok" = 1 ]; then
   1347     not_ok "nm-undefined-only" "$work/nm-undef.err"
   1348 else
   1349     not_ok "nm-undefined-only"
   1350 fi
   1351 
   1352 # nm on an archive
   1353 if [ "$nm_obj_ok" = 1 ] &&
   1354    "$KIT" ar rcs "$work/libnmtest.a" "$work/nm-main.o" \
   1355         > "$work/ar-nm.out" 2> "$work/ar-nm.err" &&
   1356    "$KIT" nm "$work/libnmtest.a" > "$work/nm-archive.out" \
   1357         2> "$work/nm-archive.err" &&
   1358    grep -q "main" "$work/nm-archive.out"; then
   1359     ok "nm-archive"
   1360 elif [ "$nm_obj_ok" = 1 ]; then
   1361     not_ok "nm-archive" "$work/nm-archive.err"
   1362 else
   1363     not_ok "nm-archive"
   1364 fi
   1365 
   1366 # nm -h (help)
   1367 if "$KIT" nm --help > "$work/nm-help.out" 2> "$work/nm-help.err" &&
   1368    grep -q "USAGE" "$work/nm-help.out"; then
   1369     ok "nm-help"
   1370 else
   1371     not_ok "nm-help" "$work/nm-help.err"
   1372 fi
   1373 
   1374 # ---- size ----
   1375 # basic: section sizes of an object file (use nm-main.o)
   1376 if [ "$nm_obj_ok" = 1 ] &&
   1377    "$KIT" size "$work/nm-main.o" > "$work/size-basic.out" 2> "$work/size-basic.err" &&
   1378    grep -q "text" "$work/size-basic.out"; then
   1379     ok "size-basic"
   1380 elif [ "$nm_obj_ok" = 1 ]; then
   1381     not_ok "size-basic" "$work/size-basic.err"
   1382 else
   1383     not_ok "size-basic"
   1384 fi
   1385 
   1386 # size -A: SysV format
   1387 if [ "$nm_obj_ok" = 1 ] &&
   1388    "$KIT" size -A "$work/nm-main.o" > "$work/size-sysv.out" 2> "$work/size-sysv.err" &&
   1389    grep -q "Total" "$work/size-sysv.out"; then
   1390     ok "size-sysv"
   1391 elif [ "$nm_obj_ok" = 1 ]; then
   1392     not_ok "size-sysv" "$work/size-sysv.err"
   1393 else
   1394     not_ok "size-sysv"
   1395 fi
   1396 
   1397 # size on an archive (uses libnmtest.a from nm test)
   1398 if [ "$nm_obj_ok" = 1 ] && [ -f "$work/libnmtest.a" ] &&
   1399    "$KIT" size "$work/libnmtest.a" > "$work/size-archive.out" \
   1400         2> "$work/size-archive.err" &&
   1401    grep -q "text" "$work/size-archive.out"; then
   1402     ok "size-archive"
   1403 elif [ "$nm_obj_ok" = 1 ]; then
   1404     not_ok "size-archive" "$work/size-archive.err"
   1405 else
   1406     not_ok "size-archive"
   1407 fi
   1408 
   1409 # size -h (help)
   1410 if "$KIT" size --help > "$work/size-help.out" 2> "$work/size-help.err" &&
   1411    grep -q "USAGE" "$work/size-help.out"; then
   1412     ok "size-help"
   1413 else
   1414     not_ok "size-help" "$work/size-help.err"
   1415 fi
   1416 
   1417 # ---- addr2line ----
   1418 cat > "$work/a2l.c" <<'SRC'
   1419 int calc(int x) { return x * 2; }
   1420 int main(void) { return calc(42); }
   1421 SRC
   1422 
   1423 if [ "$nm_obj_ok" != 1 ]; then
   1424     not_ok "addr2line-basic"
   1425     not_ok "addr2line-nonzero"
   1426 elif "$KIT" cc -g -target aarch64-linux -c "$work/a2l.c" -o "$work/a2l.o" \
   1427     > "$work/a2l-cc.out" 2> "$work/a2l-cc.err"; then
   1428     calc_addr=$("$KIT" nm "$work/a2l.o" 2>/dev/null | \
   1429                 grep " calc$" | awk '{print $1}')
   1430     if [ -n "$calc_addr" ] &&
   1431        "$KIT" addr2line -e "$work/a2l.o" "$calc_addr" \
   1432            > "$work/a2l-hit.out" 2> "$work/a2l-hit.err" &&
   1433        grep -q "a2l.c" "$work/a2l-hit.out"; then
   1434         ok "addr2line-basic"
   1435     else
   1436         { printf 'calc_addr=%s\n' "$calc_addr"
   1437           sed 's/^/    | /' "$work/a2l-hit.err"; } > "$work/a2l-hit.diag"
   1438         not_ok "addr2line-basic" "$work/a2l-hit.diag"
   1439     fi
   1440     # addr2line should produce output (not crash) on any address
   1441     if "$KIT" addr2line -e "$work/a2l.o" 0x0 \
   1442         > "$work/a2l-miss.out" 2> "$work/a2l-miss.err" &&
   1443        [ -s "$work/a2l-miss.out" ]; then
   1444         ok "addr2line-nonzero"
   1445     else
   1446         not_ok "addr2line-nonzero" "$work/a2l-miss.err"
   1447     fi
   1448 else
   1449     not_ok "addr2line-basic" "$work/a2l-cc.err"
   1450     not_ok "addr2line-nonzero"
   1451 fi
   1452 
   1453 # addr2line over Mach-O DWARF: the producer emits .debug_* into the
   1454 # __DWARF segment as __debug_* (16-char-truncated, Apple spelling) and
   1455 # the reader maps the requested ELF name onto that form, so addr2line
   1456 # resolves file:line on a Mach-O object too (not just ELF).
   1457 if "$KIT" cc -g -target arm64-apple-macos -c "$work/a2l.c" \
   1458        -o "$work/a2l.macho.o" > "$work/a2l-m-cc.out" 2> "$work/a2l-m-cc.err"; then
   1459     m_addr=$("$KIT" nm "$work/a2l.macho.o" 2>/dev/null | \
   1460              grep " _\{0,1\}calc$" | awk '{print $1}')
   1461     if [ -n "$m_addr" ] &&
   1462        "$KIT" addr2line -e "$work/a2l.macho.o" "$m_addr" \
   1463            > "$work/a2l-m.out" 2> "$work/a2l-m.err" &&
   1464        grep -q "a2l.c" "$work/a2l-m.out"; then
   1465         ok "addr2line-macho"
   1466     else
   1467         { printf 'm_addr=%s\n' "$m_addr"
   1468           sed 's/^/out| /' "$work/a2l-m.out"
   1469           sed 's/^/err| /' "$work/a2l-m.err"; } > "$work/a2l-m.diag"
   1470         not_ok "addr2line-macho" "$work/a2l-m.diag"
   1471     fi
   1472 else
   1473     not_ok "addr2line-macho" "$work/a2l-m-cc.err"
   1474 fi
   1475 
   1476 # Debug-info retention through the Mach-O linker: link a self-contained
   1477 # image (no SDK/libSystem needed), then resolve file:line via the same
   1478 # `nm | addr2line` flow as the ELF case — exercising the __DWARF segment
   1479 # the linker carried plus the reader on a linked image. nm reports
   1480 # absolute vaddrs for linked Mach-O images, so its value feeds addr2line
   1481 # directly.
   1482 cat > "$work/a2lm.c" <<'SRC'
   1483 int helper(int x) { return x * 3; }
   1484 int compute(int n) { return helper(n) + n; }
   1485 void mainx(void) { compute(5); }
   1486 SRC
   1487 if "$KIT" cc -g -target arm64-apple-macos -c "$work/a2lm.c" \
   1488        -o "$work/a2lm.o" > "$work/a2lm-cc.out" 2> "$work/a2lm-cc.err" &&
   1489    "$KIT" ld -o "$work/a2lm.exe" "$work/a2lm.o" -e mainx \
   1490        > "$work/a2lm-ld.out" 2> "$work/a2lm-ld.err"; then
   1491     cm_addr=$("$KIT" nm "$work/a2lm.exe" 2>/dev/null | \
   1492               grep " _\{0,1\}compute$" | awk '{print $1}')
   1493     if "$KIT" objdump -h "$work/a2lm.exe" 2>/dev/null \
   1494            | grep -q '__DWARF,__debug_info' &&
   1495        [ -n "$cm_addr" ] &&
   1496        "$KIT" addr2line -e "$work/a2lm.exe" "0x$cm_addr" \
   1497            > "$work/a2lm-a2l.out" 2> "$work/a2lm-a2l.err" &&
   1498        grep -q "a2lm.c" "$work/a2lm-a2l.out"; then
   1499         ok "addr2line-macho-linked"
   1500     else
   1501         printf 'compute=%s: %s\n' "$cm_addr" \
   1502             "$(cat "$work/a2lm-a2l.out" 2>/dev/null)" > "$work/a2lm.diag"
   1503         not_ok "addr2line-macho-linked" "$work/a2lm.diag"
   1504     fi
   1505 else
   1506     not_ok "addr2line-macho-linked" "$work/a2lm-ld.err"
   1507 fi
   1508 
   1509 # addr2line -h (help)
   1510 if "$KIT" addr2line --help > "$work/a2l-help.out" 2> "$work/a2l-help.err" &&
   1511    grep -q "USAGE" "$work/a2l-help.out"; then
   1512     ok "addr2line-help"
   1513 else
   1514     not_ok "addr2line-help" "$work/a2l-help.err"
   1515 fi
   1516 
   1517 # --emit=ir: dump the semantic CG IR tape (requires -O1+).
   1518 cat > "$work/ir.c" <<'SRC'
   1519 int add(int a, int b) {
   1520   int c = a + b;
   1521   return c * 2;
   1522 }
   1523 SRC
   1524 
   1525 if "$KIT" cc -O1 --emit=ir -c "$work/ir.c" -o "$work/ir.out" \
   1526     > "$work/ir-emit.out" 2> "$work/ir-emit.err" &&
   1527    grep -q "^func sym#" "$work/ir.out" &&
   1528    grep -q "binop" "$work/ir.out" &&
   1529    grep -q "iadd" "$work/ir.out" &&
   1530    grep -q "ret value=" "$work/ir.out"; then
   1531     ok "cc-emit-ir"
   1532 else
   1533     not_ok "cc-emit-ir" "$work/ir-emit.err"
   1534 fi
   1535 
   1536 if "$KIT" cc --help > "$work/cc-help.out" 2> "$work/cc-help.err" &&
   1537    grep -q -- "-O2 aliases -O1" "$work/cc-help.out"; then
   1538     ok "cc-help-o2-alias"
   1539 else
   1540     not_ok "cc-help-o2-alias" "$work/cc-help.err"
   1541 fi
   1542 
   1543 if "$KIT" --help > "$work/kit-help.out" 2> "$work/kit-help.err" &&
   1544    ! grep -q "^  emu[[:space:]]" "$work/kit-help.out"; then
   1545     ok "kit-help-hides-emu"
   1546 else
   1547     not_ok "kit-help-hides-emu" "$work/kit-help.out"
   1548 fi
   1549 
   1550 if grep -q "ELF -shared modes" "$work/kit-help.out" &&
   1551    grep -q "static .a or ELF shared library" "$work/kit-help.out" &&
   1552    grep -q "executable or ELF shared library" "$work/kit-help.out"; then
   1553     ok "kit-help-elf-shared-scope"
   1554 else
   1555     not_ok "kit-help-elf-shared-scope" "$work/kit-help.out"
   1556 fi
   1557 
   1558 if "$KIT" ld --help > "$work/ld-help.out" 2> "$work/ld-help.err" &&
   1559    grep -Fq "relocated release discovers its sibling support automatically" \
   1560        "$work/ld-help.out" &&
   1561    grep -Fq -- "--support-dir remains an authoritative override" \
   1562        "$work/ld-help.out" &&
   1563    ! grep -Fq "currently needs an explicit --support-dir" \
   1564        "$work/ld-help.out"; then
   1565     ok "ld-help-canonical-support"
   1566 else
   1567     not_ok "ld-help-canonical-support" "$work/ld-help.out"
   1568 fi
   1569 
   1570 if grep -Fq "public link boundary validates raw objects" "$work/ld-help.out" &&
   1571    grep -Fq "archive member" "$work/ld-help.out" &&
   1572    grep -Fq -- "-target is authoritative" "$work/ld-help.out"; then
   1573     ok "ld-help-central-compatibility"
   1574 else
   1575     not_ok "ld-help-central-compatibility" "$work/ld-help.out"
   1576 fi
   1577 
   1578 if "$KIT" dbg --help > "$work/dbg-help.out" 2> "$work/dbg-help.err" &&
   1579    ! grep -q "toy" "$work/dbg-help.out"; then
   1580     ok "dbg-help-hides-toy"
   1581 else
   1582     not_ok "dbg-help-hides-toy" "$work/dbg-help.out"
   1583 fi
   1584 
   1585 if "$KIT" cc -O2 --emit=ir -c "$work/ir.c" -o "$work/ir-o2.out" \
   1586     > "$work/ir-o2-emit.out" 2> "$work/ir-o2-emit.err" &&
   1587    cmp -s "$work/ir.out" "$work/ir-o2.out"; then
   1588     ok "cc-o2-aliases-o1-ir"
   1589 else
   1590     { echo "expected -O2 IR to match -O1"; cat "$work/ir-o2-emit.err"; } \
   1591         > "$work/ir-o2.diag"
   1592     not_ok "cc-o2-aliases-o1-ir" "$work/ir-o2.diag"
   1593 fi
   1594 
   1595 # --emit=ir without -O1 must be rejected (no IR tape is recorded at -O0).
   1596 if "$KIT" cc --emit=ir -c "$work/ir.c" -o "$work/ir-o0.out" \
   1597     > "$work/ir-o0.out.log" 2> "$work/ir-o0.err"; then
   1598     echo "expected failure at -O0" > "$work/ir-o0.diag"
   1599     not_ok "cc-emit-ir-requires-opt" "$work/ir-o0.diag"
   1600 elif grep -q "requires -O1" "$work/ir-o0.err"; then
   1601     ok "cc-emit-ir-requires-opt"
   1602 else
   1603     cp "$work/ir-o0.err" "$work/ir-o0-wrong.diag"
   1604     not_ok "cc-emit-ir-requires-opt" "$work/ir-o0-wrong.diag"
   1605 fi
   1606 
   1607 # ---- install: symlink the kit tools into a dir ----
   1608 driver_section install_backtrace_autovar
   1609 
   1610 # Default set: toolchain + standard-named byte utils, each link -> the binary.
   1611 inst_dir="$work/inst"
   1612 run_ok "install-default" "$KIT" install "$inst_dir"
   1613 for t in cc cpp as ld ar ranlib strip objcopy objdump nm size addr2line \
   1614          image strings xxd cmp sha256sum b2sum crc32 gzip gunzip lz4 lz4c; do
   1615     assert_file_exists "install-has-$t" "$inst_dir/$t"
   1616 done
   1617 is_executable "install-cc-executable" "$inst_dir/cc"
   1618 # A symlink to the binary has identical bytes, and dispatches by basename.
   1619 same_file "install-cc-points-at-kit" "$KIT" "$inst_dir/cc"
   1620 run_ok "install-symlink-dispatches" "$inst_dir/cc" --help
   1621 
   1622 # Non-default tools (e.g. mc) are excluded unless --all selects everything.
   1623 if [ -e "$inst_dir/mc" ]; then
   1624     echo "mc present in the default set" > "$work/install-mc.diag"
   1625     not_ok "install-default-excludes-mc" "$work/install-mc.diag"
   1626 else
   1627     ok "install-default-excludes-mc"
   1628 fi
   1629 inst_all="$work/inst-all"
   1630 run_ok "install-all" "$KIT" install --all "$inst_all"
   1631 assert_file_exists "install-all-has-mc" "$inst_all/mc"
   1632 if [ -e "$inst_all/emu" ]; then
   1633     echo "emu present in install --all" > "$work/install-all-emu.diag"
   1634     not_ok "install-all-hides-emu" "$work/install-all-emu.diag"
   1635 else
   1636     ok "install-all-hides-emu"
   1637 fi
   1638 
   1639 # Existing entries are an error without -f, replaced with it.
   1640 run_fail "install-existing-without-force" "$KIT" install "$inst_dir"
   1641 run_ok "install-existing-with-force" "$KIT" install -f "$inst_dir"
   1642 
   1643 # An explicit tool list installs exactly those names; an unknown name and a
   1644 # missing directory are usage errors.
   1645 inst_pick="$work/inst-pick"
   1646 run_ok "install-explicit" "$KIT" install "$inst_pick" nm ld
   1647 assert_file_exists "install-explicit-nm" "$inst_pick/nm"
   1648 if [ -e "$inst_pick/cc" ]; then
   1649     echo "cc installed despite an explicit 'nm ld' list" \
   1650         > "$work/install-pick.diag"
   1651     not_ok "install-explicit-only-listed" "$work/install-pick.diag"
   1652 else
   1653     ok "install-explicit-only-listed"
   1654 fi
   1655 run_fail "install-unknown-tool" "$KIT" install "$work/inst-bad" bogustool
   1656 run_ok "install-noargs-help" "$KIT" install
   1657 
   1658 # A hard link shares the binary's inode (the `-ef` test compares st_dev+st_ino).
   1659 inst_hard="$work/inst-hard"
   1660 run_ok "install-hardlink" "$KIT" install -H "$inst_hard" nm
   1661 if [ "$inst_hard/nm" -ef "$KIT" ]; then
   1662     ok "install-hardlink-same-inode"
   1663 else
   1664     echo "hard link does not share the binary's inode" \
   1665         > "$work/install-hard.diag"
   1666     not_ok "install-hardlink-same-inode" "$work/install-hard.diag"
   1667 fi
   1668 
   1669 # Dry-run reports intent but changes nothing (the target dir stays absent).
   1670 inst_dry="$work/inst-dry"
   1671 run_ok "install-dry-run" "$KIT" install -n "$inst_dry"
   1672 if [ -e "$inst_dry" ]; then
   1673     echo "dry-run created $inst_dry" > "$work/install-dry.diag"
   1674     not_ok "install-dry-run-no-changes" "$work/install-dry.diag"
   1675 else
   1676     ok "install-dry-run-no-changes"
   1677 fi
   1678 
   1679 # ---- kit run: auto-backtrace on a fatal fault ----------------------------
   1680 # `kit run` executes the JITed entry in-process; on a fatal fault it installs a
   1681 # guard that walks the frame-pointer chain at the crash point and prints a
   1682 # symbolized backtrace to stderr, then exits non-zero (128 + signal). Gated on
   1683 # the host being able to natively `kit run` at all (cross-arch hosts can't), so
   1684 # probe with a trivial program first.
   1685 cat > "$work/run-probe.c" <<'SRC'
   1686 int main(void) { return 0; }
   1687 SRC
   1688 if "$KIT" run "$work/run-probe.c" > "$work/run-probe.out" 2> "$work/run-probe.err"; then
   1689     cat > "$work/run-crash.c" <<'SRC'
   1690 __attribute__((noinline)) void bt_leaf(int* p) { *p = 42; }
   1691 __attribute__((noinline)) void bt_mid(void) { bt_leaf((int*)0); }
   1692 __attribute__((noinline)) void bt_root(void) { bt_mid(); }
   1693 int main(void) { bt_root(); return 0; }
   1694 SRC
   1695     "$KIT" run -g "$work/run-crash.c" \
   1696         > "$work/run-crash.out" 2> "$work/run-crash.err"
   1697     run_crash_rc=$?
   1698     # A clean exit would mean the guard missed the fault.
   1699     if [ "$run_crash_rc" -ne 0 ]; then
   1700         ok "run-backtrace-nonzero-exit"
   1701     else
   1702         { printf 'rc=%s\n' "$run_crash_rc"; cat "$work/run-crash.err"; } \
   1703             > "$work/run-crash.diag"
   1704         not_ok "run-backtrace-nonzero-exit" "$work/run-crash.diag"
   1705     fi
   1706     # The symbolized chain (innermost first) must reach every kit frame with a
   1707     # source location; outer host-runtime frames are truncated at the image edge.
   1708     contains "run-backtrace-leaf" "$work/run-crash.err" "bt_leaf"
   1709     contains "run-backtrace-mid" "$work/run-crash.err" "bt_mid"
   1710     contains "run-backtrace-root" "$work/run-crash.err" "bt_root"
   1711     contains "run-backtrace-source" "$work/run-crash.err" "run-crash.c"
   1712 else
   1713     skip_test "run-backtrace-nonzero-exit" "host cannot natively kit-run"
   1714     skip_test "run-backtrace-leaf" "host cannot natively kit-run"
   1715     skip_test "run-backtrace-mid" "host cannot natively kit-run"
   1716     skip_test "run-backtrace-root" "host cannot natively kit-run"
   1717     skip_test "run-backtrace-source" "host cannot natively kit-run"
   1718 fi
   1719 
   1720 # ---- -ftrivial-auto-var-init: zero-init uninitialized automatic variables ----
   1721 # =pattern is parsed but not yet implemented; =bogus is a usage error; =zero and
   1722 # =uninitialized compile cleanly.
   1723 run_fail "cc-autovar-pattern-rejected" "$KIT" cc \
   1724     -ftrivial-auto-var-init=pattern -c "$work/main.c" -o "$work/avi-pat.o"
   1725 contains "cc-autovar-pattern-msg" "$work/cc-autovar-pattern-rejected.err" \
   1726     "not yet supported"
   1727 run_fail "cc-autovar-bogus-rejected" "$KIT" cc \
   1728     -ftrivial-auto-var-init=bogus -c "$work/main.c" -o "$work/avi-bog.o"
   1729 run_ok "cc-autovar-zero-compiles" "$KIT" cc \
   1730     -ftrivial-auto-var-init=zero -c "$work/main.c" -o "$work/avi-zero.o"
   1731 run_ok "cc-autovar-uninit-compiles" "$KIT" cc \
   1732     -ftrivial-auto-var-init=uninitialized -c "$work/main.c" -o "$work/avi-uninit.o"
   1733 
   1734 # Functional: an uninitialized local array read back under =zero is all-zero, so
   1735 # `s` stays 100 and the exit status is deterministic. Gated on native run.
   1736 cat > "$work/avi-zero-run.c" <<'SRC'
   1737 int main(void) {
   1738   int a[64];
   1739   int s = 100;
   1740   int* p = a; /* address-taken: forces a real stack slot, not a value */
   1741   for (int i = 0; i < 64; i++) s += p[i];
   1742   return s & 0xff;
   1743 }
   1744 SRC
   1745 if "$KIT" cc "$work/main.c" -o "$work/avi-probe" \
   1746        > "$work/avi-probe.out" 2> "$work/avi-probe.err" &&
   1747    "$work/avi-probe" > /dev/null 2>&1; then
   1748     if "$KIT" cc -ftrivial-auto-var-init=zero "$work/avi-zero-run.c" \
   1749            -o "$work/avi-zero-run" \
   1750            > "$work/avi-zero-run.out" 2> "$work/avi-zero-run.err"; then
   1751         "$work/avi-zero-run"
   1752         avi_rc=$?
   1753         if [ "$avi_rc" -eq 100 ]; then
   1754             ok "cc-autovar-zero-is-deterministic"
   1755         else
   1756             printf 'exit=%s want=100\n' "$avi_rc" > "$work/avi-zero-run.diag"
   1757             not_ok "cc-autovar-zero-is-deterministic" "$work/avi-zero-run.diag"
   1758         fi
   1759     else
   1760         not_ok "cc-autovar-zero-is-deterministic" "$work/avi-zero-run.err"
   1761     fi
   1762 else
   1763     skip_test "cc-autovar-zero-is-deterministic" \
   1764         "host cannot natively run kit cc output"
   1765 fi
   1766 
   1767 driver_section_done
   1768 kit_summary driver-cc
   1769 kit_exit