kit

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

bounce.sh (9410B)


      1 #!/usr/bin/env bash
      2 # test/bounce/bounce.sh — format-bounce stress test, on the shared corpus
      3 # harness (test/lib/kit_corpus.sh).
      4 #
      5 # Goal: stress kit's object read/write/reloc, archive, and partial-link
      6 # paths by taking one compiled program and *bouncing* its object through
      7 # chains of format conversions and toolchain transforms, then verifying
      8 # the relinked executable still produces the right answer. We already have
      9 # cross-arch run coverage elsewhere; this test exists to exercise the
     10 # format machinery, not the arches.
     11 #
     12 # Matrix = case x opt x tuple (one <arch>-linux tuple per requested arch).
     13 # For each (case, opt, arch) item the program is compiled to a relocatable
     14 # object once, then each applicable bounce CHAIN rebuilds an executable from
     15 # it (the chain axis is iterated inside the lane, like wasm's multi-result
     16 # lanes):
     17 #
     18 #   direct     baseline: link prog.o + crt.o straight through
     19 #   macho_rt   ELF -> Mach-O -> ELF (objcopy) then link
     20 #   coff_rt    ELF -> COFF   -> ELF (objcopy) then link
     21 #   tri_rt     ELF -> Mach-O -> COFF -> ELF (chained) then link
     22 #   ldr        `ld -r` partial-link merge of prog.o+crt.o, then final link
     23 #   strip_dbg  objcopy --strip-debug on prog.o, then link
     24 #   ar         `ar rcs` prog.o into an archive, on-demand link with crt.o
     25 #
     26 # Each chain's executable is queued through kit_queue_e and run in ONE batched
     27 # container exec at flush; its exit code is compared to a host-cc reference
     28 # for that case. The chain transforms + compute_ref host-reference stay
     29 # LANE-LOCAL. Every lane hook writes only under $KIT_WORK and records via kit_*,
     30 # so the runner is parallel-safe; KIT_BOUNCE_PARALLEL flips dispatch.
     31 #
     32 # Arch selection: defaults to the host-native Linux arch (fast, no
     33 # emulation). Override with KIT_BOUNCE_ARCHES="aarch64 x64 rv64" to sweep
     34 # others through podman/qemu. Skip is a failure unless KIT_TEST_ALLOW_SKIP=1.
     35 set -u
     36 
     37 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
     38 BIN="${KIT_BIN:-$ROOT/build/kit}"
     39 HOSTCC="${CC:-cc}"
     40 BUILD="$ROOT/build/test/bounce"
     41 CRT="$ROOT/test/bounce/crt/crt.c"
     42 OPT_LEVELS="${KIT_BOUNCE_OPTS:-0 1}"
     43 
     44 export KIT_LIB_DIR="$ROOT/test/lib"
     45 # shellcheck source=../lib/kit_corpus.sh
     46 . "$ROOT/test/lib/kit_corpus.sh"
     47 
     48 rm -rf "$BUILD"
     49 mkdir -p "$BUILD"
     50 
     51 # ---- arch selection --------------------------------------------------------
     52 
     53 case "$(uname -m 2>/dev/null)" in
     54   arm64|aarch64) NATIVE=aarch64 ;;
     55   x86_64)        NATIVE=x64 ;;
     56   *)             NATIVE="" ;;
     57 esac
     58 ARCHES="${KIT_BOUNCE_ARCHES:-$NATIVE}"
     59 
     60 arch_triple() {
     61   case "$1" in
     62     aarch64) echo aarch64-linux ;;
     63     x64)     echo x86_64-linux ;;
     64     rv64)    echo riscv64-linux ;;
     65     *)       echo "" ;;
     66   esac
     67 }
     68 arch_define() {
     69   case "$1" in
     70     aarch64) echo BOUNCE_AARCH64 ;;
     71     x64)     echo BOUNCE_X64 ;;
     72     rv64)    echo BOUNCE_RV64 ;;
     73     *)       echo "" ;;
     74   esac
     75 }
     76 
     77 # ---- exec_target wiring ----------------------------------------------------
     78 
     79 have_podman=0; command -v podman >/dev/null 2>&1 && have_podman=1
     80 have_qemu=0; QEMU_BIN=""
     81 case "$(uname -m 2>/dev/null)" in arm64|aarch64) is_aarch64=1 ;; *) is_aarch64=0 ;; esac
     82 export have_podman have_qemu QEMU_BIN is_aarch64
     83 EXEC_TARGET_MOUNT_ROOT="$BUILD"
     84 export EXEC_TARGET_MOUNT_ROOT
     85 # shellcheck source=../lib/exec_target.sh
     86 . "$ROOT/test/lib/exec_target.sh"
     87 
     88 # ---- prerequisites ---------------------------------------------------------
     89 
     90 if [ ! -x "$BIN" ]; then
     91   echo "missing $BIN — run \`make bin\` first" >&2
     92   exit 2
     93 fi
     94 if [ -z "$ARCHES" ]; then
     95   kit_skip "arch" "unsupported host arch $(uname -m) — set KIT_BOUNCE_ARCHES"
     96   kit_summary test-bounce; kit_exit
     97 fi
     98 if ! "$HOSTCC" -x c -c /dev/null -o /dev/null 2>/dev/null; then
     99   kit_skip "hostcc" "host '$HOSTCC' cannot compile — set CC"
    100   kit_summary test-bounce; kit_exit
    101 fi
    102 
    103 CASES_GLOB="$ROOT/test/bounce/cases/*.c"
    104 # shellcheck disable=SC2086
    105 if ! ls $CASES_GLOB >/dev/null 2>&1; then
    106   echo "no cases under test/bounce/cases" >&2
    107   exit 2
    108 fi
    109 
    110 # ---- per-case host reference (LANE-LOCAL) ----------------------------------
    111 # Reference exit code for a case = running it on the host. The cases are
    112 # portable LP64 integer C, so the host result is the oracle every target
    113 # and every bounce chain must reproduce. Writes only under $KIT_WORK.
    114 compute_ref() {
    115   local case="$1" ref_exe="$KIT_WORK/ref" drv="$KIT_WORK/refmain.c"
    116   printf 'int bounce_main(void);\nint main(void){return bounce_main();}\n' >"$drv"
    117   if ! "$HOSTCC" -O1 "$case" "$drv" -o "$ref_exe" 2>"$KIT_WORK/ref.err"; then
    118     return 1
    119   fi
    120   "$ref_exe"; echo $?
    121 }
    122 
    123 # ---- bounce chains (LANE-LOCAL) --------------------------------------------
    124 # Each chain takes prog.o + crt.o and produces an executable at $out, or
    125 # returns nonzero (with a diagnostic on stdout) if any transform/link fails.
    126 # $1=prog.o $2=crt.o $3=out $4=workdir
    127 chain_direct()  { "$BIN" ld -o "$3" -e _start -static "$1" "$2" 2>&1; }
    128 chain_macho_rt() {
    129   "$BIN" objcopy -O mach-o "$1" "$4/m.o" 2>&1 || return 1
    130   "$BIN" objcopy -O elf "$4/m.o" "$4/m2.o" 2>&1 || return 1
    131   "$BIN" ld -o "$3" -e _start -static "$4/m2.o" "$2" 2>&1
    132 }
    133 chain_coff_rt() {
    134   "$BIN" objcopy -O coff "$1" "$4/c.o" 2>&1 || return 1
    135   "$BIN" objcopy -O elf "$4/c.o" "$4/c2.o" 2>&1 || return 1
    136   "$BIN" ld -o "$3" -e _start -static "$4/c2.o" "$2" 2>&1
    137 }
    138 chain_tri_rt() {
    139   "$BIN" objcopy -O mach-o "$1" "$4/t1.o" 2>&1 || return 1
    140   "$BIN" objcopy -O coff "$4/t1.o" "$4/t2.o" 2>&1 || return 1
    141   "$BIN" objcopy -O elf "$4/t2.o" "$4/t3.o" 2>&1 || return 1
    142   "$BIN" ld -o "$3" -e _start -static "$4/t3.o" "$2" 2>&1
    143 }
    144 chain_ldr() {
    145   "$BIN" ld -r -o "$4/merged.o" "$1" "$2" 2>&1 || return 1
    146   "$BIN" ld -o "$3" -e _start -static "$4/merged.o" 2>&1
    147 }
    148 chain_strip_dbg() {
    149   "$BIN" objcopy --strip-debug "$1" "$4/s.o" 2>&1 || return 1
    150   "$BIN" ld -o "$3" -e _start -static "$4/s.o" "$2" 2>&1
    151 }
    152 chain_ar() {
    153   rm -f "$4/lib.a"
    154   "$BIN" ar rcs "$4/lib.a" "$1" 2>&1 || return 1
    155   "$BIN" ld -o "$3" -e _start -static "$2" "$4/lib.a" 2>&1
    156 }
    157 CHAINS="direct macho_rt coff_rt tri_rt ldr strip_dbg ar"
    158 
    159 # A chain is only meaningful when every object format it routes through is
    160 # a real kit target for that arch. Support matrix:
    161 #   ELF     all arches (Linux/freestanding)
    162 #   COFF    x64 + aarch64 (Windows)
    163 #   Mach-O  aarch64 only (Apple Silicon; kit does not target x64 Mach-O)
    164 # Returns 0 when $chain is applicable for $arch.
    165 chain_applies() {
    166   local arch="$1" chain="$2"
    167   case "$chain" in
    168     macho_rt | tri_rt) # route through Mach-O
    169       [ "$arch" = aarch64 ] ;;
    170     coff_rt) # route through COFF
    171       case "$arch" in aarch64 | x64) return 0 ;; *) return 1 ;; esac ;;
    172     *) return 0 ;; # ELF-only: direct, ldr, strip_dbg, ar
    173   esac
    174 }
    175 
    176 # ---- lane B: compile prog.o, then run every applicable chain ---------------
    177 # KIT_ARCH is the corpus tuple's arch (aarch64/x64/rv64); KIT_OBJ is "linux".
    178 # crt + host reference + prog.o are built here (KIT_WORK-confined). Each chain's
    179 # executable is deferred via kit_queue_e and scored at flush (rc == host ref).
    180 kit_lane_B() {
    181   local arch="$KIT_ARCH" exec_tag="$KIT_ARCH-linux"
    182   # Disambiguate result names by arch+opt (the engine's KIT_NAME does not carry
    183   # the tuple), matching the original "<name>.<arch>.O<opt>.<chain>" labels.
    184   local stem="$KIT_BASE.$arch.O$KIT_OPT"
    185   local triple define
    186   triple="$(arch_triple "$arch")"
    187   define="$(arch_define "$arch")"
    188   if [ -z "$triple" ]; then kit_skip "$stem" "unknown arch tag"; return; fi
    189   if ! exec_target_supported "$exec_tag"; then
    190     kit_skip "$stem" "no runner (need podman or qemu for $arch-linux)"; return
    191   fi
    192 
    193   # crt depends only on (arch, opt); built per-item under $KIT_WORK.
    194   local crt_o="$KIT_WORK/crt.o"
    195   if ! "$BIN" cc -target "$triple" -D"$define" -O"$KIT_OPT" -c "$CRT" \
    196       -o "$crt_o" 2>"$KIT_WORK/crt.err"; then
    197     kit_fail "$stem" "crt.c (see $KIT_WORK/crt.err)"; return
    198   fi
    199 
    200   local ref
    201   ref="$(compute_ref "$KIT_SRC")"
    202   if [ -z "$ref" ]; then
    203     kit_fail "$stem" "reference build (host cc; see $KIT_WORK/ref.err)"; return
    204   fi
    205 
    206   local prog_o="$KIT_WORK/prog.o"
    207   if ! "$BIN" cc -target "$triple" -O"$KIT_OPT" -c "$KIT_SRC" -o "$prog_o" \
    208       2>"$KIT_WORK/prog.err"; then
    209     kit_fail "$stem" "compile (see $KIT_WORK/prog.err)"; return
    210   fi
    211 
    212   local chain tag wd exe
    213   for chain in $CHAINS; do
    214     chain_applies "$arch" "$chain" || continue
    215     tag="$stem.$chain"
    216     wd="$KIT_WORK/$chain.d"; mkdir -p "$wd"
    217     exe="$KIT_WORK/$chain.exe"
    218     if ! "chain_$chain" "$prog_o" "$crt_o" "$exe" "$wd" \
    219         >"$KIT_WORK/$chain.link.log" 2>&1; then
    220       kit_fail "$tag" "transform/link; see $KIT_WORK/$chain.link.log"
    221       continue
    222     fi
    223     # Defer exec: the engine batches all queued cases into one container run
    224     # at flush, then scores rc against the host reference (EXPECTED).
    225     kit_queue_e "$tag" "$exe" \
    226       "$KIT_WORK/$chain.out" "$KIT_WORK/$chain.err" "$KIT_WORK/$chain.rc" \
    227       "$ref" "$exec_tag"
    228   done
    229 }
    230 
    231 # ---- drive the corpus ------------------------------------------------------
    232 # One tuple per requested arch; opt axis from KIT_BOUNCE_OPTS; single lane B.
    233 TUPLES=
    234 for arch in $ARCHES; do
    235   TUPLES="$TUPLES $arch-linux"
    236 done
    237 
    238 KIT_LABEL=test-bounce KIT_BUILD_DIR="$BUILD/work" \
    239   KIT_CORPUS_GLOBS="$CASES_GLOB" KIT_CORPUS_EXT=c KIT_SIDECAR_DIR="$ROOT/test/bounce/cases" \
    240   KIT_LANES="B" KIT_OPT_LEVELS="$OPT_LEVELS" KIT_TUPLES="$TUPLES" KIT_TARGETS_EXT="" \
    241   KIT_PARALLELIZABLE="${KIT_BOUNCE_PARALLEL:-1}" kit_corpus_run
    242 
    243 kit_summary test-bounce
    244 kit_exit