run.sh (47351B)
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 cat > "$work/main.c" <<'SRC' 30 int main(void) { return 0; } 31 int _start(void) { return 0; } 32 SRC 33 34 cat > "$work/other.c" <<'SRC' 35 int other(void) { return 0; } 36 SRC 37 38 # ---- executable permission bits (cc/ld) ---- 39 if (umask 077; "$KIT" cc "$work/main.c" -o "$work/cc-exe") \ 40 > "$work/cc.out" 2> "$work/cc.err"; then 41 check_mode "cc-executable-mode" "$work/cc-exe" 700 42 else 43 not_ok "cc-executable-mode" "$work/cc.err" 44 fi 45 46 rm -f "$work/a.out" 47 if (cd "$work" && umask 077 && "$KIT" cc main.c) \ 48 > "$work/cc-link-default.out" 2> "$work/cc-link-default.err"; then 49 if [ -f "$work/a.out" ]; then 50 check_mode "cc-link-default-output" "$work/a.out" 700 51 else 52 echo "a.out not created" > "$work/cc-link-default.diag" 53 not_ok "cc-link-default-output" "$work/cc-link-default.diag" 54 fi 55 else 56 not_ok "cc-link-default-output" "$work/cc-link-default.err" 57 fi 58 59 if (umask 077; "$KIT" cc -c "$work/main.c" -o "$work/main.o") \ 60 > "$work/cc-c.out" 2> "$work/cc-c.err"; then 61 : > "$work/ld-exe" 62 chmod 0644 "$work/ld-exe" 63 if (umask 077; "$KIT" ld "$work/main.o" -o "$work/ld-exe") \ 64 > "$work/ld.out" 2> "$work/ld.err"; then 65 check_mode "ld-executable-mode" "$work/ld-exe" 700 66 else 67 not_ok "ld-executable-mode" "$work/ld.err" 68 fi 69 else 70 not_ok "ld-executable-mode" "$work/cc-c.err" 71 fi 72 73 # ---- stdin piping: -x asm/s/asm-cpp/S → object ---- 74 for xlang in asm s asm-cpp S; do 75 if printf '.globl asm_stdin\nasm_stdin:\n ret\n' | 76 "$KIT" cc -target x86_64-linux -x "$xlang" -c - \ 77 -o "$work/stdin-$xlang.o" \ 78 > "$work/stdin-$xlang.out" 2> "$work/stdin-$xlang.err" && 79 [ -f "$work/stdin-$xlang.o" ]; then 80 ok "cc-stdin-x-$xlang" 81 else 82 not_ok "cc-stdin-x-$xlang" "$work/stdin-$xlang.err" 83 fi 84 done 85 86 # ---- stdin piping: -x wasm/wat → emitted C ---- 87 for xlang in wasm wat; do 88 if printf '(module (func (export "test_main") (result i32) i32.const 0))\n' | 89 "$KIT" cc --emit=c -x "$xlang" - \ 90 -o "$work/stdin-$xlang.c" \ 91 > "$work/stdin-$xlang.out" 2> "$work/stdin-$xlang.err" && 92 [ -s "$work/stdin-$xlang.c" ]; then 93 ok "cc-stdin-x-$xlang" 94 else 95 not_ok "cc-stdin-x-$xlang" "$work/stdin-$xlang.err" 96 fi 97 done 98 99 # ---- ld -r partial link: output mode + relinkability ---- 100 cat > "$work/partial-main.c" <<'SRC' 101 int foo(void); 102 int _start(void) { return foo(); } 103 SRC 104 cat > "$work/partial-foo.c" <<'SRC' 105 int foo(void) { return 0; } 106 SRC 107 108 if "$KIT" cc -target x86_64-linux -c "$work/partial-main.c" \ 109 -o "$work/partial-main.o" > "$work/partial-main.out" \ 110 2> "$work/partial-main.err" && 111 "$KIT" cc -target x86_64-linux -c "$work/partial-foo.c" \ 112 -o "$work/partial-foo.o" > "$work/partial-foo.out" \ 113 2> "$work/partial-foo.err"; then 114 : > "$work/partial.o" 115 chmod 0644 "$work/partial.o" 116 if (umask 077; "$KIT" ld -r "$work/partial-main.o" \ 117 "$work/partial-foo.o" -o "$work/partial.o") \ 118 > "$work/ld-r.out" 2> "$work/ld-r.err"; then 119 check_mode "ld-r-output-mode" "$work/partial.o" 644 120 if "$KIT" ld "$work/partial.o" -o "$work/partial-exe" \ 121 > "$work/ld-r-final.out" 2> "$work/ld-r-final.err"; then 122 ok "ld-r-final-link" 123 else 124 not_ok "ld-r-final-link" "$work/ld-r-final.err" 125 fi 126 else 127 not_ok "ld-r-output-mode" "$work/ld-r.err" 128 fi 129 else 130 { sed 's/^/main: /' "$work/partial-main.err" 131 sed 's/^/foo: /' "$work/partial-foo.err"; } > "$work/ld-r-setup.diag" 132 not_ok "ld-r-output-mode" "$work/ld-r-setup.diag" 133 fi 134 135 # ---- default output names (cc -c) ---- 136 rm -f "$work/main.o" 137 if (cd "$work" && "$KIT" cc -c main.c) \ 138 > "$work/cc-c-default.out" 2> "$work/cc-c-default.err"; then 139 if [ -f "$work/main.o" ]; then 140 ok "cc-c-default-output" 141 else 142 echo "main.o not created" > "$work/cc-c-default.diag" 143 not_ok "cc-c-default-output" "$work/cc-c-default.diag" 144 fi 145 else 146 not_ok "cc-c-default-output" "$work/cc-c-default.err" 147 fi 148 149 rm -f "$work/main.o" "$work/other.o" 150 if (cd "$work" && "$KIT" cc -c main.c other.c) \ 151 > "$work/cc-c-multi.out" 2> "$work/cc-c-multi.err"; then 152 if [ -f "$work/main.o" ] && [ -f "$work/other.o" ]; then 153 ok "cc-c-multi-default-output" 154 else 155 echo "expected main.o and other.o" > "$work/cc-c-multi.diag" 156 not_ok "cc-c-multi-default-output" "$work/cc-c-multi.diag" 157 fi 158 else 159 not_ok "cc-c-multi-default-output" "$work/cc-c-multi.err" 160 fi 161 162 # ---- cc -E → stdout ---- 163 if "$KIT" cc -E "$work/main.c" > "$work/cc-E-default.out" 2> "$work/cc-E-default.err"; then 164 if [ -s "$work/cc-E-default.out" ]; then 165 ok "cc-E-default-stdout" 166 else 167 echo "stdout was empty" > "$work/cc-E-default.diag" 168 not_ok "cc-E-default-stdout" "$work/cc-E-default.diag" 169 fi 170 else 171 not_ok "cc-E-default-stdout" "$work/cc-E-default.err" 172 fi 173 174 # ---- cc -dumpmachine probe ---- 175 if "$KIT" cc -target riscv64-linux -dumpmachine \ 176 > "$work/cc-dumpmachine.out" 2> "$work/cc-dumpmachine.err" && 177 [ "$(cat "$work/cc-dumpmachine.out")" = "riscv64-linux" ]; then 178 ok "cc-dumpmachine-probe" 179 else 180 not_ok "cc-dumpmachine-probe" "$work/cc-dumpmachine.err" 181 fi 182 183 # ---- cc -print-file-name probe ---- 184 if "$KIT" cc -print-file-name=crt1.o \ 185 > "$work/cc-print-file-name.out" 2> "$work/cc-print-file-name.err" && 186 [ "$(cat "$work/cc-print-file-name.out")" = "crt1.o" ]; then 187 ok "cc-print-file-name-probe" 188 else 189 not_ok "cc-print-file-name-probe" "$work/cc-print-file-name.err" 190 fi 191 192 # ---- cc -print-resource-dir is non-empty (the freestanding header root) ---- 193 if "$KIT" cc -print-resource-dir \ 194 > "$work/cc-resdir.out" 2> "$work/cc-resdir.err" && 195 [ -s "$work/cc-resdir.out" ]; then 196 ok "cc-print-resource-dir" 197 else 198 { echo "expected a non-empty resource dir"; cat "$work/cc-resdir.err"; } \ 199 > "$work/cc-resdir.diag" 200 not_ok "cc-print-resource-dir" "$work/cc-resdir.diag" 201 fi 202 203 # ---- cc -print-search-dirs surfaces the hosted sysroot dirs ---- 204 # KIT_SYSROOT + a cross target makes the output deterministic on any host 205 # (independent of a native SDK). The Linux expansion must surface <sysroot>/lib, 206 # <sysroot>/include, and the arch multiarch include subdir. 207 if KIT_SYSROOT="$work/sr" "$KIT" cc -print-search-dirs -lc -target x86_64-linux \ 208 > "$work/cc-searchdirs.out" 2> "$work/cc-searchdirs.err" && 209 grep -q "$work/sr/lib" "$work/cc-searchdirs.out" && 210 grep -q "$work/sr/include" "$work/cc-searchdirs.out" && 211 grep -q "x86_64-linux-gnu" "$work/cc-searchdirs.out"; then 212 ok "cc-print-search-dirs-hosted" 213 else 214 cp "$work/cc-searchdirs.out" "$work/cc-searchdirs.diag" 215 not_ok "cc-print-search-dirs-hosted" "$work/cc-searchdirs.diag" 216 fi 217 218 # ---- cc -print-sysroot echoes the effective explicit sysroot ---- 219 if [ "$("$KIT" cc -print-sysroot --sysroot /opt/sdkx 2>/dev/null)" = "/opt/sdkx" ] && 220 [ "$(KIT_SYSROOT=/opt/sdky "$KIT" cc -print-sysroot 2>/dev/null)" = "/opt/sdky" ]; then 221 ok "cc-print-sysroot" 222 else 223 "$KIT" cc -print-sysroot --sysroot /opt/sdkx > "$work/cc-psysroot.diag" 2>&1 224 not_ok "cc-print-sysroot" "$work/cc-psysroot.diag" 225 fi 226 227 # ---- accepted-but-ignored compatibility flags are silently accepted ---- 228 if "$KIT" cc -Wall -Wextra -std=c11 -ffreestanding -c "$work/main.c" \ 229 -o "$work/ignored.o" > "$work/cc-ignored.out" 2> "$work/cc-ignored.err" && 230 [ -f "$work/ignored.o" ] && [ ! -s "$work/cc-ignored.err" ]; then 231 ok "cc-ignored-compat-flags" 232 else 233 cp "$work/cc-ignored.err" "$work/cc-ignored.diag" 234 not_ok "cc-ignored-compat-flags" "$work/cc-ignored.diag" 235 fi 236 237 # ---- --output= long form (cc) ---- 238 if "$KIT" cc -c "$work/main.c" --output="$work/cc-long-output.o" \ 239 > "$work/cc-long-output.out" 2> "$work/cc-long-output.err" && 240 [ -f "$work/cc-long-output.o" ]; then 241 ok "cc-long-output" 242 else 243 not_ok "cc-long-output" "$work/cc-long-output.err" 244 fi 245 246 # ---- -iquote include path ---- 247 mkdir -p "$work/quote-inc" 248 cat > "$work/quote-inc/q.h" <<'SRC' 249 #define Q_VALUE 7 250 SRC 251 cat > "$work/quote-include.c" <<'SRC' 252 #include "q.h" 253 int q(void) { return Q_VALUE; } 254 SRC 255 run_ok "cc-iquote-include" "$KIT" cc -c "$work/quote-include.c" \ 256 -iquote "$work/quote-inc" -o "$work/quote-include.o" 257 258 # ---- implicit freestanding headers ---- 259 cat > "$work/implicit-header.c" <<'SRC' 260 #include <stddef.h> 261 #include <stdint.h> 262 int f(void) { return (int)sizeof(size_t) + (int)UINT8_MAX; } 263 SRC 264 run_ok "cc-implicit-freestanding-headers" "$KIT" cc -target aarch64-linux \ 265 -c "$work/implicit-header.c" -o "$work/implicit-header.o" 266 267 # ---- runtime auto-build + link via nm (aarch64) ---- 268 mkdir -p "$work/rt-support/rt" 269 cp -R "$repo_root/rt/include" "$work/rt-support/rt/include" 270 cp -R "$repo_root/rt/lib" "$work/rt-support/rt/lib" 271 cat > "$work/rt-div.c" <<'SRC' 272 #include <stdint.h> 273 typedef unsigned __int128 u128; 274 u128 div128(u128 a, u128 b) { return a / b; } 275 void _start(void) { 276 volatile u128 x = div128((u128)9, (u128)3); 277 (void)x; 278 for (;;) {} 279 } 280 SRC 281 # Verify the runtime was auto-built AND linked by checking the linked 282 # executable actually defines the runtime symbol the source needs 283 # (__udivti3 for the u128 division). This is location-independent: the rt 284 # archive is cached under DriverEnv's cache_dir, not the support-dir. 285 if "$KIT" cc --support-dir "$work/rt-support" -target aarch64-linux \ 286 -e _start "$work/rt-div.c" -o "$work/rt-div" \ 287 > "$work/rt-div.out" 2> "$work/rt-div.err" && 288 "$KIT" nm "$work/rt-div" 2> "$work/rt-div-nm.err" \ 289 | grep -qE '[Tt] __udivti3'; then 290 ok "cc-auto-builds-and-links-libkit-rt" 291 else 292 not_ok "cc-auto-builds-and-links-libkit-rt" "$work/rt-div.err" 293 fi 294 295 cat > "$work/rt-x64-start.c" <<'SRC' 296 extern int test_main(void); 297 void _start(void) { 298 volatile int rc = test_main(); 299 (void)rc; 300 for (;;) {} 301 } 302 SRC 303 # freestanding_lib.c uses vsnprintf etc., so a defined `vsnprintf` in the 304 # linked image proves the runtime's printf.c (a libc source, not just 305 # compiler-rt) was auto-built and linked — what the old `ar t | grep printf.c` 306 # member check verified, but location-independent of the rt cache dir. 307 # The rt's libc symbols are weak (a user libc may override them), so accept a 308 # weak (W/w) definition as well as a strong (T/t) one. 309 if "$KIT" cc --support-dir "$work/rt-support" -target x86_64-linux \ 310 -e _start "$repo_root/test/rt/cases/freestanding_lib.c" \ 311 "$work/rt-x64-start.c" \ 312 -o "$work/rt-x64" > "$work/rt-x64.out" 2> "$work/rt-x64.err" && 313 "$KIT" nm "$work/rt-x64" 2> "$work/rt-x64-nm.err" \ 314 | grep -qE '[TtWw] vsnprintf'; then 315 ok "cc-auto-builds-and-links-libkit-rt-x64" 316 else 317 { sed 's/^/cc: /' "$work/rt-x64.err" 318 sed 's/^/nm: /' "$work/rt-x64-nm.err" 2>/dev/null; } > "$work/rt-x64.diag" 319 not_ok "cc-auto-builds-and-links-libkit-rt-x64" "$work/rt-x64.diag" 320 fi 321 322 # ---- ld auto-builds + links the runtime (rt archive cached under support-dir) ---- 323 mkdir -p "$work/ld-rt-support/rt" 324 cp -R "$repo_root/rt/include" "$work/ld-rt-support/rt/include" 325 cp -R "$repo_root/rt/lib" "$work/ld-rt-support/rt/lib" 326 if "$KIT" cc --support-dir "$work/ld-rt-support" -target aarch64-linux \ 327 -c "$work/rt-div.c" -o "$work/ld-rt-div.o" \ 328 > "$work/ld-rt-div-cc.out" 2> "$work/ld-rt-div-cc.err" && 329 "$KIT" ld --support-dir "$work/ld-rt-support" \ 330 -e _start "$work/ld-rt-div.o" -o "$work/ld-rt-div" \ 331 > "$work/ld-rt-div.out" 2> "$work/ld-rt-div.err" && 332 [ -f "$work/ld-rt-support/build/rt/aarch64-linux/libkit_rt.a" ]; then 333 ok "ld-auto-builds-and-links-libkit-rt" 334 else 335 { sed 's/^/cc: /' "$work/ld-rt-div-cc.err" 336 sed 's/^/ld: /' "$work/ld-rt-div.err" 2>/dev/null; } > "$work/ld-rt.diag" 337 not_ok "ld-auto-builds-and-links-libkit-rt" "$work/ld-rt.diag" 338 fi 339 340 # ---- --output= long form (ld) ---- 341 if "$KIT" ld "$work/main.o" --output="$work/ld-long-output" \ 342 > "$work/ld-long-output.out" 2> "$work/ld-long-output.err" && 343 [ -f "$work/ld-long-output" ]; then 344 ok "ld-long-output" 345 else 346 not_ok "ld-long-output" "$work/ld-long-output.err" 347 fi 348 349 # ---- ld --no-undefined rejects unresolved refs in a shared object ---- 350 cat > "$work/shared-undef.c" <<'SRC' 351 int missing(void); 352 int f(void) { return missing(); } 353 SRC 354 if "$KIT" cc -target x86_64-linux -fPIC -c "$work/shared-undef.c" \ 355 -o "$work/shared-undef.o" > "$work/shared-undef-cc.out" \ 356 2> "$work/shared-undef-cc.err"; then 357 run_fail "ld-no-undefined" "$KIT" ld -shared --no-undefined \ 358 "$work/shared-undef.o" -o "$work/shared-undef.so" 359 else 360 not_ok "ld-no-undefined" "$work/shared-undef-cc.err" 361 fi 362 363 # ---- ld -lc expands hosted CRT/libc from --sysroot ---- 364 mkdir -p "$work/ld-hosted-sr/lib" "$work/ld-hosted-sr/include" 365 cat > "$work/ld-hosted-main.c" <<'SRC' 366 int main(void) { return 0; } 367 SRC 368 cat > "$work/ld-hosted-crt.c" <<'SRC' 369 extern int main(void); 370 void _start(void) { (void)main(); for (;;) {} } 371 SRC 372 cat > "$work/ld-hosted-crti.c" <<'SRC' 373 void __kit_fake_crti(void) {} 374 SRC 375 cat > "$work/ld-hosted-crtn.c" <<'SRC' 376 void __kit_fake_crtn(void) {} 377 SRC 378 cat > "$work/ld-hosted-libc.c" <<'SRC' 379 int libc_marker(void) { return 7; } 380 SRC 381 if "$KIT" cc -target x86_64-linux -fPIE -c "$work/ld-hosted-main.c" \ 382 -o "$work/ld-hosted-main.o" > "$work/ld-hosted-main.out" \ 383 2> "$work/ld-hosted-main.err" && 384 "$KIT" cc -target x86_64-linux -fPIE -c "$work/ld-hosted-crt.c" \ 385 -o "$work/ld-hosted-sr/lib/Scrt1.o" > "$work/ld-hosted-scrt.out" \ 386 2> "$work/ld-hosted-scrt.err" && 387 "$KIT" cc -target x86_64-linux -fno-PIC -c "$work/ld-hosted-crt.c" \ 388 -o "$work/ld-hosted-sr/lib/crt1.o" > "$work/ld-hosted-crt1.out" \ 389 2> "$work/ld-hosted-crt1.err" && 390 "$KIT" cc -target x86_64-linux -c "$work/ld-hosted-crti.c" \ 391 -o "$work/ld-hosted-sr/lib/crti.o" > "$work/ld-hosted-crti.out" \ 392 2> "$work/ld-hosted-crti.err" && 393 "$KIT" cc -target x86_64-linux -c "$work/ld-hosted-crtn.c" \ 394 -o "$work/ld-hosted-sr/lib/crtn.o" > "$work/ld-hosted-crtn.out" \ 395 2> "$work/ld-hosted-crtn.err" && 396 "$KIT" cc -target x86_64-linux -fPIC -c "$work/ld-hosted-libc.c" \ 397 -o "$work/ld-hosted-libc-pic.o" > "$work/ld-hosted-libc-pic.out" \ 398 2> "$work/ld-hosted-libc-pic.err" && 399 "$KIT" ld -shared -nostdlib -e libc_marker "$work/ld-hosted-libc-pic.o" \ 400 -o "$work/ld-hosted-sr/lib/libc.so" > "$work/ld-hosted-so.out" \ 401 2> "$work/ld-hosted-so.err" && 402 "$KIT" cc -target x86_64-linux -fno-PIC -c "$work/ld-hosted-libc.c" \ 403 -o "$work/ld-hosted-libc-static.o" > "$work/ld-hosted-libc-static.out" \ 404 2> "$work/ld-hosted-libc-static.err" && 405 "$KIT" ar rc "$work/ld-hosted-sr/lib/libc.a" \ 406 "$work/ld-hosted-libc-static.o" > "$work/ld-hosted-ar.out" \ 407 2> "$work/ld-hosted-ar.err"; then 408 if "$KIT" ld --support-dir "$work/ld-rt-support" \ 409 --sysroot "$work/ld-hosted-sr" -pie -lc \ 410 "$work/ld-hosted-main.o" -o "$work/ld-hosted" \ 411 > "$work/ld-hosted.out" 2> "$work/ld-hosted.err" && 412 "$KIT" objdump -p "$work/ld-hosted" > "$work/ld-hosted-p.out" \ 413 2> "$work/ld-hosted-p.err" && 414 grep -q "interpreter /lib/ld-musl-x86_64.so.1" \ 415 "$work/ld-hosted-p.out" && 416 grep -q "NEEDED libc.so" "$work/ld-hosted-p.out"; then 417 ok "ld-hosted-lc-sysroot" 418 else 419 { sed 's/^/ld: /' "$work/ld-hosted.err" 2>/dev/null 420 sed 's/^/dump: /' "$work/ld-hosted-p.err" 2>/dev/null 421 sed 's/^/ | /' "$work/ld-hosted-p.out" 2>/dev/null; } \ 422 > "$work/ld-hosted.diag" 423 not_ok "ld-hosted-lc-sysroot" "$work/ld-hosted.diag" 424 fi 425 else 426 { sed 's/^/main: /' "$work/ld-hosted-main.err" 2>/dev/null 427 sed 's/^/scrt: /' "$work/ld-hosted-scrt.err" 2>/dev/null 428 sed 's/^/crt1: /' "$work/ld-hosted-crt1.err" 2>/dev/null 429 sed 's/^/crti: /' "$work/ld-hosted-crti.err" 2>/dev/null 430 sed 's/^/crtn: /' "$work/ld-hosted-crtn.err" 2>/dev/null 431 sed 's/^/so: /' "$work/ld-hosted-so.err" 2>/dev/null 432 sed 's/^/ar: /' "$work/ld-hosted-ar.err" 2>/dev/null; } \ 433 > "$work/ld-hosted-setup.diag" 434 not_ok "ld-hosted-lc-sysroot" "$work/ld-hosted-setup.diag" 435 fi 436 437 # ---- ld PIE without dynamic deps has no INTERP/DYNAMIC program headers ---- 438 cat > "$work/ld-static-pie.c" <<'SRC' 439 void _start(void) { for (;;) {} } 440 SRC 441 if "$KIT" cc -target x86_64-linux -fPIE -c "$work/ld-static-pie.c" \ 442 -o "$work/ld-static-pie.o" > "$work/ld-static-pie-cc.out" \ 443 2> "$work/ld-static-pie-cc.err" && 444 "$KIT" ld -pie -nostdlib "$work/ld-static-pie.o" \ 445 -o "$work/ld-static-pie" > "$work/ld-static-pie.out" \ 446 2> "$work/ld-static-pie.err" && 447 "$KIT" objdump -p "$work/ld-static-pie" \ 448 > "$work/ld-static-pie-p.out" 2> "$work/ld-static-pie-p.err"; then 449 if ! grep -q "interpreter " "$work/ld-static-pie-p.out" && 450 ! grep -q "^ DYNAMIC" "$work/ld-static-pie-p.out" && 451 ! grep -q "NEEDED" "$work/ld-static-pie-p.out"; then 452 ok "ld-static-pie-no-dynamic-headers" 453 else 454 sed 's/^/ | /' "$work/ld-static-pie-p.out" \ 455 > "$work/ld-static-pie.diag" 456 not_ok "ld-static-pie-no-dynamic-headers" \ 457 "$work/ld-static-pie.diag" 458 fi 459 else 460 { sed 's/^/cc: /' "$work/ld-static-pie-cc.err" 2>/dev/null 461 sed 's/^/ld: /' "$work/ld-static-pie.err" 2>/dev/null 462 sed 's/^/dump: /' "$work/ld-static-pie-p.err" 2>/dev/null; } \ 463 > "$work/ld-static-pie-setup.diag" 464 not_ok "ld-static-pie-no-dynamic-headers" \ 465 "$work/ld-static-pie-setup.diag" 466 fi 467 468 # ---- ld -no-pie cancels an earlier -pie and emits ET_EXEC ---- 469 if "$KIT" ld -pie -no-pie -nostdlib "$work/ld-static-pie.o" \ 470 -o "$work/ld-no-pie" > "$work/ld-no-pie.out" \ 471 2> "$work/ld-no-pie.err"; then 472 e_type=$(od -An -tx1 -j 16 -N 2 "$work/ld-no-pie" | tr -d ' \n') 473 if [ "$e_type" = "0200" ]; then 474 ok "ld-no-pie-cancels-pie" 475 else 476 printf 'e_type bytes=%s want=0200\n' "$e_type" \ 477 > "$work/ld-no-pie.diag" 478 not_ok "ld-no-pie-cancels-pie" "$work/ld-no-pie.diag" 479 fi 480 else 481 not_ok "ld-no-pie-cancels-pie" "$work/ld-no-pie.err" 482 fi 483 484 # ---- -no-pie cancels an earlier -pie and emits ET_EXEC ---- 485 cat > "$work/cc-no-pie.c" <<'SRC' 486 void _start(void) { for (;;) {} } 487 SRC 488 if "$KIT" cc -target x86_64-linux -nostdlib -pie -no-pie \ 489 "$work/cc-no-pie.c" -o "$work/cc-no-pie" \ 490 > "$work/cc-no-pie.out" 2> "$work/cc-no-pie.err"; then 491 e_type=$(od -An -tx1 -j 16 -N 2 "$work/cc-no-pie" | tr -d ' \n') 492 if [ "$e_type" = "0200" ]; then 493 ok "cc-no-pie-cancels-pie" 494 else 495 printf 'e_type bytes=%s want=0200\n' "$e_type" \ 496 > "$work/cc-no-pie.diag" 497 not_ok "cc-no-pie-cancels-pie" "$work/cc-no-pie.diag" 498 fi 499 else 500 not_ok "cc-no-pie-cancels-pie" "$work/cc-no-pie.err" 501 fi 502 503 # ---- objdump -x aggregate (sections + symbol table) ---- 504 if "$KIT" objdump -x "$work/main.o" \ 505 > "$work/objdump-x.out" 2> "$work/objdump-x.err" && 506 grep -q "Sections:" "$work/objdump-x.out" && 507 grep -q "SYMBOL TABLE:" "$work/objdump-x.out"; then 508 ok "objdump-x-aggregate" 509 else 510 not_ok "objdump-x-aggregate" "$work/objdump-x.err" 511 fi 512 513 # ---- Mach-O -ffunction-sections atom coalescing ---- 514 { 515 printf 'int live(void) { return 0; }\n' 516 i=0 517 while [ "$i" -lt 300 ]; do 518 printf 'int f%s(void) { return %s; }\n' "$i" "$i" 519 i=$((i + 1)) 520 done 521 } > "$work/macho-many.c" 522 if "$KIT" cc -target arm64-apple-macos -O1 -ffunction-sections \ 523 -c "$work/macho-many.c" -o "$work/macho-many.o" \ 524 > "$work/macho-many-cc.out" 2> "$work/macho-many-cc.err" && 525 "$KIT" objdump -h -t "$work/macho-many.o" \ 526 > "$work/macho-many-dump.out" 2> "$work/macho-many-dump.err"; then 527 macho_flags=$(od -An -tx4 -j 24 -N 4 "$work/macho-many.o" 2>/dev/null | 528 tr -d '[:space:]') 529 macho_sec_count=$(awk ' 530 /^SYMBOL TABLE:/ { in_sec = 0 } 531 in_sec && /^ *[0-9]+ / { n++ } 532 /^Sections:/ { in_sec = 1 } 533 END { print n + 0 } 534 ' "$work/macho-many-dump.out") 535 if [ "$macho_sec_count" -le 8 ] && 536 ! grep -q '\*UND\*' "$work/macho-many-dump.out" && 537 [ "$macho_flags" = "00002000" ]; then 538 ok "macho-function-sections-atoms" 539 else 540 { printf 'sections=%s flags=%s; unexpected UND/flags/fanout\n' \ 541 "$macho_sec_count" "$macho_flags" 542 sed 's/^/ | /' "$work/macho-many-dump.out"; } \ 543 > "$work/macho-many.diag" 544 not_ok "macho-function-sections-atoms" "$work/macho-many.diag" 545 fi 546 else 547 { sed 's/^/cc: /' "$work/macho-many-cc.err" 548 sed 's/^/dump: /' "$work/macho-many-dump.err"; } > "$work/macho-many-setup.diag" 549 not_ok "macho-function-sections-atoms" "$work/macho-many-setup.diag" 550 fi 551 552 # ---- run: JIT compile a source + archive on demand, exit status is the result ---- 553 cat > "$work/run-main.c" <<'SRC' 554 int add42(int); 555 int main(void) { return add42(0); } 556 SRC 557 cat > "$work/run-lib.c" <<'SRC' 558 int add42(int x) { return x + 42; } 559 SRC 560 561 if "$KIT" cc -I"$repo_root/rt/include" \ 562 -c "$work/run-lib.c" -o "$work/run-lib.o" \ 563 > "$work/run-lib.out" 2> "$work/run-lib.err" && 564 "$KIT" ar rcs "$work/librun.a" "$work/run-lib.o" \ 565 > "$work/run-ar.out" 2> "$work/run-ar.err"; then 566 "$KIT" run -I"$repo_root/rt/include" \ 567 "$work/run-main.c" "$work/librun.a" \ 568 > "$work/run-archive.out" 2> "$work/run-archive.err" 569 run_status=$? 570 if [ "$run_status" -eq 42 ]; then 571 ok "run-source-archive-demand" 572 else 573 { printf 'status %s, want 42\n' "$run_status" 574 sed 's/^/ | /' "$work/run-archive.err"; } > "$work/run-archive.diag" 575 not_ok "run-source-archive-demand" "$work/run-archive.diag" 576 fi 577 else 578 { sed 's/^/lib: /' "$work/run-lib.err" 579 sed 's/^/ar: /' "$work/run-ar.err"; } > "$work/run-setup.diag" 580 not_ok "run-source-archive-demand" "$work/run-setup.diag" 581 fi 582 583 # ---- run --script: #! shebang interpreter, argv passthrough, implicit -lc ---- 584 # Make a .c file executable with a `#!` line and run it directly. The kernel 585 # launches the interpreter and appends the script path + the user's args, so 586 # `--script` names the sole source and routes everything after it to the 587 # program's argv. `--script` implies -lc; under the JIT that only needs a libc 588 # sysroot for #include resolution, so probe for a usable one and skip if none. 589 shebang_sysroot="" 590 if command -v xcrun >/dev/null 2>&1; then 591 shebang_sysroot="$(xcrun --show-sdk-path 2>/dev/null || true)" 592 fi 593 shebang_sysroot="${KIT_TEST_SYSROOT:-$shebang_sysroot}" 594 595 cat > "$work/shebang-probe.c" <<'SRC' 596 #include <stdio.h> 597 int main(void) { return 0; } 598 SRC 599 shebang_ok=0 600 if [ -n "$shebang_sysroot" ] && 601 "$KIT" run --sysroot "$shebang_sysroot" --script "$work/shebang-probe.c" \ 602 > "$work/shebang-probe.out" 2> "$work/shebang-probe.err"; then 603 shebang_ok=1 604 fi 605 606 if [ "$shebang_ok" -eq 1 ]; then 607 cat > "$work/greet.c" <<SHEBANG 608 #!/usr/bin/env -S $KIT run --sysroot $shebang_sysroot --script 609 #include <stdio.h> 610 #include <stdlib.h> 611 int main(int argc, char** argv) { 612 if (argc < 2) { fprintf(stderr, "usage: greet N\n"); return 2; } 613 printf("greet:%d\n", atoi(argv[1]) + 1); 614 return 0; 615 } 616 SHEBANG 617 chmod +x "$work/greet.c" 618 619 # Execute the C file directly. The arg "41" reaches the program (not 620 # `kit run`); -lc is implied so <stdio.h>/<stdlib.h> resolve. 621 if "$work/greet.c" 41 > "$work/greet.out" 2> "$work/greet.err"; then 622 contains "run-shebang-arg" "$work/greet.out" "greet:42" 623 else 624 not_ok "run-shebang-arg" "$work/greet.err" 625 fi 626 627 # A flag-shaped program arg after the script must pass through to the 628 # program, not be parsed as a `kit run` option. atoi("-5")+1 = -4. 629 "$work/greet.c" -5 > "$work/greet-flag.out" 2> "$work/greet-flag.err" 630 greet_flag_rc=$? 631 if [ "$greet_flag_rc" -eq 0 ] && grep -q "greet:-4" "$work/greet-flag.out"; then 632 ok "run-shebang-flaglike-arg" 633 else 634 { printf 'rc=%s\n' "$greet_flag_rc" 635 sed 's/^/out: /' "$work/greet-flag.out" 636 sed 's/^/err: /' "$work/greet-flag.err"; } > "$work/greet-flag.diag" 637 not_ok "run-shebang-flaglike-arg" "$work/greet-flag.diag" 638 fi 639 else 640 skip_test "run-shebang-arg" "no usable libc sysroot (set KIT_TEST_SYSROOT)" 641 skip_test "run-shebang-flaglike-arg" "no usable libc sysroot (set KIT_TEST_SYSROOT)" 642 fi 643 644 # ---- hosted sysroot defaulting: --sysroot is not always required ---------- 645 # When hosted libc is engaged (-lc) but no --sysroot is given, the sysroot is 646 # resolved from KIT_SYSROOT (the single, portable env override — any target) or 647 # an on-disk host probe (auto-discovery; native target only, never cross). 648 cat > "$work/hosted-min.c" <<'SRC' 649 int main(void) { return 0; } 650 SRC 651 cat > "$work/hosted-hello.c" <<'SRC' 652 #include <stdio.h> 653 int main(void) { puts("hosted-autoprobe"); return 0; } 654 SRC 655 656 # Tier 1 — KIT_SYSROOT supplies the sysroot. A Darwin cross-target with -c needs 657 # only the profile to resolve (defines/includes, no link inputs), so a bare 658 # existing directory suffices and this runs on every host. 659 mkdir -p "$work/fake-sdk/usr/include" 660 if KIT_SYSROOT="$work/fake-sdk" "$KIT" cc -c -target arm64-macos -lc \ 661 "$work/hosted-min.c" -o "$work/hosted-kitsysroot.o" \ 662 > "$work/hosted-kitsysroot.out" 2> "$work/hosted-kitsysroot.err"; then 663 ok "cc-hosted-kit-sysroot-env" 664 else 665 not_ok "cc-hosted-kit-sysroot-env" "$work/hosted-kitsysroot.err" 666 fi 667 668 # The host probe must NOT fire for a cross-compile: a foreign-OS target with no 669 # --sysroot/KIT_SYSROOT must still error rather than borrow the host's libc. 670 # Pick a target whose OS differs from the host so the native-only gate always 671 # blocks the probe; scrub the env override so only an (incorrect) auto-probe 672 # could apply. 673 case "$(uname -s)" in 674 Linux) cross_target="arm64-macos" ;; # macOS is never the Linux host's OS 675 *) cross_target="x86_64-linux" ;; # Linux is never the macOS/other host's OS 676 esac 677 if env -u KIT_SYSROOT "$KIT" cc -c -target "$cross_target" -lc \ 678 "$work/hosted-min.c" -o "$work/hosted-cross.o" \ 679 > "$work/hosted-cross.out" 2> "$work/hosted-cross.err"; then 680 { echo "cross-compile unexpectedly resolved a sysroot via auto-probe" 681 sed 's/^/out: /' "$work/hosted-cross.out"; } > "$work/hosted-cross.diag" 682 not_ok "cc-hosted-cross-no-autoprobe" "$work/hosted-cross.diag" 683 else 684 ok "cc-hosted-cross-no-autoprobe" 685 fi 686 687 # Tier 2 — host probe end-to-end: no --sysroot, no KIT_SYSROOT, native target. 688 # Compile + link + run a real hosted program so crt/libc binding is exercised, 689 # not just header/define resolution. Runs where a host libc is discoverable 690 # (a macOS host with the CLT/Xcode SDK, or a Linux host with libc headers); 691 # skip otherwise (e.g. FreeBSD here is untested). 692 autoprobe_ok=0 693 case "$(uname -s)" in 694 Darwin) 695 { [ -d /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk ] || 696 [ -d "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" ]; } && 697 autoprobe_ok=1 ;; 698 Linux) 699 [ -e /usr/include/stdio.h ] && autoprobe_ok=1 ;; 700 esac 701 if [ "$autoprobe_ok" -eq 1 ]; then 702 if env -u KIT_SYSROOT "$KIT" cc -lc "$work/hosted-hello.c" \ 703 -o "$work/hosted-autoprobe" \ 704 > "$work/hosted-autoprobe.out" 2> "$work/hosted-autoprobe.err" && 705 "$work/hosted-autoprobe" > "$work/hosted-autoprobe.run" 2>&1 && 706 grep -q "hosted-autoprobe" "$work/hosted-autoprobe.run"; then 707 ok "cc-hosted-sysroot-autoprobe" 708 else 709 { sed 's/^/err: /' "$work/hosted-autoprobe.err" 710 [ -f "$work/hosted-autoprobe.run" ] && 711 sed 's/^/run: /' "$work/hosted-autoprobe.run"; } \ 712 > "$work/hosted-autoprobe.diag" 713 not_ok "cc-hosted-sysroot-autoprobe" "$work/hosted-autoprobe.diag" 714 fi 715 else 716 skip_test "cc-hosted-sysroot-autoprobe" "no discoverable host libc to probe" 717 fi 718 719 # ---- archive link order is enforced (def after ref vs ref after def) ---- 720 cat > "$work/order-main.c" <<'SRC' 721 int foo(void); 722 int _start(void) { return foo(); } 723 SRC 724 cat > "$work/order-foo.c" <<'SRC' 725 int foo(void) { return 0; } 726 SRC 727 728 if "$KIT" cc -target x86_64-linux -c "$work/order-main.c" -o "$work/order-main.o" \ 729 > "$work/order-main.out" 2> "$work/order-main.err" && 730 "$KIT" cc -target x86_64-linux -c "$work/order-foo.c" -o "$work/order-foo.o" \ 731 > "$work/order-foo.out" 2> "$work/order-foo.err" && 732 "$KIT" ar rc "$work/libfoo.a" "$work/order-foo.o" \ 733 > "$work/order-ar.out" 2> "$work/order-ar.err"; then 734 if "$KIT" cc -target x86_64-linux -L"$work" "$work/order-main.o" -lfoo \ 735 -o "$work/order-right" > "$work/order-right.out" 2> "$work/order-right.err" && 736 ! "$KIT" cc -target x86_64-linux -L"$work" -lfoo "$work/order-main.o" \ 737 -o "$work/order-wrong" > "$work/order-wrong.out" 2> "$work/order-wrong.err"; then 738 ok "cc-link-archive-order" 739 else 740 { sed 's/^/right| /' "$work/order-right.err" 741 sed 's/^/wrong| /' "$work/order-wrong.err"; } > "$work/order.diag" 742 not_ok "cc-link-archive-order" "$work/order.diag" 743 fi 744 else 745 { sed 's/^/main: /' "$work/order-main.err" 746 sed 's/^/foo: /' "$work/order-foo.err" 747 sed 's/^/ar: /' "$work/order-ar.err"; } > "$work/order-setup.diag" 748 not_ok "cc-link-archive-order" "$work/order-setup.diag" 749 fi 750 751 # ---- rv64 cross-target end-to-end (as, cc, ld, objdump) ---- 752 # Exercises the rv64 lane of each tool the toolchain claims to support. 753 # Cross-compile-only; no qemu/native exec required. 754 cat > "$work/rv64-asm.S" <<'SRC' 755 .text 756 .globl rv64_entry 757 rv64_entry: 758 li a0, 7 759 ret 760 SRC 761 if "$KIT" as -target riscv64-linux "$work/rv64-asm.S" -o "$work/rv64-asm.o" \ 762 > "$work/rv64-as.out" 2> "$work/rv64-as.err"; then 763 if "$KIT" objdump -h "$work/rv64-asm.o" \ 764 > "$work/rv64-as-h.out" 2> "$work/rv64-as-h.err" && 765 grep -q "elf64-riscv64" "$work/rv64-as-h.out"; then 766 ok "rv64-as-cc-objdump-elf" 767 else 768 cp "$work/rv64-as-h.out" "$work/rv64-as-h.diag" 769 not_ok "rv64-as-cc-objdump-elf" "$work/rv64-as-h.diag" 770 fi 771 else 772 not_ok "rv64-as-cc-objdump-elf" "$work/rv64-as.err" 773 fi 774 775 cat > "$work/rv64-cc.c" <<'SRC' 776 int rv64_main(int x) { return x + 1; } 777 SRC 778 if "$KIT" cc -target riscv64-linux -c "$work/rv64-cc.c" -o "$work/rv64-cc.o" \ 779 > "$work/rv64-cc.out" 2> "$work/rv64-cc.err"; then 780 if "$KIT" objdump -d "$work/rv64-cc.o" \ 781 > "$work/rv64-cc-d.out" 2> "$work/rv64-cc-d.err" && 782 grep -q "ret" "$work/rv64-cc-d.out"; then 783 ok "rv64-cc-emits-ret" 784 else 785 cp "$work/rv64-cc-d.out" "$work/rv64-cc-d.diag" 786 not_ok "rv64-cc-emits-ret" "$work/rv64-cc-d.diag" 787 fi 788 else 789 not_ok "rv64-cc-emits-ret" "$work/rv64-cc.err" 790 fi 791 792 cat > "$work/rv64-ld-start.c" <<'SRC' 793 void _start(void) { for (;;) {} } 794 SRC 795 if "$KIT" cc -target riscv64-linux -ffreestanding -fno-PIC \ 796 -c "$work/rv64-ld-start.c" -o "$work/rv64-ld-start.o" \ 797 > "$work/rv64-ld-cc.out" 2> "$work/rv64-ld-cc.err"; then 798 if "$KIT" ld -static -e _start "$work/rv64-ld-start.o" \ 799 -o "$work/rv64-ld.exe" \ 800 > "$work/rv64-ld.out" 2> "$work/rv64-ld.err"; then 801 # ELF e_machine == EM_RISCV (243 = 0xF3) at byte offset 0x12, 802 # little-endian 16-bit field. Validates the linker emitted an 803 # rv64 ELF executable without needing objdump to parse ET_EXEC. 804 em_byte=$(od -An -tx1 -j 18 -N 1 "$work/rv64-ld.exe" | tr -d ' \n') 805 if [ "$em_byte" = "f3" ]; then 806 ok "rv64-ld-static-exe" 807 else 808 printf 'e_machine byte=%s want=f3\n' "$em_byte" \ 809 > "$work/rv64-ld.diag" 810 not_ok "rv64-ld-static-exe" "$work/rv64-ld.diag" 811 fi 812 else 813 not_ok "rv64-ld-static-exe" "$work/rv64-ld.err" 814 fi 815 else 816 not_ok "rv64-ld-static-exe" "$work/rv64-ld-cc.err" 817 fi 818 819 # ---- check: frontend-only, emits no output files ---- 820 rm -f "$work/check.o" "$work/a.out" 821 if "$KIT" check "$work/main.c" > "$work/check.out" 2> "$work/check.err"; then 822 if [ ! -e "$work/check.o" ] && [ ! -e "$work/a.out" ]; then 823 ok "check-no-output" 824 else 825 echo "unexpected output file" > "$work/check.diag" 826 not_ok "check-no-output" "$work/check.diag" 827 fi 828 else 829 not_ok "check-no-output" "$work/check.err" 830 fi 831 832 cat > "$work/check-bad.c" <<'SRC' 833 int broken( { return 0; } 834 SRC 835 if "$KIT" check "$work/check-bad.c" \ 836 > "$work/check-bad.out" 2> "$work/check-bad.err"; then 837 echo "bad source passed" > "$work/check-bad.diag" 838 not_ok "check-reports-errors" "$work/check-bad.diag" 839 else 840 if grep -q "fatal:" "$work/check-bad.err"; then 841 ok "check-reports-errors" 842 else 843 cp "$work/check-bad.err" "$work/check-bad-missing.diag" 844 not_ok "check-reports-errors" "$work/check-bad-missing.diag" 845 fi 846 fi 847 848 # ---- nm ---- 849 # Compile a fresh object for the nm/size/addr2line tests. 850 # Use aarch64-linux ELF so DWARF works for addr2line and symbols 851 # have predictable names (no Mach-O underscore prefix). 852 rm -f "$work/nm-main.o" 853 nm_obj_ok=1 854 if ! "$KIT" cc -target aarch64-linux -c "$work/main.c" -o "$work/nm-main.o" \ 855 > "$work/nm-cc.out" 2> "$work/nm-cc.err"; then 856 not_ok "nm-setup" "$work/nm-cc.err" 857 nm_obj_ok=0 858 fi 859 860 # basic: symbols in an object file 861 if [ "$nm_obj_ok" = 1 ] && 862 "$KIT" nm "$work/nm-main.o" > "$work/nm-basic.out" 2> "$work/nm-basic.err" && 863 grep -q "main" "$work/nm-basic.out" && 864 grep -q "_start" "$work/nm-basic.out"; then 865 ok "nm-basic" 866 elif [ "$nm_obj_ok" = 1 ]; then 867 not_ok "nm-basic" "$work/nm-basic.err" 868 else 869 not_ok "nm-basic" 870 fi 871 872 # nm -g: only global symbols. 873 # Source with a local (static) function and a global one. 874 cat > "$work/nm-global.c" <<'SRC' 875 static int hidden(void) { return 1; } 876 int visible(void) { return hidden(); } 877 SRC 878 if [ "$nm_obj_ok" = 1 ] && 879 "$KIT" cc -target aarch64-linux -c "$work/nm-global.c" \ 880 -o "$work/nm-global.o" > "$work/nm-global-cc.out" \ 881 2> "$work/nm-global-cc.err" && 882 "$KIT" nm "$work/nm-global.o" > "$work/nm-global-all.out" 2>/dev/null && 883 grep -q "hidden" "$work/nm-global-all.out" && 884 "$KIT" nm -g "$work/nm-global.o" > "$work/nm-global-g.out" 2>/dev/null && 885 grep -q "visible" "$work/nm-global-g.out" && 886 ! grep -q "hidden" "$work/nm-global-g.out"; then 887 ok "nm-global-only" 888 else 889 not_ok "nm-global-only" 890 fi 891 892 # nm -u: undefined only (ELF objects may have no undefined) 893 if [ "$nm_obj_ok" = 1 ] && 894 "$KIT" nm -u "$work/nm-main.o" > "$work/nm-undef.out" 2>"$work/nm-undef.err"; then 895 ok "nm-undefined-only" 896 elif [ "$nm_obj_ok" = 1 ]; then 897 not_ok "nm-undefined-only" "$work/nm-undef.err" 898 else 899 not_ok "nm-undefined-only" 900 fi 901 902 # nm on an archive 903 if [ "$nm_obj_ok" = 1 ] && 904 "$KIT" ar rcs "$work/libnmtest.a" "$work/nm-main.o" \ 905 > "$work/ar-nm.out" 2> "$work/ar-nm.err" && 906 "$KIT" nm "$work/libnmtest.a" > "$work/nm-archive.out" \ 907 2> "$work/nm-archive.err" && 908 grep -q "main" "$work/nm-archive.out"; then 909 ok "nm-archive" 910 elif [ "$nm_obj_ok" = 1 ]; then 911 not_ok "nm-archive" "$work/nm-archive.err" 912 else 913 not_ok "nm-archive" 914 fi 915 916 # nm -h (help) 917 if "$KIT" nm --help > "$work/nm-help.out" 2> "$work/nm-help.err" && 918 grep -q "USAGE" "$work/nm-help.out"; then 919 ok "nm-help" 920 else 921 not_ok "nm-help" "$work/nm-help.err" 922 fi 923 924 # ---- size ---- 925 # basic: section sizes of an object file (use nm-main.o) 926 if [ "$nm_obj_ok" = 1 ] && 927 "$KIT" size "$work/nm-main.o" > "$work/size-basic.out" 2> "$work/size-basic.err" && 928 grep -q "text" "$work/size-basic.out"; then 929 ok "size-basic" 930 elif [ "$nm_obj_ok" = 1 ]; then 931 not_ok "size-basic" "$work/size-basic.err" 932 else 933 not_ok "size-basic" 934 fi 935 936 # size -A: SysV format 937 if [ "$nm_obj_ok" = 1 ] && 938 "$KIT" size -A "$work/nm-main.o" > "$work/size-sysv.out" 2> "$work/size-sysv.err" && 939 grep -q "Total" "$work/size-sysv.out"; then 940 ok "size-sysv" 941 elif [ "$nm_obj_ok" = 1 ]; then 942 not_ok "size-sysv" "$work/size-sysv.err" 943 else 944 not_ok "size-sysv" 945 fi 946 947 # size on an archive (uses libnmtest.a from nm test) 948 if [ "$nm_obj_ok" = 1 ] && [ -f "$work/libnmtest.a" ] && 949 "$KIT" size "$work/libnmtest.a" > "$work/size-archive.out" \ 950 2> "$work/size-archive.err" && 951 grep -q "text" "$work/size-archive.out"; then 952 ok "size-archive" 953 elif [ "$nm_obj_ok" = 1 ]; then 954 not_ok "size-archive" "$work/size-archive.err" 955 else 956 not_ok "size-archive" 957 fi 958 959 # size -h (help) 960 if "$KIT" size --help > "$work/size-help.out" 2> "$work/size-help.err" && 961 grep -q "USAGE" "$work/size-help.out"; then 962 ok "size-help" 963 else 964 not_ok "size-help" "$work/size-help.err" 965 fi 966 967 # ---- addr2line ---- 968 cat > "$work/a2l.c" <<'SRC' 969 int calc(int x) { return x * 2; } 970 int main(void) { return calc(42); } 971 SRC 972 973 if [ "$nm_obj_ok" != 1 ]; then 974 not_ok "addr2line-basic" 975 not_ok "addr2line-nonzero" 976 elif "$KIT" cc -g -target aarch64-linux -c "$work/a2l.c" -o "$work/a2l.o" \ 977 > "$work/a2l-cc.out" 2> "$work/a2l-cc.err"; then 978 calc_addr=$("$KIT" nm "$work/a2l.o" 2>/dev/null | \ 979 grep " calc$" | awk '{print $1}') 980 if [ -n "$calc_addr" ] && 981 "$KIT" addr2line -e "$work/a2l.o" "$calc_addr" \ 982 > "$work/a2l-hit.out" 2> "$work/a2l-hit.err" && 983 grep -q "a2l.c" "$work/a2l-hit.out"; then 984 ok "addr2line-basic" 985 else 986 { printf 'calc_addr=%s\n' "$calc_addr" 987 sed 's/^/ | /' "$work/a2l-hit.err"; } > "$work/a2l-hit.diag" 988 not_ok "addr2line-basic" "$work/a2l-hit.diag" 989 fi 990 # addr2line should produce output (not crash) on any address 991 if "$KIT" addr2line -e "$work/a2l.o" 0x0 \ 992 > "$work/a2l-miss.out" 2> "$work/a2l-miss.err" && 993 [ -s "$work/a2l-miss.out" ]; then 994 ok "addr2line-nonzero" 995 else 996 not_ok "addr2line-nonzero" "$work/a2l-miss.err" 997 fi 998 else 999 not_ok "addr2line-basic" "$work/a2l-cc.err" 1000 not_ok "addr2line-nonzero" 1001 fi 1002 1003 # addr2line over Mach-O DWARF: the producer emits .debug_* into the 1004 # __DWARF segment as __debug_* (16-char-truncated, Apple spelling) and 1005 # the reader maps the requested ELF name onto that form, so addr2line 1006 # resolves file:line on a Mach-O object too (not just ELF). 1007 if "$KIT" cc -g -target arm64-apple-macos -c "$work/a2l.c" \ 1008 -o "$work/a2l.macho.o" > "$work/a2l-m-cc.out" 2> "$work/a2l-m-cc.err"; then 1009 m_addr=$("$KIT" nm "$work/a2l.macho.o" 2>/dev/null | \ 1010 grep " _\{0,1\}calc$" | awk '{print $1}') 1011 if [ -n "$m_addr" ] && 1012 "$KIT" addr2line -e "$work/a2l.macho.o" "$m_addr" \ 1013 > "$work/a2l-m.out" 2> "$work/a2l-m.err" && 1014 grep -q "a2l.c" "$work/a2l-m.out"; then 1015 ok "addr2line-macho" 1016 else 1017 { printf 'm_addr=%s\n' "$m_addr" 1018 sed 's/^/out| /' "$work/a2l-m.out" 1019 sed 's/^/err| /' "$work/a2l-m.err"; } > "$work/a2l-m.diag" 1020 not_ok "addr2line-macho" "$work/a2l-m.diag" 1021 fi 1022 else 1023 not_ok "addr2line-macho" "$work/a2l-m-cc.err" 1024 fi 1025 1026 # Debug-info retention through the Mach-O linker: link a self-contained 1027 # image (no SDK/libSystem needed), then resolve file:line via the same 1028 # `nm | addr2line` flow as the ELF case — exercising the __DWARF segment 1029 # the linker carried plus the reader on a linked image. nm reports 1030 # absolute vaddrs for linked Mach-O images, so its value feeds addr2line 1031 # directly. 1032 cat > "$work/a2lm.c" <<'SRC' 1033 int helper(int x) { return x * 3; } 1034 int compute(int n) { return helper(n) + n; } 1035 void mainx(void) { compute(5); } 1036 SRC 1037 if "$KIT" cc -g -target arm64-apple-macos -c "$work/a2lm.c" \ 1038 -o "$work/a2lm.o" > "$work/a2lm-cc.out" 2> "$work/a2lm-cc.err" && 1039 "$KIT" ld -o "$work/a2lm.exe" "$work/a2lm.o" -e mainx \ 1040 > "$work/a2lm-ld.out" 2> "$work/a2lm-ld.err"; then 1041 cm_addr=$("$KIT" nm "$work/a2lm.exe" 2>/dev/null | \ 1042 grep " _\{0,1\}compute$" | awk '{print $1}') 1043 if "$KIT" objdump -h "$work/a2lm.exe" 2>/dev/null \ 1044 | grep -q '__DWARF,__debug_info' && 1045 [ -n "$cm_addr" ] && 1046 "$KIT" addr2line -e "$work/a2lm.exe" "0x$cm_addr" \ 1047 > "$work/a2lm-a2l.out" 2> "$work/a2lm-a2l.err" && 1048 grep -q "a2lm.c" "$work/a2lm-a2l.out"; then 1049 ok "addr2line-macho-linked" 1050 else 1051 printf 'compute=%s: %s\n' "$cm_addr" \ 1052 "$(cat "$work/a2lm-a2l.out" 2>/dev/null)" > "$work/a2lm.diag" 1053 not_ok "addr2line-macho-linked" "$work/a2lm.diag" 1054 fi 1055 else 1056 not_ok "addr2line-macho-linked" "$work/a2lm-ld.err" 1057 fi 1058 1059 # addr2line -h (help) 1060 if "$KIT" addr2line --help > "$work/a2l-help.out" 2> "$work/a2l-help.err" && 1061 grep -q "USAGE" "$work/a2l-help.out"; then 1062 ok "addr2line-help" 1063 else 1064 not_ok "addr2line-help" "$work/a2l-help.err" 1065 fi 1066 1067 # --emit=ir: dump the semantic CG IR tape (requires -O1+). 1068 cat > "$work/ir.c" <<'SRC' 1069 int add(int a, int b) { 1070 int c = a + b; 1071 return c * 2; 1072 } 1073 SRC 1074 1075 if "$KIT" cc -O1 --emit=ir -c "$work/ir.c" -o "$work/ir.out" \ 1076 > "$work/ir-emit.out" 2> "$work/ir-emit.err" && 1077 grep -q "^func sym#" "$work/ir.out" && 1078 grep -q "binop" "$work/ir.out" && 1079 grep -q "iadd" "$work/ir.out" && 1080 grep -q "ret values=\[" "$work/ir.out"; then 1081 ok "cc-emit-ir" 1082 else 1083 not_ok "cc-emit-ir" "$work/ir-emit.err" 1084 fi 1085 1086 # --emit=ir without -O1 must be rejected (no IR tape is recorded at -O0). 1087 if "$KIT" cc --emit=ir -c "$work/ir.c" -o "$work/ir-o0.out" \ 1088 > "$work/ir-o0.out.log" 2> "$work/ir-o0.err"; then 1089 echo "expected failure at -O0" > "$work/ir-o0.diag" 1090 not_ok "cc-emit-ir-requires-opt" "$work/ir-o0.diag" 1091 elif grep -q "requires -O1" "$work/ir-o0.err"; then 1092 ok "cc-emit-ir-requires-opt" 1093 else 1094 cp "$work/ir-o0.err" "$work/ir-o0-wrong.diag" 1095 not_ok "cc-emit-ir-requires-opt" "$work/ir-o0-wrong.diag" 1096 fi 1097 1098 # ---- install: symlink the kit tools into a dir ---- 1099 # Default set: toolchain + standard-named byte utils, each link -> the binary. 1100 inst_dir="$work/inst" 1101 run_ok "install-default" "$KIT" install "$inst_dir" 1102 for t in cc cpp as ld ar ranlib strip objcopy objdump nm size addr2line \ 1103 strings xxd cmp sha256sum b2sum crc32 gzip gunzip lz4 lz4c; do 1104 assert_file_exists "install-has-$t" "$inst_dir/$t" 1105 done 1106 is_executable "install-cc-executable" "$inst_dir/cc" 1107 # A symlink to the binary has identical bytes, and dispatches by basename. 1108 same_file "install-cc-points-at-kit" "$KIT" "$inst_dir/cc" 1109 run_ok "install-symlink-dispatches" "$inst_dir/cc" --help 1110 1111 # Non-default tools (e.g. mc) are excluded unless --all selects everything. 1112 if [ -e "$inst_dir/mc" ]; then 1113 echo "mc present in the default set" > "$work/install-mc.diag" 1114 not_ok "install-default-excludes-mc" "$work/install-mc.diag" 1115 else 1116 ok "install-default-excludes-mc" 1117 fi 1118 inst_all="$work/inst-all" 1119 run_ok "install-all" "$KIT" install --all "$inst_all" 1120 assert_file_exists "install-all-has-mc" "$inst_all/mc" 1121 1122 # Existing entries are an error without -f, replaced with it. 1123 run_fail "install-existing-without-force" "$KIT" install "$inst_dir" 1124 run_ok "install-existing-with-force" "$KIT" install -f "$inst_dir" 1125 1126 # An explicit tool list installs exactly those names; an unknown name and a 1127 # missing directory are usage errors. 1128 inst_pick="$work/inst-pick" 1129 run_ok "install-explicit" "$KIT" install "$inst_pick" nm ld 1130 assert_file_exists "install-explicit-nm" "$inst_pick/nm" 1131 if [ -e "$inst_pick/cc" ]; then 1132 echo "cc installed despite an explicit 'nm ld' list" \ 1133 > "$work/install-pick.diag" 1134 not_ok "install-explicit-only-listed" "$work/install-pick.diag" 1135 else 1136 ok "install-explicit-only-listed" 1137 fi 1138 run_fail "install-unknown-tool" "$KIT" install "$work/inst-bad" bogustool 1139 run_fail "install-missing-dir" "$KIT" install 1140 1141 # A hard link shares the binary's inode (the `-ef` test compares st_dev+st_ino). 1142 inst_hard="$work/inst-hard" 1143 run_ok "install-hardlink" "$KIT" install -H "$inst_hard" nm 1144 if [ "$inst_hard/nm" -ef "$KIT" ]; then 1145 ok "install-hardlink-same-inode" 1146 else 1147 echo "hard link does not share the binary's inode" \ 1148 > "$work/install-hard.diag" 1149 not_ok "install-hardlink-same-inode" "$work/install-hard.diag" 1150 fi 1151 1152 # Dry-run reports intent but changes nothing (the target dir stays absent). 1153 inst_dry="$work/inst-dry" 1154 run_ok "install-dry-run" "$KIT" install -n "$inst_dry" 1155 if [ -e "$inst_dry" ]; then 1156 echo "dry-run created $inst_dry" > "$work/install-dry.diag" 1157 not_ok "install-dry-run-no-changes" "$work/install-dry.diag" 1158 else 1159 ok "install-dry-run-no-changes" 1160 fi 1161 1162 # ---- kit run: auto-backtrace on a fatal fault ---------------------------- 1163 # `kit run` executes the JITed entry in-process; on a fatal fault it installs a 1164 # guard that walks the frame-pointer chain at the crash point and prints a 1165 # symbolized backtrace to stderr, then exits non-zero (128 + signal). Gated on 1166 # the host being able to natively `kit run` at all (cross-arch hosts can't), so 1167 # probe with a trivial program first. 1168 cat > "$work/run-probe.c" <<'SRC' 1169 int main(void) { return 0; } 1170 SRC 1171 if "$KIT" run "$work/run-probe.c" > "$work/run-probe.out" 2> "$work/run-probe.err"; then 1172 cat > "$work/run-crash.c" <<'SRC' 1173 __attribute__((noinline)) void bt_leaf(int* p) { *p = 42; } 1174 __attribute__((noinline)) void bt_mid(void) { bt_leaf((int*)0); } 1175 __attribute__((noinline)) void bt_root(void) { bt_mid(); } 1176 int main(void) { bt_root(); return 0; } 1177 SRC 1178 "$KIT" run -g "$work/run-crash.c" \ 1179 > "$work/run-crash.out" 2> "$work/run-crash.err" 1180 run_crash_rc=$? 1181 # A clean exit would mean the guard missed the fault. 1182 if [ "$run_crash_rc" -ne 0 ]; then 1183 ok "run-backtrace-nonzero-exit" 1184 else 1185 { printf 'rc=%s\n' "$run_crash_rc"; cat "$work/run-crash.err"; } \ 1186 > "$work/run-crash.diag" 1187 not_ok "run-backtrace-nonzero-exit" "$work/run-crash.diag" 1188 fi 1189 # The symbolized chain (innermost first) must reach every kit frame with a 1190 # source location; outer host-runtime frames are truncated at the image edge. 1191 contains "run-backtrace-leaf" "$work/run-crash.err" "bt_leaf" 1192 contains "run-backtrace-mid" "$work/run-crash.err" "bt_mid" 1193 contains "run-backtrace-root" "$work/run-crash.err" "bt_root" 1194 contains "run-backtrace-source" "$work/run-crash.err" "run-crash.c" 1195 else 1196 skip_test "run-backtrace-nonzero-exit" "host cannot natively kit-run" 1197 skip_test "run-backtrace-leaf" "host cannot natively kit-run" 1198 skip_test "run-backtrace-mid" "host cannot natively kit-run" 1199 skip_test "run-backtrace-root" "host cannot natively kit-run" 1200 skip_test "run-backtrace-source" "host cannot natively kit-run" 1201 fi 1202 1203 kit_summary driver-cc 1204 kit_exit