kit

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

commit ae5a02a4ae3f15832cae5b972d6b928320f26623
parent c5899fea4182a2cc597568712047d0b29cf5002c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 08:00:17 -0700

test: add ELF DSO harness, aarch64 all-clang lane (DYNLIB.md milestone 1)

test/link/elf-dso-aa64/ is the shared-library test fixture described in
doc/plan/DYNLIB.md §10.  It has three planned lanes (only lane A is
implemented now; B/C follow as kit ld -shared and kit cc -fPIC land):

  A — all-clang: clang -fPIC + lld -shared produce libfoo.so; a freestanding
      consumer links against it.  Structural checks via readelf assert ET_DYN,
      e_entry 0, no PT_INTERP, PT_DYNAMIC present, DT_SONAME, exported symbols
      (foo_add / foo_counter / foo_get_counter), foo_hidden absent from .dynsym,
      bar_external present as UND, and correct consumer DT_NEEDED / RPATH.
      Runtime: exec_dso_podman runs the consumer in alpine (musl) and
      debian:bookworm-slim (glibc) arm64 containers; all 14 checks pass.

The fixture uses -nostdlib to avoid needing a cross sysroot, and freestanding
_start + aarch64 SVC for exit so the consumer needs no libc at compile time.
RPATH $ORIGIN makes libfoo.so findable at runtime from within the container's
/work mount without any LD_LIBRARY_PATH plumbing.

mk/test.mk: add test-link-dso-aa64 target (opt-in, not in DEFAULT_TEST_TARGETS
since it requires clang aarch64 cross + ld.lld + provisioned podman images).

Diffstat:
Mmk/test.mk | 11+++++++++++
Atest/link/elf-dso-aa64/consumer.c | 39+++++++++++++++++++++++++++++++++++++++
Atest/link/elf-dso-aa64/lib_foo.c | 33+++++++++++++++++++++++++++++++++
Atest/link/elf-dso-aa64/run.sh | 393+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 476 insertions(+), 0 deletions(-)

