diff options
Diffstat (limited to 'tests/tst-needed-runtime-runner.sh')
| -rwxr-xr-x | tests/tst-needed-runtime-runner.sh | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/tests/tst-needed-runtime-runner.sh b/tests/tst-needed-runtime-runner.sh new file mode 100755 index 0000000..1119ff9 --- /dev/null +++ b/tests/tst-needed-runtime-runner.sh @@ -0,0 +1,193 @@ +#!/bin/sh +set -eu + +: "${CPU_AS:?}" "${CPU_LD:?}" "${CPU_RUN:?}" "${CPU_READELF:?}" + +TMP=${TMPDIR:-/tmp}/libmcpu-needed-runtime-$$ +mkdir -p "$TMP/lib" +trap 'rm -rf "$TMP"' EXIT HUP INT TERM + +cat > "$TMP/main.s" <<'EOS' +.global _life +.extern afoo +.text +_start_life: + LDI r64@0x00000, 40 + CALL afoo + MOV r64@0x00018, r64@0x00000 + LDI r32@0x00030, 0 + EXIT r32@0x00030 +EOS + +cat > "$TMP/a.s" <<'EOS' +.global afoo +.extern bfoo +.text +afoo: + CALL bfoo + RET +EOS + +cat > "$TMP/b.s" <<'EOS' +.global bfoo +.text +bfoo: + LDI r64@0x00008, 2 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS + +"$CPU_AS" -o "$TMP/main.o" "$TMP/main.s" +"$CPU_AS" -o "$TMP/a.o" "$TMP/a.s" +"$CPU_AS" -o "$TMP/b.o" "$TMP/b.s" + +# A real recursive graph: main -> liba.so.1 -> libb.so.1. +"$CPU_LD" --shared --soname=libb.so.1 \ + -o "$TMP/lib/libb.so.1.0.0" "$TMP/b.o" +ln -s libb.so.1.0.0 "$TMP/lib/libb.so.1" +ln -s libb.so.1 "$TMP/lib/libb.so" + +"$CPU_LD" --shared --soname=liba.so.1 \ + -L"$TMP/lib" -lb -o "$TMP/lib/liba.so.1.0.0" "$TMP/a.o" +ln -s liba.so.1.0.0 "$TMP/lib/liba.so.1" +ln -s liba.so.1 "$TMP/lib/liba.so" + +"$CPU_LD" --dynamic --stack-size=4096 --no-heap \ + -L"$TMP/lib" -la -o "$TMP/main" "$TMP/main.o" + +"$CPU_READELF" -d "$TMP/main" | grep -q 'NEEDED.*\[liba.so.1\]' +"$CPU_READELF" -d "$TMP/lib/liba.so.1.0.0" | \ + grep -q 'NEEDED.*\[libb.so.1\]' +[ "`"$CPU_RUN" --library-path="$TMP/lib" \ + --int-reg=r64@0x00018 "$TMP/main"`" = 42 ] + +# A missing transitive dependency is reported by SONAME, before process load. +mv "$TMP/lib/libb.so.1" "$TMP/lib/libb.so.1.save" +if "$CPU_RUN" --library-path="$TMP/lib" "$TMP/main" \ + >"$TMP/missing.out" 2>"$TMP/missing.err"; then + echo "missing transitive dependency unexpectedly loaded" >&2 + exit 1 +fi +grep -q "cannot resolve DT_NEEDED 'libb.so.1'" "$TMP/missing.err" +mv "$TMP/lib/libb.so.1.save" "$TMP/lib/libb.so.1" + +# The resolved filename is not trusted: its internal SONAME must be the exact +# DT_NEEDED identity requested by the dependent object. +cat > "$TMP/wrong.s" <<'EOS' +.global afoo +.text +afoo: + RET +EOS +"$CPU_AS" -o "$TMP/wrong.o" "$TMP/wrong.s" +"$CPU_LD" --shared --soname=libwrong.so.9 \ + -o "$TMP/lib/libwrong.so.9.0.0" "$TMP/wrong.o" +rm "$TMP/lib/liba.so.1" +ln -s libwrong.so.9.0.0 "$TMP/lib/liba.so.1" +if "$CPU_RUN" --library-path="$TMP/lib" "$TMP/main" \ + >"$TMP/mismatch.out" 2>"$TMP/mismatch.err"; then + echo "SONAME mismatch unexpectedly loaded" >&2 + exit 1 +fi +grep -q "DT_NEEDED 'liba.so.1' resolved to SONAME 'libwrong.so.9'" \ + "$TMP/mismatch.err" +rm "$TMP/lib/liba.so.1" +ln -s liba.so.1.0.0 "$TMP/lib/liba.so.1" + +# Dependency cycles are legal graph edges. Registration by SONAME before +# recursive descent prevents A -> B -> A from recursing or loading twice. +cat > "$TMP/cycle-main.s" <<'EOS' +.global _life +.extern cycle_a +.text +_start_life: + LDI r64@0x00000, 40 + CALL cycle_a + MOV r64@0x00018, r64@0x00000 + LDI r32@0x00030, 0 + EXIT r32@0x00030 +EOS +cat > "$TMP/cycle-a.s" <<'EOS' +.global cycle_a +.text +cycle_a: + LDI r64@0x00008, 4 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS +cat > "$TMP/cycle-b.s" <<'EOS' +.global cycle_b +.text +cycle_b: + RET +EOS +"$CPU_AS" -o "$TMP/cycle-main.o" "$TMP/cycle-main.s" +"$CPU_AS" -o "$TMP/cycle-a.o" "$TMP/cycle-a.s" +"$CPU_AS" -o "$TMP/cycle-b.o" "$TMP/cycle-b.s" + +# Bootstrap one side of the cycle, build B -> A, then rebuild A -> B. +"$CPU_LD" --shared --soname=libcyclea.so.1 \ + -o "$TMP/lib/libcyclea.so.1.0.0" "$TMP/cycle-a.o" +ln -s libcyclea.so.1.0.0 "$TMP/lib/libcyclea.so.1" +ln -s libcyclea.so.1 "$TMP/lib/libcyclea.so" +"$CPU_LD" --shared --soname=libcycleb.so.1 \ + -L"$TMP/lib" -lcyclea \ + -o "$TMP/lib/libcycleb.so.1.0.0" "$TMP/cycle-b.o" +ln -s libcycleb.so.1.0.0 "$TMP/lib/libcycleb.so.1" +ln -s libcycleb.so.1 "$TMP/lib/libcycleb.so" +"$CPU_LD" --shared --soname=libcyclea.so.1 \ + -L"$TMP/lib" -lcycleb \ + -o "$TMP/lib/libcyclea.so.1.0.0" "$TMP/cycle-a.o" +"$CPU_LD" --dynamic --stack-size=4096 --no-heap \ + -L"$TMP/lib" -lcyclea -o "$TMP/cycle-main" "$TMP/cycle-main.o" +[ "`"$CPU_RUN" --library-path="$TMP/lib" \ + --int-reg=r64@0x00018 "$TMP/cycle-main"`" = 44 ] + +# Explicit preload remains the override mechanism. An ABI-v3 preload with the +# same SONAME satisfies the normal dependency and keeps the established +# explicit-preload priority. +cat > "$TMP/direct-main.s" <<'EOS' +.global _life +.extern foo +.text +_start_life: + LDI r64@0x00000, 40 + CALL foo + MOV r64@0x00018, r64@0x00000 + LDI r32@0x00030, 0 + EXIT r32@0x00030 +EOS +cat > "$TMP/foo.s" <<'EOS' +.global foo +.text +foo: + LDI r64@0x00008, 2 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS +cat > "$TMP/override.s" <<'EOS' +.global foo +.text +foo: + LDI r64@0x00008, 3 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS +"$CPU_AS" -o "$TMP/direct-main.o" "$TMP/direct-main.s" +"$CPU_AS" -o "$TMP/foo.o" "$TMP/foo.s" +"$CPU_AS" -o "$TMP/override.o" "$TMP/override.s" +"$CPU_LD" --shared --soname=libfoo.so.1 \ + -o "$TMP/lib/libfoo.so.1.0.0" "$TMP/foo.o" +ln -s libfoo.so.1.0.0 "$TMP/lib/libfoo.so.1" +ln -s libfoo.so.1 "$TMP/lib/libfoo.so" +"$CPU_LD" --shared --soname=libfoo.so.1 \ + -o "$TMP/override.so" "$TMP/override.o" +"$CPU_LD" --dynamic --stack-size=4096 --no-heap \ + -L"$TMP/lib" -lfoo -o "$TMP/direct-main" "$TMP/direct-main.o" +[ "`"$CPU_RUN" --library-path="$TMP/lib" \ + --int-reg=r64@0x00018 "$TMP/direct-main"`" = 42 ] +[ "`"$CPU_RUN" --library-path="$TMP/lib" \ + --ldpreload="$TMP/override.so" \ + --int-reg=r64@0x00018 "$TMP/direct-main"`" = 43 ] + +exit 0 |
