#!/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