summaryrefslogtreecommitdiff
path: root/tests/tst-link-needed-runner.sh
diff options
context:
space:
mode:
authorkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
committerkx <kx@radix-linux.su>2026-10-09 18:00:15 +0300
commit065da3509019b15dd0e678072e6be629e5330bef (patch)
tree69c934c9e786ac048b4fbe8f4c4c5571661521f4 /tests/tst-link-needed-runner.sh
parentd130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff)
downloadlibmcpu-0.4.0.tar.xz
Version 0.4.00.4.0
Diffstat (limited to 'tests/tst-link-needed-runner.sh')
-rwxr-xr-xtests/tst-link-needed-runner.sh151
1 files changed, 151 insertions, 0 deletions
diff --git a/tests/tst-link-needed-runner.sh b/tests/tst-link-needed-runner.sh
new file mode 100755
index 0000000..8f33986
--- /dev/null
+++ b/tests/tst-link-needed-runner.sh
@@ -0,0 +1,151 @@
+#!/bin/sh
+set -eu
+
+: "${CPU_AS:?}" "${CPU_LD:?}" "${CPU_RUN:?}" "${CPU_READELF:?}"
+
+TMP=${TMPDIR:-/tmp}/libmcpu-link-needed-$$
+HOME="$TMP/home"
+export HOME
+mkdir -p "$HOME/.mcpu/etc" "$TMP/root/lib" "$TMP/first"
+trap 'rm -rf "$TMP"' EXIT HUP INT TERM
+
+cat > "$TMP/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/good.s" <<'EOS'
+.global foo
+.text
+foo:
+ LDI r64@0x00008, 2
+ IADD r64@0x00000, r64@0x00000, r64@0x00008
+ RET
+EOS
+
+cat > "$TMP/alt.s" <<'EOS'
+.global foo
+.text
+foo:
+ LDI r64@0x00008, 3
+ IADD r64@0x00000, r64@0x00000, r64@0x00008
+ RET
+EOS
+
+cat > "$TMP/bad.s" <<'EOS'
+.global other
+.text
+other:
+ RET
+EOS
+
+"$CPU_AS" -o "$TMP/main.o" "$TMP/main.s"
+"$CPU_AS" -o "$TMP/good.o" "$TMP/good.s"
+"$CPU_AS" -o "$TMP/alt.o" "$TMP/alt.s"
+"$CPU_AS" -o "$TMP/bad.o" "$TMP/bad.s"
+
+# The development link name is libfoo.so, but the ABI identity recorded in
+# DT_NEEDED comes from the validated library SONAME.
+"$CPU_LD" --shared --soname=libfoo.so.1 \
+ -o "$TMP/root/lib/libfoo.so.1.2.3" "$TMP/good.o"
+ln -s libfoo.so.1.2.3 "$TMP/root/lib/libfoo.so.1"
+ln -s libfoo.so.1 "$TMP/root/lib/libfoo.so"
+
+"$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -L"$TMP/root/lib" -lfoo -o "$TMP/main" "$TMP/main.o"
+"$CPU_READELF" -d "$TMP/main" > "$TMP/main.dynamic"
+grep -q 'NEEDED.*\[libfoo.so.1\]' "$TMP/main.dynamic"
+[ "`grep -c 'NEEDED' "$TMP/main.dynamic"`" -eq 1 ]
+if grep -q 'libfoo.so.1.2.3' "$TMP/main.dynamic"; then
+ echo "DT_NEEDED unexpectedly contains physical library filename" >&2
+ exit 1
+fi
+
+# The separated -l NAME spelling is equivalent.
+"$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -L "$TMP/root/lib" -l foo -o "$TMP/main-separated" "$TMP/main.o"
+"$CPU_READELF" -d "$TMP/main-separated" | grep -q 'NEEDED.*\[libfoo.so.1\]'
+
+# Repeated requests resolving to the same SONAME produce one dependency.
+"$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -L"$TMP/root/lib" -lfoo -lfoo -o "$TMP/main-duplicate" "$TMP/main.o"
+"$CPU_READELF" -d "$TMP/main-duplicate" > "$TMP/main-duplicate.dynamic"
+[ "`grep -c 'NEEDED' "$TMP/main-duplicate.dynamic"`" -eq 1 ]
+
+# -L order is real link-name search order. The selected object's SONAME,
+# not the requested link name, is recorded.
+"$CPU_LD" --shared --soname=libfoo-alt.so.7 \
+ -o "$TMP/first/libfoo.so" "$TMP/alt.o"
+"$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -L"$TMP/first" -L"$TMP/root/lib" -lfoo \
+ -o "$TMP/main-first" "$TMP/main.o"
+"$CPU_READELF" -d "$TMP/main-first" | grep -q 'NEEDED.*\[libfoo-alt.so.7\]'
+
+# The configured system library tree is also used by -l.
+cat > "$HOME/.mcpu/etc/mcpu-ld.conf" <<EOC
+MCPU_LD_SYSTEM_LIBRARY_PATH = $TMP/root/lib;
+EOC
+"$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -lfoo -o "$TMP/main-config" "$TMP/main.o"
+"$CPU_READELF" -d "$TMP/main-config" | grep -q 'NEEDED.*\[libfoo.so.1\]'
+
+# A shared object may itself record a direct ABI-v3 dependency.
+"$CPU_LD" --shared --soname=libchild.so.1 \
+ -lfoo -o "$TMP/libchild.so.1.0.0" "$TMP/bad.o"
+"$CPU_READELF" -d "$TMP/libchild.so.1.0.0" > "$TMP/child.dynamic"
+grep -q 'SONAME.*\[libchild.so.1\]' "$TMP/child.dynamic"
+grep -q 'NEEDED.*\[libfoo.so.1\]' "$TMP/child.dynamic"
+
+# A named dependency must exist and must be a valid ABI-v3 ET_DYN object.
+if "$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -L"$TMP/root/lib" -lmissing -o "$TMP/missing" "$TMP/main.o" \
+ >/dev/null 2>&1; then
+ echo "missing -l dependency unexpectedly linked" >&2
+ exit 1
+fi
+
+cp "$TMP/main" "$TMP/root/lib/libexec.so"
+if "$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -L"$TMP/root/lib" -lexec -o "$TMP/from-exec" "$TMP/main.o" \
+ >/dev/null 2>&1; then
+ echo "ET_EXEC unexpectedly accepted as -l provider" >&2
+ exit 1
+fi
+
+# Existing file, valid shared object, wrong exports: link must fail before
+# producing an executable.
+"$CPU_LD" --shared --soname=libbad.so.1 \
+ -o "$TMP/root/lib/libbad.so" "$TMP/bad.o"
+rm -f "$TMP/no-provider"
+if "$CPU_LD" --dynamic --stack-size=4096 --no-heap \
+ -L"$TMP/root/lib" -lbad -o "$TMP/no-provider" "$TMP/main.o" \
+ >"$TMP/no-provider.out" 2>"$TMP/no-provider.err"; then
+ echo "library without required export unexpectedly linked" >&2
+ exit 1
+fi
+grep -q 'undefined symbol not provided by linked libraries: foo' \
+ "$TMP/no-provider.err"
+[ ! -e "$TMP/no-provider" ]
+
+# ABI-v1 -l searches only for a static archive. This directory contains
+# only libfoo.so, so the static link must fail instead of using ET_DYN.
+if "$CPU_LD" -L"$TMP/root/lib" -lfoo -o "$TMP/static" "$TMP/main.o" \
+ >/dev/null 2>&1; then
+ echo "ABI-v1 unexpectedly selected a shared -l provider" >&2
+ exit 1
+fi
+
+# 0.2.16 resolves the recorded SONAME at runtime through the same system
+# library search policy. No explicit --ldpreload is required.
+[ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/main"`" = 42 ]
+
+"$CPU_LD" --help 2>&1 | grep -q -- '-l <name>'
+
+exit 0