diff --git a/mk/test.mk b/mk/test.mk @@ -105,6 +105,7 @@ TEST_TARGETS = \ test-link-reloc-apply \ test-link-jit-tls-relax \ test-link-coff-weak-alias \ + test-link-dso-aa64 \ test-macho \ test-native-direct-target \ test-opt \ @@ -903,6 +904,16 @@ test-link: lib $(ROUNDTRIP_BIN) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_ test-link-x64: lib rt-x86_64-linux $(ROUNDTRIP_BIN) $(LINK_EXE_RUNNER) $(JIT_RUNNER) @KIT_TEST_ARCH=x64 KIT_TEST_PATHS=RE bash test/link/run.sh +# test-link-dso-aa64: ELF shared-library (DSO) harness targeting aarch64-linux. +# Lane A (all-clang) uses clang + lld to produce libfoo.so and a freestanding +# consumer, runs readelf structural checks, and executes the consumer inside +# podman containers (alpine/musl and debian/glibc). No kit linker or codegen +# is involved at lane A; lanes B (kit ld -shared) and C (all-kit) follow as the +# kit DSO implementation lands. Opt-in: requires clang aarch64 cross + ld.lld + +# readelf + provisioned podman images (make test-images) for runtime checks. +test-link-dso-aa64: + bash test/link/elf-dso-aa64/run.sh + test-macho: lib $(TEST_RT_DEP) $(ROUNDTRIP_BIN_MACHO) $(LINK_EXE_RUNNER) $(JIT_RUNNER) KIT_TEST_OBJ=macho \ KIT_TEST_ARCH=$${KIT_TEST_ARCH:-aa64} \ diff --git a/test/link/elf-dso-aa64/consumer.c b/test/link/elf-dso-aa64/consumer.c @@ -0,0 +1,39 @@ +/* test/link/elf-dso-aa64/consumer.c — DSO consumer; freestanding aarch64. + * + * Dynamically linked against libfoo.so. Provides bar_external so that + * libfoo.so's undefined import is satisfied at runtime via the executable's + * exported symbol table (requires --export-dynamic / -rdynamic at link time). + * + * Uses a direct SVC for exit so the binary needs no libc and no sysroot at + * compile time; only the dynamic linker and libfoo.so are needed at runtime. + * + * Exit codes: + * 0 all checks passed + * 1 foo_add(10, 32) != 42 + * 2 foo_get_counter() != 42 + */ + +extern int foo_add(int a, int b); +extern int foo_get_counter(void); + +/* Satisfies libfoo.so's undefined import of bar_external. Exported to the + * runtime dynamic symbol table by --export-dynamic on the consumer link. */ +int bar_external(int x) { (void)x; return 0; } + +static __attribute__((noreturn)) void do_exit(int code) +{ + /* aarch64 Linux: x8 = NR_exit_group (94), x0 = exit code */ + register long x8 __asm__("x8") = 94; + register long x0 __asm__("x0") = (long)code; + __asm__ volatile("svc 0" : : "r"(x8), "r"(x0) : "memory"); + __builtin_unreachable(); +} + +void _start(void) +{ + /* foo_add(10, 32) = 10 + 32 + bar_external(0) = 10 + 32 + 0 = 42 */ + if (foo_add(10, 32) != 42) do_exit(1); + /* foo_get_counter() reads foo_counter which was initialised to 42 */ + if (foo_get_counter() != 42) do_exit(2); + do_exit(0); +} diff --git a/test/link/elf-dso-aa64/lib_foo.c b/test/link/elf-dso-aa64/lib_foo.c @@ -0,0 +1,33 @@ +/* test/link/elf-dso-aa64/lib_foo.c — shared-library source fixture. + * + * Exported (appear in .dynsym as GLOBAL DEFAULT defined): + * foo_add — function; calls bar_external (undefined import) + * foo_get_counter — function returning foo_counter + * foo_counter — data, initial value 42 + * + * Hidden (must NOT appear in .dynsym as a defined exported symbol): + * foo_hidden — function with __attribute__((visibility("hidden"))) + * + * Undefined import (appears in .dynsym as GLOBAL DEFAULT UND): + * bar_external — called by foo_add; left unresolved at DSO link time + */ + +extern int bar_external(int x); + +int foo_counter = 42; + +int foo_add(int a, int b) +{ + return a + b + bar_external(0); +} + +int foo_get_counter(void) +{ + return foo_counter; +} + +__attribute__((visibility("hidden"))) +int foo_hidden(int x) +{ + return x * 2; +} diff --git a/test/link/elf-dso-aa64/run.sh b/test/link/elf-dso-aa64/run.sh @@ -0,0 +1,393 @@ +#!/usr/bin/env bash +# test/link/elf-dso-aa64/run.sh — ELF shared-library harness, aarch64 target. +# +# Three lanes (only lane A is implemented now; B and C follow after the kit +# linker and codegen support lands): +# +# A — all-clang baseline: clang compiles PIC objects, clang (via lld) links +# the DSO and consumer, readelf structural checks, podman runtime exec. +# This lane proves the test fixture, sysroot, container, and readelf +# checks are correct before any kit linker or codegen work is in scope. +# +# B — kit ld -shared: clang still compiles PIC objects but kit ld produces +# the DSO. Isolates ELF DSO production from kit codegen. (NOT YET) +# +# C — all-kit: kit cc -fPIC compiles the objects, kit ld -shared links. +# Validates kit-generated PIC code and DSO interposition choices. (NOT YET) +# +# Prerequisites (checked at runtime; missing prereqs produce SKIP, not FAIL): +# clang — with --target=aarch64-linux-gnu cross-compilation support +# lld — ld.lld on PATH (needed for ELF shared library link from macOS) +# readelf — llvm-readelf or GNU readelf (structural checks) +# podman — for runtime execution inside an aarch64 container +# images: alpine (musl) and/or debian:bookworm-slim (glibc) +# +# Usage: +# bash test/link/elf-dso-aa64/run.sh # all structural + runtime +# KIT_TEST_ALLOW_SKIP=1 bash ... # allow skips (CI-friendly) +# KIT_LANE=A bash ... # explicit lane filter (default A) +# +# Output: structured pass/fail via kit_sh_report.sh; exit 0 = all pass. + +set -u + +ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +BUILD_DIR="$ROOT/build/test/link/elf-dso-aa64" +mkdir -p "$BUILD_DIR" + +KIT_KIT_DIR="$ROOT/test/lib" +. "$ROOT/test/lib/kit_sh_kit.sh" +kit_report_init + +KIT_SKIP_IS_FAILURE="${KIT_SKIP_IS_FAILURE:-0}" + +CLANG_TARGET="--target=aarch64-linux-gnu" +work="$BUILD_DIR" + +# ---- tool detection ----------------------------------------------------------- + +have_clang_aa64=0 +if clang $CLANG_TARGET -c -x c - -o /dev/null < /dev/null 2>/dev/null; then + have_clang_aa64=1 +fi + +have_lld=0 +command -v ld.lld >/dev/null 2>&1 && have_lld=1 + +have_readelf=0 +READELF_BIN="" +if command -v llvm-readelf >/dev/null 2>&1; then + READELF_BIN="$(command -v llvm-readelf)"; have_readelf=1 +elif command -v readelf >/dev/null 2>&1; then + READELF_BIN="$(command -v readelf)"; have_readelf=1 +fi + +# exec_target.sh requirements +have_qemu=0 +QEMU_BIN="" +have_podman=0 +is_aarch64=0 +arch_raw="$(uname -m 2>/dev/null || true)" +{ [ "$arch_raw" = "aarch64" ] || [ "$arch_raw" = "arm64" ]; } && is_aarch64=1 +command -v podman >/dev/null 2>&1 && have_podman=1 + +EXEC_TARGET_MOUNT_ROOT="$BUILD_DIR" +export have_qemu QEMU_BIN have_podman is_aarch64 EXEC_TARGET_MOUNT_ROOT + +# shellcheck source=../../lib/exec_target.sh +. "$ROOT/test/lib/exec_target.sh" + +# ---- exec_dso_podman ---------------------------------------------------------- +# Like exec_target_run but podman-only. Dynamically linked DSO consumers need a +# full OS environment (dynamic linker + libc); qemu-user alone cannot satisfy +# that without a sysroot. Both files (libfoo.so + consumer) must live in the +# same directory; the consumer's RPATH $ORIGIN then resolves to /work inside the +# container (exec_target_run mounts the directory as /work). +# +# Sets RUN_RC to the container exit code, or 127 when podman is unavailable / +# the image is not provisioned. +exec_dso_podman() { + local tag="$1" exe="$2" out="$3" err="$4" + RUN_RC=127 + [ "${have_podman:-0}" -eq 1 ] || return 0 + _exec_target_image_present "$tag" || return 0 + local dir base platform image platform_flag=() + dir="$(cd "$(dirname "$exe")" && pwd)" + base="$(basename "$exe")" + platform="$(_exec_target_platform "$tag")" + image="$(_exec_target_image "$tag")" + if ! _exec_target_podman_native "$tag"; then + platform_flag=(--platform "$platform") + fi + # Mount the directory as /work so both the consumer and libfoo.so are + # accessible at the same relative paths ($ORIGIN expands to /work). + podman run --rm --pull=never \ + ${platform_flag[@]+"${platform_flag[@]}"} \ + --net=none \ + -v "$dir":/work:Z -w /work \ + "$image" "./$base" \ + >"$out" 2>"$err" + RUN_RC=$? +} + +# ---- lane A: all-clang -------------------------------------------------------- +# +# Structural checks (require clang + lld + readelf): +# A/build-lib compile lib_foo.c -fPIC and link libfoo.so +# A/header-dyn ET_DYN, e_entry = 0 +# A/no-interp no PT_INTERP in program headers +# A/has-dynamic PT_DYNAMIC present +# A/soname DT_SONAME = libfoo.so +# A/export-foo-add foo_add in .dynsym as GLOBAL defined +# A/export-foo-ctr foo_counter in .dynsym as GLOBAL defined +# A/export-foo-get foo_get_counter in .dynsym as GLOBAL defined +# A/hidden-absent foo_hidden NOT in .dynsym as a GLOBAL defined symbol +# A/import-bar bar_external in .dynsym as UND +# +# Runtime checks (require podman + provisioned image; SKIP if unavailable): +# A/run-musl consumer-musl exits 0 inside alpine (musl) container +# A/run-glibc consumer-glibc exits 0 inside debian (glibc) container + +LANE="${KIT_LANE:-A}" +if [ "$LANE" != A ]; then + skip_test "A" "KIT_LANE=$LANE (skipping lane A)" + kit_summary test-link-dso-aa64; kit_exit +fi + +# ---- A: prerequisites -------------------------------------------------------- + +if [ $have_clang_aa64 -eq 0 ]; then + skip_test "A" "clang --target=aarch64-linux-gnu unavailable" + kit_summary test-link-dso-aa64; kit_exit +fi +if [ $have_lld -eq 0 ]; then + skip_test "A" "ld.lld unavailable (needed for ELF shared library link)" + kit_summary test-link-dso-aa64; kit_exit +fi +if [ $have_readelf -eq 0 ]; then + skip_test "A" "readelf / llvm-readelf unavailable (needed for structural checks)" + kit_summary test-link-dso-aa64; kit_exit +fi + +LIBFOO_O="$work/lib_foo.o" +LIBFOO_SO="$work/libfoo.so" +CONSUMER_O="$work/consumer.o" +CONSUMER_MUSL="$work/consumer-musl" +CONSUMER_GLIBC="$work/consumer-glibc" + +SRC_DIR="$(cd "$(dirname "$0")" && pwd)" + +# ---- A/build-lib: compile lib_foo.c + link libfoo.so ------------------------- + +if ! clang $CLANG_TARGET -fuse-ld=lld \ + -O1 -fno-stack-protector -fPIC \ + -c "$SRC_DIR/lib_foo.c" -o "$LIBFOO_O" \ + >"$work/compile-lib.out" 2>"$work/compile-lib.err"; then + not_ok "A/build-lib" "$work/compile-lib.err" + kit_summary test-link-dso-aa64; kit_exit +fi + +# -Wl,--allow-shlib-undefined: bar_external is intentionally unresolved in the +# DSO; it will appear as UND in .dynsym and be satisfied at runtime by the +# consumer. This is the default for -shared with GNU/LLVM linkers but we spell +# it out to be explicit about the policy. +if ! clang $CLANG_TARGET -fuse-ld=lld \ + -shared -fPIC -nostdlib \ + -Wl,--allow-shlib-undefined \ + -Wl,-soname,libfoo.so \ + "$LIBFOO_O" -o "$LIBFOO_SO" \ + >"$work/link-lib.out" 2>"$work/link-lib.err"; then + not_ok "A/build-lib" "$work/link-lib.err" + kit_summary test-link-dso-aa64; kit_exit +fi +ok "A/build-lib" + +# ---- A: structural checks on libfoo.so --------------------------------------- + +# Dump ELF header, program headers, dynamic section, and dynamic symbol table +# once each into per-check files; all grep/awk checks read those cached files. +DUMP_HDR="$work/readelf-hdr.txt" +DUMP_PHDR="$work/readelf-phdr.txt" +DUMP_DYN="$work/readelf-dyn.txt" +DUMP_DYNSYM="$work/readelf-dynsym.txt" + +"$READELF_BIN" -h "$LIBFOO_SO" >"$DUMP_HDR" 2>/dev/null +"$READELF_BIN" -l "$LIBFOO_SO" >"$DUMP_PHDR" 2>/dev/null +"$READELF_BIN" -d "$LIBFOO_SO" >"$DUMP_DYN" 2>/dev/null +"$READELF_BIN" --dyn-syms "$LIBFOO_SO" >"$DUMP_DYNSYM" 2>/dev/null + +# A/header-dyn: ELF type must be DYN, entry address must be 0x0 +if grep -q "Type:.*DYN" "$DUMP_HDR" && \ + grep -qE "Entry point address:\s+0x0\b" "$DUMP_HDR"; then + ok "A/header-dyn" +else + printf 'FAIL: expected Type: DYN and entry 0x0\n' >"$work/A-header-dyn.diag" + grep "Type:\|Entry point" "$DUMP_HDR" >>"$work/A-header-dyn.diag" + not_ok "A/header-dyn" "$work/A-header-dyn.diag" +fi + +# A/no-interp: PT_INTERP must be absent (DSOs are loaded by another program's +# interpreter, not their own) +if grep -q "INTERP" "$DUMP_PHDR"; then + printf 'FAIL: unexpected PT_INTERP in DSO program headers\n' >"$work/A-no-interp.diag" + grep "INTERP" "$DUMP_PHDR" >>"$work/A-no-interp.diag" + not_ok "A/no-interp" "$work/A-no-interp.diag" +else + ok "A/no-interp" +fi + +# A/has-dynamic: PT_DYNAMIC must be present +if grep -q "DYNAMIC" "$DUMP_PHDR"; then + ok "A/has-dynamic" +else + printf 'FAIL: PT_DYNAMIC absent from DSO program headers\n' >"$work/A-has-dynamic.diag" + cat "$DUMP_PHDR" >>"$work/A-has-dynamic.diag" + not_ok "A/has-dynamic" "$work/A-has-dynamic.diag" +fi + +# A/soname: DT_SONAME must be present and match libfoo.so +if grep -q "(SONAME)" "$DUMP_DYN" && grep -q "libfoo.so" "$DUMP_DYN"; then + ok "A/soname" +else + printf 'FAIL: DT_SONAME absent or wrong\n' >"$work/A-soname.diag" + grep "SONAME\|soname\|libfoo" "$DUMP_DYN" >>"$work/A-soname.diag" + not_ok "A/soname" "$work/A-soname.diag" +fi + +# A/export-foo-add: foo_add must be in .dynsym as a GLOBAL defined symbol +if grep -q "GLOBAL\|WEAK" "$DUMP_DYNSYM" && grep "foo_add" "$DUMP_DYNSYM" | grep -qv "UND"; then + ok "A/export-foo-add" +else + printf 'FAIL: foo_add not exported in .dynsym\n' >"$work/A-export-foo-add.diag" + grep "foo_add" "$DUMP_DYNSYM" >>"$work/A-export-foo-add.diag" || true + not_ok "A/export-foo-add" "$work/A-export-foo-add.diag" +fi + +# A/export-foo-ctr: foo_counter must be in .dynsym as a GLOBAL defined symbol +if grep "foo_counter" "$DUMP_DYNSYM" | grep -qv "UND"; then + ok "A/export-foo-ctr" +else + printf 'FAIL: foo_counter not exported in .dynsym\n' >"$work/A-export-foo-ctr.diag" + grep "foo_counter" "$DUMP_DYNSYM" >>"$work/A-export-foo-ctr.diag" || true + not_ok "A/export-foo-ctr" "$work/A-export-foo-ctr.diag" +fi + +# A/export-foo-get: foo_get_counter must be in .dynsym as a GLOBAL defined symbol +if grep "foo_get_counter" "$DUMP_DYNSYM" | grep -qv "UND"; then + ok "A/export-foo-get" +else + printf 'FAIL: foo_get_counter not exported in .dynsym\n' >"$work/A-export-foo-get.diag" + grep "foo_get_counter" "$DUMP_DYNSYM" >>"$work/A-export-foo-get.diag" || true + not_ok "A/export-foo-get" "$work/A-export-foo-get.diag" +fi + +# A/hidden-absent: foo_hidden must NOT appear as a GLOBAL/WEAK defined symbol +# in .dynsym. Hidden symbols stay in .symtab only; they must not be preemptible. +# If it appears at all in .dynsym it must be STV_HIDDEN (Vis == HIDDEN) and not +# GLOBAL-or-WEAK DEFAULT, so check that no GLOBAL DEFAULT line mentions it. +if grep "foo_hidden" "$DUMP_DYNSYM" | grep -q "GLOBAL.*DEFAULT.*[0-9]"; then + printf 'FAIL: foo_hidden appears as GLOBAL DEFAULT in .dynsym\n' >"$work/A-hidden-absent.diag" + grep "foo_hidden" "$DUMP_DYNSYM" >>"$work/A-hidden-absent.diag" + not_ok "A/hidden-absent" "$work/A-hidden-absent.diag" +else + ok "A/hidden-absent" +fi + +# A/import-bar: bar_external must be in .dynsym as UND (undefined import) +if grep "bar_external" "$DUMP_DYNSYM" | grep -q "UND"; then + ok "A/import-bar" +else + printf 'FAIL: bar_external not in .dynsym as UND\n' >"$work/A-import-bar.diag" + grep "bar_external" "$DUMP_DYNSYM" >>"$work/A-import-bar.diag" || true + not_ok "A/import-bar" "$work/A-import-bar.diag" +fi + +# ---- A: build consumers for runtime execution -------------------------------- +# Two variants, one per libc ABI, so each can run in its matching container. +# Both are freestanding (no libc calls, no sysroot required at compile time). +# --export-dynamic exports bar_external to the executable's .dynsym so that +# libfoo.so's dynamic reference to it is resolved by the loader at startup. + +# Musl interpreter (alpine linux/arm64) +MUSL_INTERP="/lib/ld-musl-aarch64.so.1" +if ! clang $CLANG_TARGET -fuse-ld=lld \ + -O1 -ffreestanding -fno-stack-protector \ + -nostdlib -rdynamic \ + -Wl,--dynamic-linker="$MUSL_INTERP" \ + -Wl,-e,_start \ + -Wl,-rpath,\$ORIGIN \ + "$SRC_DIR/consumer.c" \ + -L"$work" -lfoo \ + -o "$CONSUMER_MUSL" \ + >"$work/build-consumer-musl.out" 2>"$work/build-consumer-musl.err"; then + skip_test "A/run-musl" "consumer-musl build failed; see $work/build-consumer-musl.err" + have_consumer_musl=0 +else + have_consumer_musl=1 +fi + +# Glibc interpreter (debian:bookworm-slim linux/arm64) +GLIBC_INTERP="/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1" +if ! clang $CLANG_TARGET -fuse-ld=lld \ + -O1 -ffreestanding -fno-stack-protector \ + -nostdlib -rdynamic \ + -Wl,--dynamic-linker="$GLIBC_INTERP" \ + -Wl,-e,_start \ + -Wl,-rpath,\$ORIGIN \ + "$SRC_DIR/consumer.c" \ + -L"$work" -lfoo \ + -o "$CONSUMER_GLIBC" \ + >"$work/build-consumer-glibc.out" 2>"$work/build-consumer-glibc.err"; then + skip_test "A/run-glibc" "consumer-glibc build failed; see $work/build-consumer-glibc.err" + have_consumer_glibc=0 +else + have_consumer_glibc=1 +fi + +# ---- A: check consumer DT_NEEDED and RPATH (structural, glibc variant) ------- +# Only if the glibc consumer built successfully. The musl and glibc consumers +# have identical dynamic metadata (same NEEDED/RPATH) — check once. +if [ $have_consumer_glibc -eq 1 ]; then + DUMP_CON_DYN="$work/readelf-consumer-dyn.txt" + "$READELF_BIN" -d "$CONSUMER_GLIBC" >"$DUMP_CON_DYN" 2>/dev/null + + # DT_NEEDED must reference libfoo.so (matching the soname we embedded) + if grep -q "(NEEDED)" "$DUMP_CON_DYN" && grep "(NEEDED)" "$DUMP_CON_DYN" | grep -q "libfoo.so"; then + ok "A/consumer-needed" + else + printf 'FAIL: consumer missing DT_NEEDED libfoo.so\n' >"$work/A-consumer-needed.diag" + grep "NEEDED\|libfoo" "$DUMP_CON_DYN" >>"$work/A-consumer-needed.diag" || true + not_ok "A/consumer-needed" "$work/A-consumer-needed.diag" + fi + + # RPATH or RUNPATH must contain $ORIGIN + if grep -E "(RPATH|RUNPATH)" "$DUMP_CON_DYN" | grep -q 'ORIGIN'; then + ok "A/consumer-rpath" + else + printf 'FAIL: consumer missing RPATH/RUNPATH with \$ORIGIN\n' >"$work/A-consumer-rpath.diag" + grep -E "RPATH|RUNPATH" "$DUMP_CON_DYN" >>"$work/A-consumer-rpath.diag" || true + not_ok "A/consumer-rpath" "$work/A-consumer-rpath.diag" + fi +fi + +# ---- A/run-musl: runtime execution in alpine (musl) container ---------------- +if [ $have_consumer_musl -eq 1 ]; then + if [ "${have_podman:-0}" -ne 1 ]; then + skip_test "A/run-musl" "podman unavailable" + elif ! _exec_target_image_present "aarch64-linux"; then + skip_test "A/run-musl" "aarch64 musl image not provisioned (run: make test-images)" + else + exec_dso_podman "aarch64-linux" "$CONSUMER_MUSL" \ + "$work/run-musl.out" "$work/run-musl.err" + if [ "$RUN_RC" -eq 0 ]; then + ok "A/run-musl" + else + printf 'consumer-musl exited %d\n' "$RUN_RC" >"$work/A-run-musl.diag" + [ -s "$work/run-musl.err" ] && sed 's/^/ | /' "$work/run-musl.err" >>"$work/A-run-musl.diag" + not_ok "A/run-musl" "$work/A-run-musl.diag" + fi + fi +fi + +# ---- A/run-glibc: runtime execution in debian (glibc) container -------------- +if [ $have_consumer_glibc -eq 1 ]; then + if [ "${have_podman:-0}" -ne 1 ]; then + skip_test "A/run-glibc" "podman unavailable" + elif ! _exec_target_image_present "aarch64-linux-glibc"; then + skip_test "A/run-glibc" "aarch64 glibc image not provisioned (run: make test-images)" + else + exec_dso_podman "aarch64-linux-glibc" "$CONSUMER_GLIBC" \ + "$work/run-glibc.out" "$work/run-glibc.err" + if [ "$RUN_RC" -eq 0 ]; then + ok "A/run-glibc" + else + printf 'consumer-glibc exited %d\n' "$RUN_RC" >"$work/A-run-glibc.diag" + [ -s "$work/run-glibc.err" ] && sed 's/^/ | /' "$work/run-glibc.err" >>"$work/A-run-glibc.diag" + not_ok "A/run-glibc" "$work/A-run-glibc.diag" + fi + fi +fi + +# ---- summary ----------------------------------------------------------------- +kit_summary test-link-dso-aa64 +kit_exit