kit

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

run.sh (10947B)


      1 #!/usr/bin/env bash
      2 # test/elf/run.sh — ELF object-file fidelity tests, on the shared corpus
      3 # harness (test/lib/kit_corpus.sh).
      4 #
      5 # Scope: read/write/roundtrip of relocatable ELF .o files only. Linker
      6 # and exe behavior live in test/link/ — do not duplicate them here.
      7 #
      8 # Layers — each is a SEQUENTIAL corpus (its own kit_corpus_run, single lane,
      9 # no opt axis), NOT a parallel lane within a case:
     10 #
     11 #   A. test/elf/unit/*.c       hand-built ObjBuilder roundtrip tests. Each .c
     12 #                              links against build/libkit.a, runs, exits 0
     13 #                              on success.  (lane A)
     14 #   B. test/elf/cases/*.c      clang-oracle structural roundtrip tests:
     15 #                                 clang -c case.c -> golden.o
     16 #                                 kit-roundtrip golden.o -> rt.o
     17 #                                 normalized readelf diff golden.o vs rt.o.
     18 #                              Honors per-case NN_name.targets tuple
     19 #                              applicability (engine SKIP-NA) and per-case
     20 #                              NN_name.cflags extra compiler flags.  (lane B)
     21 #   C. test/elf/bad/*.elf      malformed inputs; expect kit-roundtrip to
     22 #                              fail (nonzero, non-signal) with a substring from
     23 #                              <name>.expect on stderr.  (lane C)
     24 #
     25 # Tools detected: clang, llvm-readelf/readelf, python3. Missing tools cause the
     26 # dependent layer's cases to be SKIPped, not failed. Set KIT_TEST_ALLOW_SKIP=1
     27 # to allow the harness to exit 0 with skips; otherwise any skip makes the run
     28 # fail (so CI catches a silently degraded run).
     29 #
     30 # Parallelism:
     31 #   default                 run in parallel with a capped CPU-count default.
     32 #   KIT_TEST_JOBS=N       run up to N cases per layer concurrently.
     33 #   KIT_TEST_JOBS=auto    same as the default.
     34 #   KIT_ELF_PARALLEL=0    force serial dispatch.
     35 # Every lane hook writes only under $KIT_WORK and records only via kit_*, so the
     36 # console summary is identical serial or parallel; per-case command output for
     37 # a failure is surfaced by the engine in deterministic index order.
     38 
     39 set -u
     40 
     41 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
     42 TEST_DIR="$ROOT/test/elf"
     43 BUILD_DIR="$ROOT/build/test"
     44 LIB_AR="$ROOT/build/libkit.a"
     45 NORMALIZE="$TEST_DIR/normalize.py"
     46 
     47 export KIT_LIB_DIR="$ROOT/test/lib"
     48 # shellcheck source=../lib/kit_corpus.sh
     49 . "$ROOT/test/lib/kit_corpus.sh"
     50 
     51 mkdir -p "$BUILD_DIR"
     52 
     53 KIT_BIN="${KIT:-$ROOT/build/kit}"
     54 PAR="${KIT_ELF_PARALLEL:-1}"
     55 
     56 # ----- tool detection ----------------------------------------------------
     57 
     58 have_clang=0
     59 have_llvm_readelf=0
     60 have_python3=0
     61 
     62 command -v clang                >/dev/null 2>&1 && have_clang=1
     63 command -v llvm-readelf         >/dev/null 2>&1 && have_llvm_readelf=1
     64 command -v readelf              >/dev/null 2>&1 && have_llvm_readelf=1
     65 command -v python3              >/dev/null 2>&1 && have_python3=1
     66 
     67 # ----- locate kit-roundtrip -------------------------------------------
     68 # Built as a Make target (mk/test.mk) so it picks up libkit.a changes.
     69 
     70 ROUNDTRIP_BIN="$BUILD_DIR/kit-roundtrip"
     71 roundtrip_ok=0
     72 [ -x "$ROUNDTRIP_BIN" ] && roundtrip_ok=1
     73 
     74 # ----- Layer A toolchain wiring ------------------------------------------
     75 
     76 UNIT_CC="${CC:-clang}"
     77 UNIT_SYSROOT_FLAGS=()
     78 unit_sysroot="$(xcrun --show-sdk-path 2>/dev/null || true)"
     79 [ -n "$unit_sysroot" ] && UNIT_SYSROOT_FLAGS=(-isysroot "$unit_sysroot")
     80 UNIT_EXTRA_CFLAGS=()
     81 UNIT_EXTRA_LDFLAGS=()
     82 if [ -n "${KIT_ELF_UNIT_CFLAGS:-}" ]; then
     83     # shellcheck disable=SC2206
     84     UNIT_EXTRA_CFLAGS=(${KIT_ELF_UNIT_CFLAGS})
     85 fi
     86 if [ -n "${KIT_ELF_UNIT_LDFLAGS:-}" ]; then
     87     # shellcheck disable=SC2206
     88     UNIT_EXTRA_LDFLAGS=(${KIT_ELF_UNIT_LDFLAGS})
     89 fi
     90 
     91 # ----- Layer B target wiring ---------------------------------------------
     92 # Map (KIT_TEST_ARCH, KIT_TEST_OBJ) (defaults aa64+elf) to the clang
     93 # `--target=` triple the Layer B golden objects are compiled against.
     94 # kit-roundtrip then detects the input's e_machine / Mach-O cputype and
     95 # constructs a matching KitTargetSpec, so the readelf/objdump diff stays
     96 # apples-to-apples per target.
     97 case "${KIT_TEST_OBJ:-elf}" in
     98     elf)
     99         case "${KIT_TEST_ARCH:-aa64}" in
    100             aa64|aarch64|arm64)   CLANG_TARGET="aarch64-linux-gnu" ;;
    101             x64|x86_64|amd64)     CLANG_TARGET="x86_64-linux-gnu" ;;
    102             rv64|riscv64)         CLANG_TARGET="riscv64-linux-gnu" ;;
    103             *) printf 'unknown KIT_TEST_ARCH=%s\n' "${KIT_TEST_ARCH}" >&2; exit 2 ;;
    104         esac
    105         ;;
    106     macho)
    107         case "${KIT_TEST_ARCH:-aa64}" in
    108             aa64|aarch64|arm64)   CLANG_TARGET="arm64-apple-macos" ;;
    109             x64|x86_64|amd64)     CLANG_TARGET="x86_64-apple-macos" ;;
    110             *) printf 'KIT_TEST_OBJ=macho: unsupported arch %s\n' "${KIT_TEST_ARCH}" >&2; exit 2 ;;
    111         esac
    112         ;;
    113     *) printf 'unknown KIT_TEST_OBJ=%s\n' "${KIT_TEST_OBJ}" >&2; exit 2 ;;
    114 esac
    115 
    116 CUR_TUPLE="${KIT_TEST_ARCH:-aa64}-${KIT_TEST_OBJ:-elf}"
    117 
    118 # ----- header summary ----------------------------------------------------
    119 
    120 printf 'kit ELF test harness\n'
    121 printf '  clang:           %s\n' "$([ $have_clang -eq 1 ] && echo yes || echo no)"
    122 printf '  llvm-readelf:    %s\n' "$([ $have_llvm_readelf -eq 1 ] && echo yes || echo no)"
    123 printf '  python3:         %s\n' "$([ $have_python3 -eq 1 ] && echo yes || echo no)"
    124 printf '  kit-roundtrip: %s\n' "$([ $roundtrip_ok -eq 1 ] && echo found || echo "MISSING — run \"make $ROUNDTRIP_BIN\"")"
    125 printf '  target:          %s (tuple %s)\n' "$CLANG_TARGET" "$CUR_TUPLE"
    126 printf '\n'
    127 
    128 # ----- Layer A lane: unit/*.c --------------------------------------------
    129 # Build a native binary against libkit.a, run it, expect exit 0. All
    130 # artifacts (binary + logs) live under $KIT_WORK for parallel safety.
    131 kit_lane_A() {
    132     local name="unit/$KIT_NAME"
    133     if [ ! -f "$LIB_AR" ]; then
    134         kit_skip "$name" "build/libkit.a missing"; return
    135     fi
    136 
    137     local bin="$KIT_WORK/$KIT_BASE"
    138     local build_log="$KIT_WORK/build.log"
    139     local out_log="$KIT_WORK/out.log"
    140 
    141     if ! "$UNIT_CC" -std=c11 -Wall -Wextra -Werror \
    142         "${UNIT_EXTRA_CFLAGS[@]}" \
    143         "${UNIT_SYSROOT_FLAGS[@]}" \
    144         -I"$ROOT/include" -I"$ROOT/src" -I"$ROOT/test" \
    145         "$KIT_SRC" "$LIB_AR" "${UNIT_EXTRA_LDFLAGS[@]}" -o "$bin" \
    146         2> "$build_log"; then
    147         kit_fail "$name" "build failed; see $build_log"
    148         return
    149     fi
    150 
    151     if "$bin" > "$out_log" 2>&1; then
    152         kit_pass "$name"
    153     else
    154         kit_fail "$name"
    155         sed 's/^/    | /' "$out_log"
    156     fi
    157 }
    158 
    159 # ----- Layer B lane: cases/*.c -------------------------------------------
    160 # clang -c -> golden.o ; kit-roundtrip -> rt.o ; normalized readelf diff.
    161 # Whole-case .targets applicability is handled by the engine (SKIP-NA) via
    162 # KIT_TARGETS_EXT. Per-case .cflags are read here, KIT_WORK-confined.
    163 kit_lane_B() {
    164     local name="cases/$KIT_NAME"
    165 
    166     # Per-case skip reasons (missing tooling).
    167     if [ $roundtrip_ok -ne 1 ];      then kit_skip "$name" "kit-roundtrip not built"; return; fi
    168     if [ $have_clang -ne 1 ];        then kit_skip "$name" "clang missing";             return; fi
    169     if [ $have_llvm_readelf -ne 1 ]; then kit_skip "$name" "llvm-readelf missing";      return; fi
    170     if [ $have_python3 -ne 1 ];      then kit_skip "$name" "python3 missing";           return; fi
    171 
    172     # Per-case extra compiler flags: NN_name.cflags alongside the .c passes
    173     # additional flags (e.g. -ffunction-sections, -x c++). Tokenized on shell
    174     # whitespace, matching the original harness convention.
    175     local extra_cflags=() cflags_file tok
    176     cflags_file="${KIT_SRC%.c}.cflags"
    177     if [ -f "$cflags_file" ]; then
    178         for tok in $(cat "$cflags_file"); do
    179             [ -n "$tok" ] && extra_cflags+=("$tok")
    180         done
    181     fi
    182 
    183     if ! clang --target="$CLANG_TARGET" -c -O0 "${extra_cflags[@]}" \
    184             "$KIT_SRC" -o "$KIT_WORK/golden.o" 2> "$KIT_WORK/clang.log"; then
    185         kit_skip "$name" "clang -c failed (cross-compile not configured?)"
    186         return
    187     fi
    188 
    189     if ! "$ROUNDTRIP_BIN" "$KIT_WORK/golden.o" "$KIT_WORK/rt.o" 2> "$KIT_WORK/roundtrip.log"; then
    190         kit_fail "$name" "roundtrip failed"
    191         sed 's/^/    | /' "$KIT_WORK/roundtrip.log"
    192         return
    193     fi
    194 
    195     # Structural diff: readelf normalized.
    196     python3 "$NORMALIZE" readelf "$KIT_WORK/golden.o" > "$KIT_WORK/golden.readelf" 2> /dev/null || true
    197     python3 "$NORMALIZE" readelf "$KIT_WORK/rt.o"     > "$KIT_WORK/rt.readelf"     2> /dev/null || true
    198     if ! diff -u "$KIT_WORK/golden.readelf" "$KIT_WORK/rt.readelf" > "$KIT_WORK/readelf.diff"; then
    199         kit_fail "$name" "readelf diff"
    200         head -n 40 "$KIT_WORK/readelf.diff" | sed 's/^/    | /'
    201         return
    202     fi
    203 
    204     kit_pass "$name"
    205 }
    206 
    207 # ----- Layer C lane: bad/*.elf -------------------------------------------
    208 # Expect kit-roundtrip to fail (nonzero, non-signal) and emit a substring
    209 # from <name>.expect on stderr.
    210 kit_lane_C() {
    211     local name="bad/$KIT_NAME"
    212     if [ $roundtrip_ok -ne 1 ]; then
    213         kit_skip "$name" "kit-roundtrip not built"; return
    214     fi
    215 
    216     local rc=0
    217     "$ROUNDTRIP_BIN" "$KIT_SRC" "$KIT_WORK/out.o" \
    218         > "$KIT_WORK/stdout.log" 2> "$KIT_WORK/stderr.log" || rc=$?
    219 
    220     if [ $rc -eq 0 ]; then
    221         kit_fail "$name" "expected nonzero exit, got 0"; return
    222     fi
    223     if [ $rc -ge 128 ]; then
    224         kit_fail "$name" "terminated by signal $((rc - 128)) — segfault?"; return
    225     fi
    226 
    227     local expect_file expect
    228     expect_file="${KIT_SRC%.elf}.expect"
    229     if [ ! -f "$expect_file" ]; then
    230         kit_fail "$name" "missing $expect_file"; return
    231     fi
    232     expect="$(cat "$expect_file")"
    233     if grep -qF -- "$expect" "$KIT_WORK/stderr.log"; then
    234         kit_pass "$name"
    235     else
    236         kit_fail "$name" "stderr did not contain: $expect"
    237         sed 's/^/    | /' "$KIT_WORK/stderr.log"
    238     fi
    239 }
    240 
    241 # ----- drive the three layers --------------------------------------------
    242 
    243 printf 'Layer A — unit tests\n'
    244 KIT_LABEL=test-elf KIT_BUILD_DIR="$BUILD_DIR/elf/unit" \
    245   KIT_CORPUS_GLOBS="$TEST_DIR/unit/*.c" KIT_CORPUS_EXT=c KIT_SIDECAR_DIR="$TEST_DIR/unit" \
    246   KIT_LANES="A" KIT_OPT_LEVELS="" KIT_TUPLES="$CUR_TUPLE" KIT_TARGETS_EXT="" \
    247   KIT_PARALLELIZABLE="$PAR" kit_corpus_run
    248 printf '\n'
    249 
    250 printf 'Layer B — clang-oracle cases\n'
    251 KIT_LABEL=test-elf KIT_BUILD_DIR="$BUILD_DIR/elf/cases" \
    252   KIT_CORPUS_GLOBS="$TEST_DIR/cases/*.c" KIT_CORPUS_EXT=c KIT_SIDECAR_DIR="$TEST_DIR/cases" \
    253   KIT_LANES="B" KIT_OPT_LEVELS="" KIT_TUPLES="$CUR_TUPLE" KIT_TARGETS_EXT=".targets" \
    254   KIT_PARALLELIZABLE="$PAR" kit_corpus_run
    255 printf '\n'
    256 
    257 printf 'Layer C — negative read_elf inputs\n'
    258 KIT_LABEL=test-elf KIT_BUILD_DIR="$BUILD_DIR/elf/bad" \
    259   KIT_CORPUS_GLOBS="$TEST_DIR/bad/*.elf" KIT_CORPUS_EXT=elf KIT_SIDECAR_DIR="$TEST_DIR/bad" \
    260   KIT_LANES="C" KIT_OPT_LEVELS="" KIT_TUPLES="$CUR_TUPLE" KIT_TARGETS_EXT="" \
    261   KIT_PARALLELIZABLE="$PAR" kit_corpus_run
    262 printf '\n'
    263 
    264 # ----- summary -----------------------------------------------------------
    265 
    266 kit_summary test-elf
    267 kit_exit