summaryrefslogtreecommitdiff
path: root/tests/tst-needed-runtime-runner.sh
diff options
context:
space:
mode:
Diffstat (limited to 'tests/tst-needed-runtime-runner.sh')
-rwxr-xr-xtests/tst-needed-runtime-runner.sh193
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