diff options
| author | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
|---|---|---|
| committer | kx <kx@radix-linux.su> | 2026-10-09 18:00:15 +0300 |
| commit | 065da3509019b15dd0e678072e6be629e5330bef (patch) | |
| tree | 69c934c9e786ac048b4fbe8f4c4c5571661521f4 /tests/tst-static-archive-link-runner.sh | |
| parent | d130fe27fa753be02ed76fd9b277aa3c3d928eb9 (diff) | |
| download | libmcpu-0.4.0.tar.xz | |
Version 0.4.00.4.0
Diffstat (limited to 'tests/tst-static-archive-link-runner.sh')
| -rwxr-xr-x | tests/tst-static-archive-link-runner.sh | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/tests/tst-static-archive-link-runner.sh b/tests/tst-static-archive-link-runner.sh new file mode 100755 index 0000000..596fa16 --- /dev/null +++ b/tests/tst-static-archive-link-runner.sh @@ -0,0 +1,263 @@ +#!/bin/sh +set -eu + +: "${CPU_AS:?}" "${CPU_LD:?}" "${CPU_RUN:?}" "${CPU_READELF:?}" "${CPU_AR:?}" + +TMP=${TMPDIR:-/tmp}/libmcpu-static-archive-link-$$ +mkdir -p "$TMP/lib" "$TMP/first" "$TMP/second" "$TMP/order" +trap 'rm -rf "$TMP"' EXIT HUP INT TERM + +cat > "$TMP/main-entry.s" <<'EOS' +.global _life +.extern entry +.text +_start_life: + LDI r64@0x00000, 40 + CALL entry + MOV r64@0x00018, r64@0x00000 + LDI r32@0x00030, 0 + EXIT r32@0x00030 +EOS + +cat > "$TMP/helper.s" <<'EOS' +.global helper +.text +helper: + LDI r64@0x00008, 2 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS + +cat > "$TMP/entry.s" <<'EOS' +.global entry +.extern helper +.text +entry: + CALL helper + RET +EOS + +cat > "$TMP/unused.s" <<'EOS' +.global unused +.extern missing_forever +.text +unused: + CALL missing_forever + RET +EOS + +"$CPU_AS" -o "$TMP/main-entry.o" "$TMP/main-entry.s" +"$CPU_AS" -o "$TMP/helper.o" "$TMP/helper.s" +"$CPU_AS" -o "$TMP/entry.o" "$TMP/entry.s" +"$CPU_AS" -o "$TMP/unused.o" "$TMP/unused.s" + +# The archive intentionally has no symbol-index member. helper.o precedes +# entry.o: entry.o is extracted first, introduces an undefined helper, and a +# repeated scan must then reach the earlier helper.o. unused.o must never be +# extracted, otherwise its missing_forever reference would break the link. +"$CPU_AR" rc "$TMP/lib/libchain.a" \ + "$TMP/helper.o" "$TMP/entry.o" "$TMP/unused.o" + +"$CPU_LD" --stack-size=4096 --no-heap -L"$TMP/lib" -lchain \ + -o "$TMP/static-chain" "$TMP/main-entry.o" +[ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/static-chain"`" = 42 ] + +# ABI-v3 also falls back to a static archive when no libchain.so exists. +# Extracted members are part of the output image and therefore add no NEEDED. +"$CPU_LD" --dynamic --stack-size=4096 --no-heap \ + -L"$TMP/lib" -lchain -o "$TMP/dynamic-chain" "$TMP/main-entry.o" +[ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/dynamic-chain"`" = 42 ] +"$CPU_READELF" -d "$TMP/dynamic-chain" > "$TMP/dynamic-chain.dynamic" +[ "`grep -c 'NEEDED' "$TMP/dynamic-chain.dynamic" || true`" -eq 0 ] + +# ABI-v3 ET_DYN output follows the same static-archive fallback rule. The +# selected members become part of the shared object and do not add NEEDED. +cat > "$TMP/wrapper.s" <<'EOS' +.global wrapper +.extern entry +.text +wrapper: + CALL entry + RET +EOS + +"$CPU_AS" -o "$TMP/wrapper.o" "$TMP/wrapper.s" +"$CPU_LD" --shared --soname=libwrapper.so.1 \ + -L"$TMP/lib" -lchain -o "$TMP/libwrapper.so" "$TMP/wrapper.o" +"$CPU_READELF" -d "$TMP/libwrapper.so" > "$TMP/libwrapper.dynamic" +grep -q 'SONAME.*\[libwrapper.so.1\]' "$TMP/libwrapper.dynamic" +[ "`grep -c 'NEEDED' "$TMP/libwrapper.dynamic" || true`" -eq 0 ] + +# A weak undefined symbol does not cause archive extraction. Its PTR64 +# relocation therefore remains the established NULL weak result. +cat > "$TMP/weak-main.s" <<'EOS' +.global _life +.weak optional +.data +optional_ptr: + .ptr optional +.text +_start_life: + LDI r32@0x00030, 0 + EXIT r32@0x00030 +EOS + +cat > "$TMP/optional.s" <<'EOS' +.global optional +.text +optional: + RET +EOS + +"$CPU_AS" -o "$TMP/weak-main.o" "$TMP/weak-main.s" +"$CPU_AS" -o "$TMP/optional.o" "$TMP/optional.s" +"$CPU_AR" rcs "$TMP/lib/liboptional.a" "$TMP/optional.o" +"$CPU_LD" --stack-size=4096 --no-heap -L"$TMP/lib" -loptional \ + -o "$TMP/weak-main" "$TMP/weak-main.o" +[ "`"$CPU_RUN" --int-data=64@16 "$TMP/weak-main"`" = 0 ] + +# Static archive order across -l operands follows traditional one-pass library +# semantics. A later archive can satisfy references introduced by an earlier +# one, but an already-scanned archive is not implicitly revisited. +cat > "$TMP/order-main.s" <<'EOS' +.global _life +.extern a1 +.text +_start_life: + LDI r64@0x00000, 40 + CALL a1 + MOV r64@0x00018, r64@0x00000 + LDI r32@0x00030, 0 + EXIT r32@0x00030 +EOS + +cat > "$TMP/a1.s" <<'EOS' +.global a1 +.extern b1 +.text +a1: + CALL b1 + RET +EOS + +cat > "$TMP/a2.s" <<'EOS' +.global a2 +.text +a2: + LDI r64@0x00008, 2 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS + +cat > "$TMP/b1.s" <<'EOS' +.global b1 +.extern a2 +.text +b1: + CALL a2 + RET +EOS + +"$CPU_AS" -o "$TMP/order-main.o" "$TMP/order-main.s" +"$CPU_AS" -o "$TMP/a1.o" "$TMP/a1.s" +"$CPU_AS" -o "$TMP/a2.o" "$TMP/a2.s" +"$CPU_AS" -o "$TMP/b1.o" "$TMP/b1.s" +"$CPU_AR" rcs "$TMP/order/liba.a" "$TMP/a1.o" "$TMP/a2.o" +"$CPU_AR" rcs "$TMP/order/libb.a" "$TMP/b1.o" + +if "$CPU_LD" --stack-size=4096 --no-heap -L"$TMP/order" \ + -la -lb -o "$TMP/order-fail" "$TMP/order-main.o" \ + >/dev/null 2>"$TMP/order-fail.err"; then + echo "previous archive was unexpectedly rescanned across -l operands" >&2 + exit 1 +fi +grep -q 'undefined symbol: a2' "$TMP/order-fail.err" + +"$CPU_LD" --stack-size=4096 --no-heap -L"$TMP/order" \ + -la -lb -la -o "$TMP/order-ok" "$TMP/order-main.o" +[ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/order-ok"`" = 42 ] + +# Prepare two providers for -lpick: a static one returns 42, a shared one 43. +cat > "$TMP/pick-main.s" <<'EOS' +.global _life +.extern pick +.text +_start_life: + LDI r64@0x00000, 40 + CALL pick + MOV r64@0x00018, r64@0x00000 + LDI r32@0x00030, 0 + EXIT r32@0x00030 +EOS + +cat > "$TMP/pick-static.s" <<'EOS' +.global pick +.text +pick: + LDI r64@0x00008, 2 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS + +cat > "$TMP/pick-shared.s" <<'EOS' +.global pick +.text +pick: + LDI r64@0x00008, 3 + IADD r64@0x00000, r64@0x00000, r64@0x00008 + RET +EOS + +"$CPU_AS" -o "$TMP/pick-main.o" "$TMP/pick-main.s" +"$CPU_AS" -o "$TMP/pick-static.o" "$TMP/pick-static.s" +"$CPU_AS" -o "$TMP/pick-shared.o" "$TMP/pick-shared.s" + +"$CPU_AR" rcs "$TMP/first/libpick.a" "$TMP/pick-static.o" +"$CPU_LD" --shared --soname=libpick.so.7 \ + -o "$TMP/second/libpick.so.7.0.0" "$TMP/pick-shared.o" +ln -s libpick.so.7.0.0 "$TMP/second/libpick.so.7" +ln -s libpick.so.7 "$TMP/second/libpick.so" + +# Search is directory-major. The .a in the first -L directory wins over a +# .so in a later directory. +"$CPU_LD" --dynamic --stack-size=4096 --no-heap \ + -L"$TMP/first" -L"$TMP/second" -lpick \ + -o "$TMP/pick-first-static" "$TMP/pick-main.o" +[ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/pick-first-static"`" = 42 ] +"$CPU_READELF" -d "$TMP/pick-first-static" > "$TMP/pick-first-static.dynamic" +[ "`grep -c 'NEEDED' "$TMP/pick-first-static.dynamic" || true`" -eq 0 ] + +# Reversing -L order selects the shared object and records its SONAME. +"$CPU_LD" --dynamic --stack-size=4096 --no-heap \ + -L"$TMP/second" -L"$TMP/first" -lpick \ + -o "$TMP/pick-first-shared" "$TMP/pick-main.o" +"$CPU_READELF" -d "$TMP/pick-first-shared" > "$TMP/pick-first-shared.dynamic" +grep -q 'NEEDED.*\[libpick.so.7\]' "$TMP/pick-first-shared.dynamic" +[ "`"$CPU_RUN" --library-path="$TMP/second" \ + --int-reg=r64@0x00018 "$TMP/pick-first-shared"`" = 43 ] + +# Within one directory ABI-v3 prefers .so over .a. +cp "$TMP/first/libpick.a" "$TMP/second/libpick.a" +"$CPU_LD" --dynamic --stack-size=4096 --no-heap \ + -L"$TMP/second" -lpick -o "$TMP/pick-same-dir" "$TMP/pick-main.o" +"$CPU_READELF" -d "$TMP/pick-same-dir" | grep -q 'NEEDED.*\[libpick.so.7\]' + +# ABI-v1 never selects .so for -l. It uses the archive even when both exist. +"$CPU_LD" --stack-size=4096 --no-heap \ + -L"$TMP/second" -lpick -o "$TMP/pick-abi1" "$TMP/pick-main.o" +[ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/pick-abi1"`" = 42 ] + +# With only a shared provider available, ABI-v1 -l must fail rather than +# silently introducing a dynamic dependency into a static executable. +rm -f "$TMP/second/libpick.a" +if "$CPU_LD" --stack-size=4096 --no-heap \ + -L"$TMP/second" -lpick -o "$TMP/pick-abi1-bad" "$TMP/pick-main.o" \ + >/dev/null 2>&1; then + echo "ABI-v1 unexpectedly selected a shared library" >&2 + exit 1 +fi + +"$CPU_LD" --help 2>&1 | grep -q 'ABI-v1 and -r search .a' +"$CPU_LD" --help 2>&1 | grep -q 'ABI-v3 searches .so then .a' + +exit 0 |
