#!/bin/sh set -eu : "${CPU_AS:?}" "${CPU_LD:?}" "${CPU_RUN:?}" "${CPU_READELF:?}" : "${MCPU_TEST_ROOT:?}" "${MCPU_HLL_FIXTURE_SRCDIR:?}" TMP=${TMPDIR:-/tmp}/libmcpu-hll-crt-$$ mkdir -p "$TMP" trap 'rm -rf "$TMP"' EXIT HUP INT TERM as() { "$CPU_AS" -o "$2" "$1" >/dev/null } must_fail_link() { if "$CPU_LD" "$@" >/dev/null 2>&1; then echo "mcpu-ld unexpectedly accepted invalid HLL input: $*" >&2 exit 1 fi } # --------------------------------------------------------------------------- # Regression fixtures for compiler-normalized HLL objects and libc hooks. # --------------------------------------------------------------------------- as "$MCPU_HLL_FIXTURE_SRCDIR/program-main.S" "$TMP/program.o" as "$MCPU_HLL_FIXTURE_SRCDIR/model-pendulum-life.S" "$TMP/model.o" as "$MCPU_HLL_FIXTURE_SRCDIR/libc-stub.S" "$TMP/libc-stub.o" # The future compiler output owns HLL functions, never MELF lifecycle symbols. "$CPU_READELF" -s "$TMP/program.o" > "$TMP/program.syms" grep -q ' main$' "$TMP/program.syms" grep -q ' fini$' "$TMP/program.syms" if grep -Eq ' (_start|_life|_fini)$' "$TMP/program.syms"; then echo "PROGRAM compiler-output emulator defines lifecycle symbols" >&2 exit 1 fi "$CPU_READELF" -s "$TMP/model.o" > "$TMP/model.syms" grep -q ' init$' "$TMP/model.syms" grep -q ' life$' "$TMP/model.syms" grep -q ' fini$' "$TMP/model.syms" if grep -Eq ' (_start|_life|_fini)$' "$TMP/model.syms"; then echo "MODEL compiler-output emulator defines lifecycle symbols" >&2 exit 1 fi # --------------------------------------------------------------------------- # PROGRAM: automatic classification, CRT insertion, argc/argv bridge and fini. # --------------------------------------------------------------------------- "$CPU_LD" --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \ -o "$TMP/program" "$TMP/program.o" "$TMP/libc-stub.o" >/dev/null "$CPU_READELF" -h "$TMP/program" > "$TMP/program.hdr" entry=`sed -n 's/^ Entry point address:[[:space:]]*//p' "$TMP/program.hdr"` init=`sed -n 's/^ INIT entry address:[[:space:]]*//p' "$TMP/program.hdr"` life=`sed -n 's/^ LIFE entry address:[[:space:]]*//p' "$TMP/program.hdr"` fini=`sed -n 's/^ FINI entry address:[[:space:]]*//p' "$TMP/program.hdr"` test "x$entry" = "x$life" test "x$init" = "x0x0000000000000000" test "x$fini" != "x0x0000000000000000" set +e out=`"$CPU_RUN" --after-run=print-int-regs:r64@0x70000 \ -- "$TMP/program" alpha beta` status=$? set -e test "$status" -eq 42 test "x$out" = "x6" # ABI-v3 PROGRAM uses the same HLL interface with the runtime hooks supplied # by a normal shared MCPU Libc dependency. The four hook names must therefore # be exported from ET_DYN, while explicit preloads still may not interpose them. "$CPU_LD" --shared --soname=libhllc.so.1 \ -o "$TMP/libhllc.so.1" "$TMP/libc-stub.o" >/dev/null ln -s libhllc.so.1 "$TMP/libhllc.so" "$CPU_READELF" -s "$TMP/libhllc.so.1" > "$TMP/libhllc.syms" grep -q ' __mcpu_libc_init$' "$TMP/libhllc.syms" grep -q ' __mcpu_init_array$' "$TMP/libhllc.syms" grep -q ' __mcpu_fini_array$' "$TMP/libhllc.syms" grep -q ' __mcpu_libc_fini$' "$TMP/libhllc.syms" "$CPU_LD" --dynamic --sys-root="$MCPU_TEST_ROOT" \ --stack-size=4K --no-heap -L"$TMP" -lhllc \ -o "$TMP/program-dyn" "$TMP/program.o" >/dev/null set +e out=`MCPU_LD_PRELOAD= "$CPU_RUN" --library-path="$TMP" \ --after-run=print-int-regs:r64@0x70000 \ -- "$TMP/program-dyn" alpha beta` status=$? set -e test "$status" -eq 42 test "x$out" = "x6" # HLL complete-program functions remain bound to the main executable. Runtime # hooks come from the normal libc dependency. A preload may export all of the # same names but cannot interpose either class of protected HLL symbol. cat > "$TMP/override.s" <<'EOS' .global main .global fini .global __mcpu_libc_init .global __mcpu_init_array .global __mcpu_fini_array .global __mcpu_libc_fini .text main: LDI r64@0x70000, 700 LDI r32@0x7fffc, 70 RET fini: LDI r64@0x70000, 701 RET __mcpu_libc_init: LDI r64@0x70000, 702 RET __mcpu_init_array: LDI r64@0x70000, 703 RET __mcpu_fini_array: LDI r64@0x70000, 704 RET __mcpu_libc_fini: LDI r64@0x70000, 705 RET EOS as "$TMP/override.s" "$TMP/override.o" "$CPU_LD" --shared --soname=libhll-override.so.1 \ -o "$TMP/libhll-override.so" "$TMP/override.o" >/dev/null set +e out=`MCPU_LD_PRELOAD= "$CPU_RUN" --library-path="$TMP" \ --ldpreload="$TMP/libhll-override.so" \ --after-run=print-int-regs:r64@0x70000 \ -- "$TMP/program-dyn" alpha beta` status=$? set -e test "$status" -eq 42 test "x$out" = "x6" # --------------------------------------------------------------------------- # MODEL: compact pendulum-shaped compiler-output regression fixture. # --------------------------------------------------------------------------- "$CPU_LD" --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \ -o "$TMP/model" "$TMP/model.o" "$TMP/libc-stub.o" >/dev/null "$CPU_READELF" -h "$TMP/model" > "$TMP/model.hdr" entry=`sed -n 's/^ Entry point address:[[:space:]]*//p' "$TMP/model.hdr"` init=`sed -n 's/^ INIT entry address:[[:space:]]*//p' "$TMP/model.hdr"` life=`sed -n 's/^ LIFE entry address:[[:space:]]*//p' "$TMP/model.hdr"` fini=`sed -n 's/^ FINI entry address:[[:space:]]*//p' "$TMP/model.hdr"` test "x$entry" = "x$init" test "x$life" != "x$entry" test "x$fini" != "x0x0000000000000000" # Natural MODEL run: three LIFE_CONTINUE boundaries, then LIFE_END. set +e out=`"$CPU_RUN" --steps=10 \ --on-step=print-int-trace:64@0 \ --after-run=print-int-regs:r64@0x70000,r64@0x70010 \ -- "$TMP/model"` status=$? set -e test "$status" -eq 0 expected='1 2 3 6 1' test "x$out" = "x$expected" # init() failure is already RUNNING: normal HLL finalization is mandatory. set +e out=`"$CPU_RUN" --before-run=set-int-control:64@0=-1 \ --after-run=print-int-regs:r64@0x70000,r64@0x70010 \ -- "$TMP/model"` status=$? set -e test "$status" -eq 17 test "x$out" = "x6 1" # Positive life() status is a MODEL error and performs normal finalization. set +e out=`"$CPU_RUN" --steps=10 \ --before-run=set-int-control:64@8=2 \ --on-step=print-int-trace:64@0 \ --after-run=print-int-regs:r64@0x70000,r64@0x70010 \ -- "$TMP/model"` status=$? set -e test "$status" -eq 9 expected='1 6 1' test "x$out" = "x$expected" # Diagnostic --steps stop does not synthesize FINI. out=`"$CPU_RUN" --steps=1 \ --on-step=print-int-trace:64@0 \ --after-run=print-int-regs:r64@0x70000,r64@0x70010 \ -- "$TMP/model"` expected='1 3 0' test "x$out" = "x$expected" # --------------------------------------------------------------------------- # PRE_RUNTIME failure: exercise the real common CRT startup/finalization code. # A test-only PROGRAM adapter corrupts McpuStartup.version before entering the # real __mcpu_crt_startup. The normal user object and libc stub are unchanged. # --------------------------------------------------------------------------- mkdir -p "$TMP/bad-root/lib/mcpu" cp "$MCPU_TEST_ROOT/lib/mcpu/crti.o" "$TMP/bad-root/lib/mcpu/" cp "$MCPU_TEST_ROOT/lib/mcpu/crtn.o" "$TMP/bad-root/lib/mcpu/" cp "$MCPU_TEST_ROOT/lib/mcpu/crt1.o" "$TMP/bad-root/lib/mcpu/" cat > "$TMP/bad-crt0.s" <<'EOS' .global __mcpu_crt_startup .global main .text __mcpu_crt_hll_program: _start_life: LDI r32@0x7fe00, 99 STSP 0, r32@0x7fe00 CALL __mcpu_crt_startup LDI r32@0x7fe08, 88 EXIT r32@0x7fe08 EOS as "$TMP/bad-crt0.s" "$TMP/bad-root/lib/mcpu/crt0.o" "$CPU_LD" --sys-root="$TMP/bad-root" --stack-size=4K --no-heap \ -o "$TMP/bad-startup" "$TMP/program.o" "$TMP/libc-stub.o" >/dev/null set +e out=`"$CPU_RUN" --after-run=print-int-regs:r64@0x70000 \ -- "$TMP/bad-startup" alpha beta` status=$? set -e test "$status" -ne 0 test "x$out" = "x0" # --------------------------------------------------------------------------- # HLL complete-program classification errors. # --------------------------------------------------------------------------- cat > "$TMP/main-only.s" <<'EOS' .global main .text main: CLR r32@0x7fffc RET EOS as "$TMP/main-only.s" "$TMP/main-only.o" must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \ -o "$TMP/bad-main-only" "$TMP/main-only.o" "$TMP/libc-stub.o" cat > "$TMP/model-init.s" <<'EOS' .global init .global fini .text init: CLR r32@0x7fffc RET fini: RET EOS cat > "$TMP/model-life.s" <<'EOS' .global life .text life: CLR r32@0x7fffc RET EOS as "$TMP/model-init.s" "$TMP/model-init.o" as "$TMP/model-life.s" "$TMP/model-life.o" must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \ -o "$TMP/bad-split-model" "$TMP/model-init.o" "$TMP/model-life.o" \ "$TMP/libc-stub.o" cat > "$TMP/mixed.s" <<'EOS' .global main .global fini .text _start_life: main: CLR r32@0x7fffc RET fini: RET EOS as "$TMP/mixed.s" "$TMP/mixed.o" must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \ -o "$TMP/bad-mixed" "$TMP/mixed.o" "$TMP/libc-stub.o" cat > "$TMP/reserved.s" <<'EOS' .global __mcpu_crt_user_collision .text __mcpu_crt_user_collision: RET EOS as "$TMP/reserved.s" "$TMP/reserved.o" must_fail_link --sys-root="$MCPU_TEST_ROOT" --stack-size=4K --no-heap \ -o "$TMP/bad-reserved" "$TMP/reserved.o" # --------------------------------------------------------------------------- # Frozen ET_DYN ownership rule: model/lifecycle sections belong to ET_EXEC. # --------------------------------------------------------------------------- for sec in init fini control input output trace; do case "$sec" in init|fini) cat > "$TMP/shared-$sec.s" < "$TMP/shared-$sec.s" </dev/null 2>&1; then echo "ET_DYN unexpectedly accepted .$sec" >&2 exit 1 fi done cat > "$TMP/shared-lifecycle.s" <<'EOS' .text _start_life: END EOS as "$TMP/shared-lifecycle.s" "$TMP/shared-lifecycle.o" if "$CPU_LD" --shared --soname=libbad-lifecycle.so.1 \ -o "$TMP/libbad-lifecycle.so" "$TMP/shared-lifecycle.o" >/dev/null 2>&1; then echo "ET_DYN unexpectedly accepted lifecycle symbols" >&2 exit 1 fi exit 0