#!/bin/bash set -e CPU_AS=${CPU_AS:-../utils/mcpu-as} CPU_LD=${CPU_LD:-../utils/mcpu-ld} CPU_RUN=${CPU_RUN:-../utils/mcpu-run} CPU_READELF=${CPU_READELF:-../utils/mcpu-readelf} CPU_AR=${CPU_AR:-../utils/mcpu-ar} TMP=${TMPDIR:-/tmp}/libmcpu-partial-link-$$ trap 'rm -rf "$TMP"' EXIT HUP INT TERM mkdir -p "$TMP/lib" cat > "$TMP/main.s" <<'EOS' .global _life .extern add_two .extern shared_value .text _start_life: LDI r64@0x00000, 40 CALL add_two STORE shared_value, r64@0x00000 LOAD r64@0x00018, shared_value LDI r32@0x00030, 0 EXIT r32@0x00030 EOS cat > "$TMP/lib.s" <<'EOS' .global add_two .global shared_value .global shared_ptr .global add_two_ptr .data .align 32 shared_value: .integer 64, 0 shared_ptr: .ptr shared_value add_two_ptr: .cptr add_two .text add_two: 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/lib.o" "$TMP/lib.s" # A complete partial link is still ET_REL and may be used as the sole input # object of a later final link. "$CPU_LD" -r -o "$TMP/combined.o" "$TMP/main.o" "$TMP/lib.o" "$CPU_READELF" -h "$TMP/combined.o" > "$TMP/combined.header" "$CPU_READELF" -s "$TMP/combined.o" > "$TMP/combined.symbols" "$CPU_READELF" -r "$TMP/combined.o" > "$TMP/combined.relocs" grep -q 'Type:.*REL (Relocatable file)' "$TMP/combined.header" grep -q 'add_two' "$TMP/combined.symbols" grep -q 'shared_value' "$TMP/combined.symbols" grep -q 'add_two' "$TMP/combined.relocs" grep -q 'shared_value' "$TMP/combined.relocs" "$CPU_LD" --stack-size=4096 --no-heap -o "$TMP/combined-app" "$TMP/combined.o" [ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/combined-app"`" = 42 ] # -r does not require lifecycle completeness and preserves unresolved symbols. "$CPU_LD" -r -o "$TMP/main-partial.o" "$TMP/main.o" "$CPU_READELF" -s "$TMP/main-partial.o" | grep -q 'UND.*add_two' "$CPU_LD" --stack-size=4096 --no-heap \ -o "$TMP/late-app" "$TMP/main-partial.o" "$TMP/lib.o" [ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/late-app"`" = 42 ] "$CPU_LD" -r -o "$TMP/library-only.o" "$TMP/lib.o" "$CPU_READELF" -h "$TMP/library-only.o" | grep -q 'REL (Relocatable file)' # LOCAL symbols from separate objects remain private even when they have the # same spelling. Their relocations must still refer to their own definitions. cat > "$TMP/local-a.s" <<'EOS' .global fa .text fa: CALL local_helper RET local_helper: LDI r64@0x00008, 1 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/local-b.s" <<'EOS' .global fb .text fb: CALL local_helper RET local_helper: LDI r64@0x00008, 2 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/local-main.s" <<'EOS' .global _life .extern fa .extern fb .text _start_life: LDI r64@0x00000, 39 CALL fa CALL fb MOV r64@0x00018, r64@0x00000 LDI r32@0x00030, 0 EXIT r32@0x00030 EOS "$CPU_AS" -o "$TMP/local-a.o" "$TMP/local-a.s" "$CPU_AS" -o "$TMP/local-b.o" "$TMP/local-b.s" "$CPU_AS" -o "$TMP/local-main.o" "$TMP/local-main.s" "$CPU_LD" -r -o "$TMP/local-combined.o" "$TMP/local-a.o" "$TMP/local-b.o" "$CPU_LD" --stack-size=4096 --no-heap \ -o "$TMP/local-app" "$TMP/local-main.o" "$TMP/local-combined.o" [ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/local-app"`" = 42 ] # A strong GLOBAL definition replaces a WEAK definition in partial output. cat > "$TMP/weak.s" <<'EOS' .weak pick .text pick: LDI r64@0x00008, 1 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/strong.s" <<'EOS' .global pick .text pick: LDI r64@0x00008, 2 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS 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 "$CPU_AS" -o "$TMP/weak.o" "$TMP/weak.s" "$CPU_AS" -o "$TMP/strong.o" "$TMP/strong.s" "$CPU_AS" -o "$TMP/pick-main.o" "$TMP/pick-main.s" "$CPU_LD" -r -o "$TMP/pick.o" "$TMP/weak.o" "$TMP/strong.o" "$CPU_LD" --stack-size=4096 --no-heap \ -o "$TMP/pick-app" "$TMP/pick-main.o" "$TMP/pick.o" [ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/pick-app"`" = 42 ] # Two strong definitions are already an error at partial-link time. cat > "$TMP/clash-a.s" <<'EOS' .global clash .text clash: RET EOS cat > "$TMP/clash-b.s" <<'EOS' .global clash .text clash: RET EOS "$CPU_AS" -o "$TMP/clash-a.o" "$TMP/clash-a.s" "$CPU_AS" -o "$TMP/clash-b.o" "$TMP/clash-b.s" if "$CPU_LD" -r -o "$TMP/clash.o" "$TMP/clash-a.o" "$TMP/clash-b.o" \ > /dev/null 2> "$TMP/clash.err"; then echo "partial link unexpectedly accepted duplicate GLOBAL definitions" >&2 exit 1 fi grep -q 'multiple definition of global symbol: clash' "$TMP/clash.err" # -r may extract required ET_REL members from libNAME.a, but never consumes a # same-named shared object. The unused archive member must stay outside the # partial object even though it contains an unresolved reference of its own. cat > "$TMP/usepart.s" <<'EOS' .global call_part .extern part .text call_part: CALL part RET EOS cat > "$TMP/part-static.s" <<'EOS' .global part .text part: LDI r64@0x00008, 2 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/part-shared.s" <<'EOS' .global part .text part: LDI r64@0x00008, 3 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS cat > "$TMP/unused.s" <<'EOS' .global unused_archive_member .extern missing_from_unused_member .text unused_archive_member: CALL missing_from_unused_member RET EOS cat > "$TMP/archive-main.s" <<'EOS' .global _life .extern call_part .text _start_life: LDI r64@0x00000, 40 CALL call_part MOV r64@0x00018, r64@0x00000 LDI r32@0x00030, 0 EXIT r32@0x00030 EOS "$CPU_AS" -o "$TMP/usepart.o" "$TMP/usepart.s" "$CPU_AS" -o "$TMP/part-static.o" "$TMP/part-static.s" "$CPU_AS" -o "$TMP/part-shared.o" "$TMP/part-shared.s" "$CPU_AS" -o "$TMP/unused.o" "$TMP/unused.s" "$CPU_AS" -o "$TMP/archive-main.o" "$TMP/archive-main.s" "$CPU_AR" rcs "$TMP/lib/libpart.a" "$TMP/part-static.o" "$TMP/unused.o" "$CPU_LD" --shared --soname=libpart.so.1 \ -o "$TMP/lib/libpart.so.1.0.0" "$TMP/part-shared.o" ln -s libpart.so.1.0.0 "$TMP/lib/libpart.so.1" ln -s libpart.so.1 "$TMP/lib/libpart.so" "$CPU_LD" -r -L"$TMP/lib" -lpart -o "$TMP/from-archive.o" "$TMP/usepart.o" "$CPU_READELF" -s "$TMP/from-archive.o" > "$TMP/from-archive.symbols" grep -q ' part$' "$TMP/from-archive.symbols" if grep -q 'unused_archive_member' "$TMP/from-archive.symbols"; then echo "partial link unexpectedly extracted an unused archive member" >&2 exit 1 fi "$CPU_LD" --stack-size=4096 --no-heap \ -o "$TMP/archive-app" "$TMP/archive-main.o" "$TMP/from-archive.o" [ "`"$CPU_RUN" --int-reg=r64@0x00018 "$TMP/archive-app"`" = 42 ] # The resulting partial object is an ordinary ET_REL and can itself be stored # in a static archive. "$CPU_AR" rcs "$TMP/libcombined.a" "$TMP/combined.o" [ "`"$CPU_AR" t "$TMP/libcombined.a"`" = combined.o ] # Options that describe final executable/shared-image state are invalid in -r. if "$CPU_LD" -r --dynamic -o "$TMP/bad.o" "$TMP/lib.o" >/dev/null 2>&1; then echo "-r --dynamic unexpectedly accepted" >&2 exit 1 fi if "$CPU_LD" -r --shared --soname=bad.so -o "$TMP/bad.o" "$TMP/lib.o" >/dev/null 2>&1; then echo "-r --shared unexpectedly accepted" >&2 exit 1 fi if "$CPU_LD" -r --stack-size=4096 -o "$TMP/bad.o" "$TMP/lib.o" >/dev/null 2>&1; then echo "-r --stack-size unexpectedly accepted" >&2 exit 1 fi if "$CPU_LD" -r --no-heap -o "$TMP/bad.o" "$TMP/lib.o" >/dev/null 2>&1; then echo "-r --no-heap unexpectedly accepted" >&2 exit 1 fi if "$CPU_LD" -r --soname=bad.so -o "$TMP/bad.o" "$TMP/lib.o" >/dev/null 2>&1; then echo "-r --soname unexpectedly accepted" >&2 exit 1 fi "$CPU_LD" --help 2>&1 | grep -q -- '-r.*partial-link MELF ET_REL' exit 0