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