kit

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

run.sh (12818B)


      1 #!/usr/bin/env bash
      2 # test/libc/musl/run.sh — drive kit ld against a real musl sysroot, on the
      3 # shared corpus harness (test/lib/kit_corpus.sh).
      4 #
      5 # Cases under test/libc/cases/*.c are compiled, linked, and run against a
      6 # podman-pinned Alpine/musl sysroot, in two link variants (two lanes):
      7 #
      8 #   S  static  — non-PIC object + libc.a, classic static-exe link
      9 #       kit ld -static -o case.exe                                          \
     10 #           $SYSROOT/lib/crt1.o $SYSROOT/lib/crti.o                            \
     11 #           case.o                                                             \
     12 #           $SYSROOT/lib/libc.a $KIT_RT                                      \
     13 #           $SYSROOT/lib/crtn.o
     14 #
     15 #   D  dynamic — PIE object + libc.so, expects PT_INTERP /lib/ld-musl-<arch>.so.1
     16 #       kit ld -pie -o case.exe                                             \
     17 #           $SYSROOT/lib/Scrt1.o $SYSROOT/lib/crti.o                           \
     18 #           case.o                                                             \
     19 #           $SYSROOT/lib/libc.so $KIT_RT                                     \
     20 #           $SYSROOT/lib/crtn.o
     21 #       (musl ships ld-musl-<arch>.so.1 *as* libc — same file. The harness
     22 #       intentionally has no -dynamic-linker flag yet because kit ld
     23 #       currently doesn't accept one; this is one of the gaps we expect the
     24 #       dynamic variant to surface.)
     25 #
     26 # Each case file may carry an `expected` companion (default 0) and an optional
     27 # `expected_stdout` (.stdout) file checked with substring match.
     28 #
     29 # The cross-exec is LANE-LOCAL: the lane runs the case under podman with musl's
     30 # own arch-pinned Alpine image, whose rootfs already carries the musl loader at
     31 # /lib/ld-musl-<arch>.so.1 — so NO QEMU_LD_PREFIX and no -dynamic-linker are
     32 # needed (unlike glibc). This is deliberately NOT routed through exec_target's
     33 # default-image queue; the image + loader provenance are musl-specific.
     34 #
     35 # Arch selection — KIT_LIBC_ARCHES (default "aa64"), space-separated; each
     36 # token becomes an "<arch>-elf" corpus tuple:
     37 #   aa64 -> build/musl-sysroot/         + build/rt/aarch64-linux/libkit_rt.a
     38 #                                       + --target=aarch64-linux-musl
     39 #   x64  -> build/musl-sysroot-x64/     + build/rt/x86_64-linux/libkit_rt.a
     40 #                                       + --target=x86_64-linux-musl
     41 #   rv64 -> build/musl-sysroot-rv64/    + build/rt/riscv64-linux/libkit_rt.a
     42 #                                       + --target=riscv64-linux-musl
     43 # A missing sysroot / rt / clang-target for an enabled arch is reported as a
     44 # non-gating SKIP-NA (never a failure); only test failures cause a nonzero exit.
     45 set -u
     46 
     47 ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
     48 export KIT_LIB_DIR="$ROOT/test/lib"
     49 . "$ROOT/test/lib/kit_corpus.sh"
     50 
     51 CASES_DIR="$ROOT/test/libc/cases"
     52 BUILD_DIR="$ROOT/build/musl"
     53 KIT="${KIT:-$ROOT/build/kit}"
     54 
     55 if [ ! -x "$KIT" ]; then
     56     echo "kit driver missing at $KIT — run 'make' first" >&2
     57     exit 2
     58 fi
     59 
     60 mkdir -p "$BUILD_DIR"
     61 
     62 # ---- arch lookup tables (LANE-LOCAL config) -------------------------------
     63 # Keyed on the arch token (aa64/x64/rv64), which is the tuple's arch half.
     64 
     65 arch_sysroot() {
     66     case "$1" in
     67         aa64) echo "$ROOT/build/musl-sysroot" ;;
     68         x64)  echo "$ROOT/build/musl-sysroot-x64" ;;
     69         rv64) echo "$ROOT/build/musl-sysroot-rv64" ;;
     70         *)    echo "" ;;
     71     esac
     72 }
     73 
     74 arch_rt() {
     75     case "$1" in
     76         aa64) echo "$ROOT/build/rt/aarch64-linux/libkit_rt.a" ;;
     77         x64)  echo "$ROOT/build/rt/x86_64-linux/libkit_rt.a" ;;
     78         rv64) echo "$ROOT/build/rt/riscv64-linux/libkit_rt.a" ;;
     79         *)    echo "" ;;
     80     esac
     81 }
     82 
     83 arch_target() {
     84     case "$1" in
     85         aa64) echo "aarch64-linux-musl" ;;
     86         x64)  echo "x86_64-linux-musl" ;;
     87         rv64) echo "riscv64-linux-musl" ;;
     88         *)    echo "" ;;
     89     esac
     90 }
     91 
     92 # Container image carrying the musl loader at /lib/ld-musl-<arch>.so.1, used by
     93 # the lane-local runner. Pinned to arch-specific repos (not multi-arch tags) to
     94 # dodge the cached-wrong-arch-manifest trap and to avoid --platform (which would
     95 # force a registry manifest lookup on every run).
     96 arch_image() {
     97     case "$1" in
     98         # arm64v8/alpine ships the musl loader at /lib/ld-musl-aarch64.so.1.
     99         aa64) echo "docker.io/arm64v8/alpine:latest" ;;
    100         # amd64v2/alpine isn't a thing; amd64/alpine is the canonical pin.
    101         # Ships /lib/ld-musl-x86_64.so.1.
    102         x64)  echo "docker.io/amd64/alpine:latest" ;;
    103         # alpine:edge currently carries the riscv64 musl loader.
    104         rv64) echo "docker.io/riscv64/alpine:edge" ;;
    105         *)    echo "" ;;
    106     esac
    107 }
    108 
    109 # Spelling extract.sh accepts for `-a`: aa64 -> aarch64; x64 -> x64.
    110 arch_extract_name() {
    111     case "$1" in
    112         aa64) echo "aarch64" ;;
    113         *)    echo "$1" ;;
    114     esac
    115 }
    116 
    117 # qemu-user fallback binary per arch (used when no native exec is possible).
    118 arch_qemu() {
    119     case "$1" in
    120         aa64) echo "$QEMU_AA64" ;;
    121         x64)  echo "$QEMU_X64" ;;
    122         rv64) echo "$QEMU_RV64" ;;
    123         *)    echo "" ;;
    124     esac
    125 }
    126 
    127 # ---- host capability detection --------------------------------------------
    128 #
    129 # Native linux/<arch> hosts can exec ELFs directly under podman without
    130 # binfmt; otherwise we fall back to qemu-<arch>-static.
    131 
    132 arch_raw="$(uname -m 2>/dev/null || true)"
    133 is_aarch64=0
    134 { [ "$arch_raw" = "aarch64" ] || [ "$arch_raw" = "arm64" ]; } && is_aarch64=1
    135 is_x86_64=0
    136 { [ "$arch_raw" = "x86_64" ] || [ "$arch_raw" = "amd64" ]; } && is_x86_64=1
    137 
    138 QEMU_AA64="$(command -v qemu-aarch64-static 2>/dev/null || command -v qemu-aarch64 2>/dev/null || true)"
    139 QEMU_X64="$(command -v qemu-x86_64-static 2>/dev/null || command -v qemu-x86_64 2>/dev/null || true)"
    140 QEMU_RV64="$(command -v qemu-riscv64-static 2>/dev/null || command -v qemu-riscv64 2>/dev/null || true)"
    141 have_podman=0; command -v podman >/dev/null 2>&1 && have_podman=1
    142 
    143 # ---- lane-local target runner ---------------------------------------------
    144 # run_target <arch> <exe> <out> <err>  -> sets RUN_RC
    145 # Musl: rootfs-baked loader, NO QEMU_LD_PREFIX.
    146 run_target() {
    147     local arch="$1" exe="$2" out="$3" err="$4"
    148     local qemu image
    149     qemu="$(arch_qemu "$arch")"
    150     image="$(arch_image "$arch")"
    151     if [ -n "$qemu" ]; then
    152         "$qemu" "$exe" >"$out" 2>"$err"; RUN_RC=$?; return
    153     fi
    154     if [ $have_podman -eq 1 ]; then
    155         local dir base
    156         dir="$(cd "$(dirname "$exe")" && pwd)"; base="$(basename "$exe")"
    157         podman run --rm --pull=never --net=none \
    158             -v "$dir":/work:Z -w /work \
    159             "$image" "./$base" \
    160             >"$out" 2>"$err"
    161         RUN_RC=$?; return
    162     fi
    163     RUN_RC=127
    164 }
    165 
    166 # ---- per-case marker reading (KIT_READ_CASE) --------------------------------
    167 # Reads the optional .stdout substring oracle and resolves this tuple's arch
    168 # config. The .expected sidecar (default 0) is read by the engine already
    169 # (KIT_EXPECTED_EXT=.expected). A tuple whose sysroot/rt/clang-target is
    170 # unavailable is marked SKIP-NA (non-gating), mirroring the original's
    171 # SKIP_ARCHES informational handling.
    172 libc_read_case() {
    173     LC_ARCH="$KIT_ARCH"
    174     LC_SYSROOT="$(arch_sysroot "$LC_ARCH")"
    175     LC_RT="$(arch_rt "$LC_ARCH")"
    176     LC_TARGET="$(arch_target "$LC_ARCH")"
    177 
    178     if [ -z "$LC_SYSROOT" ] || [ -z "$LC_RT" ] || [ -z "$LC_TARGET" ]; then
    179         KIT_SKIP_NA_CASE=1; return
    180     fi
    181     if [ ! -d "$LC_SYSROOT" ]; then
    182         KIT_SKIP_NA_CASE=1; return
    183     fi
    184     if [ ! -f "$LC_RT" ]; then
    185         KIT_SKIP_NA_CASE=1; return
    186     fi
    187     # clang must understand --target=<target>. Recent clang ships linux-musl as
    188     # a target alias of linux-gnu for our purposes (we override every system
    189     # path via --sysroot).
    190     if ! clang --target="$LC_TARGET" -c -x c - -o /dev/null < /dev/null 2>/dev/null; then
    191         KIT_SKIP_NA_CASE=1; return
    192     fi
    193 
    194     LC_EXPECT_STDOUT=""
    195     if [ -f "$CASES_DIR/${KIT_BASE}.stdout" ]; then
    196         LC_EXPECT_STDOUT="$(cat "$CASES_DIR/${KIT_BASE}.stdout")"
    197     fi
    198 }
    199 
    200 # ---- shared per-variant pipeline (compile -> link -> run -> stdout) --------
    201 # libc_run_variant <variant> <label>
    202 #   variant ∈ {static, dynamic}; emits exactly one kit_pass/kit_fail.
    203 #   All artifacts live under $KIT_WORK (parallel-safe).
    204 libc_run_variant() {
    205     local variant="$1" label="$2"
    206     local work="$KIT_WORK/$variant"
    207     mkdir -p "$work"
    208 
    209     # ---- compile ----
    210     # -nostdinc strips clang's default include path (resource dir +
    211     # /usr/include) so the sysroot's musl + linux-headers tree is the sole
    212     # source. -isystem $sysroot/include picks it up.
    213     local cc_flags=(--target="$LC_TARGET" --sysroot="$LC_SYSROOT"
    214                     -nostdinc
    215                     -isystem "$LC_SYSROOT/include"
    216                     -O0)
    217     case "$variant" in
    218         static)  cc_flags+=(-fno-PIC -fno-pie) ;;
    219         dynamic) cc_flags+=(-fPIE -fpic) ;;
    220     esac
    221 
    222     local obj="$work/${KIT_BASE}.o"
    223     if ! clang "${cc_flags[@]}" -c "$KIT_SRC" -o "$obj" 2>"$work/cc.err"; then
    224         kit_fail "$label (compile)"
    225         sed 's/^/    cc| /' "$work/cc.err"
    226         return
    227     fi
    228 
    229     # ---- link ----
    230     local exe="$work/${KIT_BASE}.exe"
    231     local link_cmd
    232     case "$variant" in
    233         static)
    234             # Link order mirrors a typical static-musl invocation:
    235             #   crt1.o crti.o  obj  libc.a libkit_rt.a  crtn.o
    236             # libkit_rt provides the TF / soft-float builtins (__addtf3,
    237             # __extenddftf2 etc.) that musl's libc.a calls from printf's
    238             # long-double formatting. Archive ingestion iterates demand-load
    239             # to a fixed point so one trailing libkit_rt.a is enough.
    240             link_cmd=("$KIT" "ld" -static -o "$exe"
    241                       "$LC_SYSROOT/lib/crt1.o" "$LC_SYSROOT/lib/crti.o"
    242                       "$obj"
    243                       "$LC_SYSROOT/lib/libc.a" "$LC_RT"
    244                       "$LC_SYSROOT/lib/crtn.o")
    245             ;;
    246         dynamic)
    247             # Dynamic-exe link: PIE start file, libc.so as a *shared* input
    248             # (not an archive), expects kit ld to:
    249             #   - accept ET_DYN ELF objects as input,
    250             #   - emit PT_INTERP "/lib/ld-musl-<arch>.so.1",
    251             #   - emit PT_DYNAMIC with DT_NEEDED libc.so,
    252             #   - emit a .dynsym/.dynstr/.gnu.hash + .rela.plt/.got.plt so the
    253             #     loader can bind imported symbols at runtime.
    254             # libkit_rt.a stays — soft-float TF helpers are still
    255             # static-bound from our side. crti/crtn are unchanged.
    256             link_cmd=("$KIT" "ld" -pie -o "$exe"
    257                       "$LC_SYSROOT/lib/Scrt1.o" "$LC_SYSROOT/lib/crti.o"
    258                       "$obj"
    259                       "$LC_SYSROOT/lib/libc.so" "$LC_RT"
    260                       "$LC_SYSROOT/lib/crtn.o")
    261             ;;
    262     esac
    263 
    264     if ! "${link_cmd[@]}" >"$work/link.out" 2>"$work/link.err"; then
    265         kit_fail "$label (link)"
    266         sed 's/^/    ld| /' "$work/link.err" | head -10
    267         return
    268     fi
    269 
    270     # ---- run (lane-local cross-exec) ----
    271     run_target "$LC_ARCH" "$exe" "$work/run.out" "$work/run.err"
    272     if [ "$RUN_RC" -ne "$KIT_EXPECTED" ]; then
    273         kit_fail "$label (run rc=$RUN_RC, want $KIT_EXPECTED)"
    274         [ -s "$work/run.err" ] && sed 's/^/    err| /' "$work/run.err" | head -5
    275         [ -s "$work/run.out" ] && sed 's/^/    out| /' "$work/run.out" | head -5
    276         return
    277     fi
    278 
    279     if [ -n "$LC_EXPECT_STDOUT" ]; then
    280         if ! grep -qF -- "$LC_EXPECT_STDOUT" "$work/run.out"; then
    281             kit_fail "$label (stdout mismatch)"
    282             printf '    expected substring: %s\n' "$LC_EXPECT_STDOUT"
    283             sed 's/^/    got| /' "$work/run.out" | head -5
    284             return
    285         fi
    286     fi
    287 
    288     kit_pass "$label"
    289 }
    290 
    291 # ---- lanes (static / dynamic variant axis) --------------------------------
    292 kit_lane_S() { libc_run_variant static  "$KIT_ARCH/$KIT_BASE [static]"; }
    293 kit_lane_D() { libc_run_variant dynamic "$KIT_ARCH/$KIT_BASE [dynamic]"; }
    294 
    295 # ---- drive the corpus ------------------------------------------------------
    296 # One kit_corpus_run per arch (single "<arch>-elf" tuple), so each arch gets a
    297 # distinct per-case $KIT_WORK ($KIT_BUILD_DIR/<arch>/<base>) and the static/
    298 # dynamic lanes for one arch never collide with another arch's under parallel
    299 # dispatch. Results accumulate into the shared counters; one summary at the end.
    300 printf 'test-libc-musl arches=%s\n' "${KIT_LIBC_ARCHES:-aa64}"
    301 
    302 PAR="${KIT_MUSL_PARALLEL:-1}"
    303 for arch in ${KIT_LIBC_ARCHES:-aa64}; do
    304     KIT_LABEL=test-libc-musl KIT_BUILD_DIR="$BUILD_DIR/$arch" \
    305       KIT_CORPUS_GLOBS="$CASES_DIR/*.c" KIT_CORPUS_EXT=c KIT_SIDECAR_DIR="$CASES_DIR" \
    306       KIT_LANES="S D" KIT_OPT_LEVELS="" KIT_TUPLES="$arch-elf" \
    307       KIT_EXPECTED_EXT=.expected KIT_TARGETS_EXT="" \
    308       KIT_READ_CASE=libc_read_case KIT_PARALLELIZABLE="$PAR" \
    309       kit_corpus_run
    310 done
    311 
    312 kit_summary test-libc-musl
    313 kit_exit