#!/bin/sh set -eu : "${CPU_AS:?}" "${CPU_LD:?}" "${CPU_RUN:?}" TMP=${TMPDIR:-/tmp}/libmcpu-symbol-scope-$$ mkdir -p "$TMP/lib" trap 'rm -rf "$TMP"' EXIT HUP INT TERM cat > "$TMP/main-import.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/a.s" <<'EOS' .global foo .text foo: LDI r64@0x00008, 2 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/b.s" <<'EOS' .global foo .text foo: LDI r64@0x00008, 3 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS "$CPU_AS" -o "$TMP/main-import.o" "$TMP/main-import.s" "$CPU_AS" -o "$TMP/a.o" "$TMP/a.s" "$CPU_AS" -o "$TMP/b.o" "$TMP/b.s" "$CPU_LD" --shared --soname=liba.so.1 -o "$TMP/lib/liba.so.1.0.0" "$TMP/a.o" "$CPU_LD" --shared --soname=libb.so.1 -o "$TMP/lib/libb.so.1.0.0" "$TMP/b.o" ln -s liba.so.1.0.0 "$TMP/lib/liba.so.1" ln -s liba.so.1 "$TMP/lib/liba.so" ln -s libb.so.1.0.0 "$TMP/lib/libb.so.1" ln -s libb.so.1 "$TMP/lib/libb.so" # Normal DT_NEEDED scope is breadth-first and left-to-right. For equal # GLOBAL definitions the first direct dependency wins. "$CPU_LD" --dynamic --stack-size=4096 --no-heap \ -L"$TMP/lib" -la -lb -o "$TMP/main-ab" "$TMP/main-import.o" [ "`"$CPU_RUN" --library-path="$TMP/lib" --int-reg=r64@0x00018 "$TMP/main-ab"`" = 42 ] "$CPU_LD" --dynamic --stack-size=4096 --no-heap \ -L"$TMP/lib" -lb -la -o "$TMP/main-ba" "$TMP/main-import.o" [ "`"$CPU_RUN" --library-path="$TMP/lib" --int-reg=r64@0x00018 "$TMP/main-ba"`" = 43 ] # A main-executable GLOBAL outranks normal dependencies. cat > "$TMP/main-own.s" <<'EOS' .global _life .global foo .text _start_life: LDI r64@0x00000, 40 CALL foo MOV r64@0x00018, r64@0x00000 LDI r32@0x00030, 0 EXIT r32@0x00030 foo: LDI r64@0x00008, 1 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS "$CPU_AS" -o "$TMP/main-own.o" "$TMP/main-own.s" "$CPU_LD" --dynamic --stack-size=4096 --no-heap \ -L"$TMP/lib" -la -o "$TMP/main-own" "$TMP/main-own.o" [ "`"$CPU_RUN" --library-path="$TMP/lib" --int-reg=r64@0x00018 "$TMP/main-own"`" = 41 ] # Explicit preload is the intentional override scope. Equal-strength GLOBAL # preloads keep the historical rightmost-higher-priority rule. [ "`"$CPU_RUN" --library-path="$TMP/lib" \ --ldpreload=liba.so:libb.so \ --int-reg=r64@0x00018 "$TMP/main-own"`" = 43 ] [ "`"$CPU_RUN" --library-path="$TMP/lib" \ --ldpreload=libb.so:liba.so \ --int-reg=r64@0x00018 "$TMP/main-own"`" = 42 ] # Binding strength outranks scope class: a WEAK preload must not preempt a # normal GLOBAL definition. cat > "$TMP/weak.s" <<'EOS' .weak foo .text foo: LDI r64@0x00008, 9 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS "$CPU_AS" -o "$TMP/weak.o" "$TMP/weak.s" "$CPU_LD" --shared --soname=libweak.so.1 -o "$TMP/libweak.so" "$TMP/weak.o" [ "`"$CPU_RUN" --library-path="$TMP/lib" --ldpreload="$TMP/libweak.so" \ --int-reg=r64@0x00018 "$TMP/main-ab"`" = 42 ] # If only WEAK definitions exist, the same scope ordering applies: first normal # dependency wins, while rightmost explicit preload wins. cat > "$TMP/weak-main.s" <<'EOS' .global _life .weak 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/wa.s" <<'EOS' .weak foo .text foo: LDI r64@0x00008, 2 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/wb.s" <<'EOS' .weak foo .text foo: LDI r64@0x00008, 3 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS "$CPU_AS" -o "$TMP/weak-main.o" "$TMP/weak-main.s" "$CPU_AS" -o "$TMP/wa.o" "$TMP/wa.s" "$CPU_AS" -o "$TMP/wb.o" "$TMP/wb.s" "$CPU_LD" --shared --soname=libwa.so.1 -o "$TMP/lib/libwa.so.1" "$TMP/wa.o" "$CPU_LD" --shared --soname=libwb.so.1 -o "$TMP/lib/libwb.so.1" "$TMP/wb.o" ln -s libwa.so.1 "$TMP/lib/libwa.so" ln -s libwb.so.1 "$TMP/lib/libwb.so" "$CPU_LD" --dynamic --stack-size=4096 --no-heap \ -L"$TMP/lib" -lwa -lwb -o "$TMP/weak-main" "$TMP/weak-main.o" [ "`"$CPU_RUN" --library-path="$TMP/lib" --int-reg=r64@0x00018 "$TMP/weak-main"`" = 42 ] [ "`"$CPU_RUN" --ldpreload="$TMP/lib/libwa.so.1:$TMP/lib/libwb.so.1" \ --int-reg=r64@0x00018 "$TMP/weak-main"`" = 43 ] # A direct dependency outranks a conflicting transitive dependency. The # resolver must therefore build a true breadth-first scope rather than expose # DFS traversal order to symbol resolution. cat > "$TMP/deep.s" <<'EOS' .global deep_anchor .global foo .text deep_anchor: RET foo: LDI r64@0x00008, 9 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/mid.s" <<'EOS' .global mid_anchor .extern deep_anchor .text mid_anchor: CALL deep_anchor RET EOS cat > "$TMP/direct.s" <<'EOS' .global foo .text foo: LDI r64@0x00008, 3 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/bfs-main.s" <<'EOS' .global _life .extern mid_anchor .extern foo .text _start_life: LDI r64@0x00000, 40 CALL mid_anchor CALL foo MOV r64@0x00018, r64@0x00000 LDI r32@0x00030, 0 EXIT r32@0x00030 EOS "$CPU_AS" -o "$TMP/deep.o" "$TMP/deep.s" "$CPU_AS" -o "$TMP/mid.o" "$TMP/mid.s" "$CPU_AS" -o "$TMP/direct.o" "$TMP/direct.s" "$CPU_AS" -o "$TMP/bfs-main.o" "$TMP/bfs-main.s" "$CPU_LD" --shared --soname=libdeep.so.1 -o "$TMP/lib/libdeep.so.1.0.0" "$TMP/deep.o" ln -s libdeep.so.1.0.0 "$TMP/lib/libdeep.so.1" ln -s libdeep.so.1 "$TMP/lib/libdeep.so" "$CPU_LD" --shared --soname=libmid.so.1 -L"$TMP/lib" -ldeep \ -o "$TMP/lib/libmid.so.1.0.0" "$TMP/mid.o" ln -s libmid.so.1.0.0 "$TMP/lib/libmid.so.1" ln -s libmid.so.1 "$TMP/lib/libmid.so" "$CPU_LD" --shared --soname=libdirect.so.1 -o "$TMP/lib/libdirect.so.1.0.0" "$TMP/direct.o" ln -s libdirect.so.1.0.0 "$TMP/lib/libdirect.so.1" ln -s libdirect.so.1 "$TMP/lib/libdirect.so" "$CPU_LD" --dynamic --stack-size=4096 --no-heap \ -L"$TMP/lib" -lmid -ldirect -o "$TMP/bfs-main" "$TMP/bfs-main.o" [ "`"$CPU_RUN" --library-path="$TMP/lib" --int-reg=r64@0x00018 "$TMP/bfs-main"`" = 43 ] # Diamond graph: A and B both depend on C. SONAME deduplication loads C once, # while the direct A/B scope still follows left-to-right order. cat > "$TMP/common.s" <<'EOS' .global common_anchor .text common_anchor: RET EOS cat > "$TMP/da.s" <<'EOS' .global a_anchor .global foo .extern common_anchor .text a_anchor: CALL common_anchor RET foo: LDI r64@0x00008, 2 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/db.s" <<'EOS' .global b_anchor .global foo .extern common_anchor .text b_anchor: CALL common_anchor RET foo: LDI r64@0x00008, 3 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/diamond-main.s" <<'EOS' .global _life .extern a_anchor .extern b_anchor .extern foo .text _start_life: LDI r64@0x00000, 40 CALL a_anchor CALL b_anchor CALL foo MOV r64@0x00018, r64@0x00000 LDI r32@0x00030, 0 EXIT r32@0x00030 EOS "$CPU_AS" -o "$TMP/common.o" "$TMP/common.s" "$CPU_AS" -o "$TMP/da.o" "$TMP/da.s" "$CPU_AS" -o "$TMP/db.o" "$TMP/db.s" "$CPU_AS" -o "$TMP/diamond-main.o" "$TMP/diamond-main.s" "$CPU_LD" --shared --soname=libcommon.so.1 -o "$TMP/lib/libcommon.so.1.0.0" "$TMP/common.o" ln -s libcommon.so.1.0.0 "$TMP/lib/libcommon.so.1" ln -s libcommon.so.1 "$TMP/lib/libcommon.so" "$CPU_LD" --shared --soname=libda.so.1 -L"$TMP/lib" -lcommon \ -o "$TMP/lib/libda.so.1.0.0" "$TMP/da.o" ln -s libda.so.1.0.0 "$TMP/lib/libda.so.1" ln -s libda.so.1 "$TMP/lib/libda.so" "$CPU_LD" --shared --soname=libdb.so.1 -L"$TMP/lib" -lcommon \ -o "$TMP/lib/libdb.so.1.0.0" "$TMP/db.o" ln -s libdb.so.1.0.0 "$TMP/lib/libdb.so.1" ln -s libdb.so.1 "$TMP/lib/libdb.so" "$CPU_LD" --dynamic --stack-size=4096 --no-heap \ -L"$TMP/lib" -lda -ldb -o "$TMP/diamond-main" "$TMP/diamond-main.o" [ "`"$CPU_RUN" --library-path="$TMP/lib" --int-reg=r64@0x00018 "$TMP/diamond-main"`" = 42 ] exit 0