[RFC] Standardizing undefined symbol relocation retention in ELF ld.bfd
Hakan Candar
hakancandar@protonmail.com
Mon Jul 7 13:24:11 GMT 2025
Hello,
I now have access to my toolchains again and have re-run the tests
against yesterday’s master (binutils 2.44.50.20250706). Below is an updated
table that drops the historical columns and shows only the behaviour
present in current sources.
Target | Version | Keeps Weak | Keeps Strong | Accepts -z dynamic-undefined-weak
--------------+--------------------+------------+--------------+-------------------------
x86_64 | 2.44.50.20250706 | Yes | No | Yes
aarch64 | 2.44.50.20250706 | Yes | No | No (warns)
ppc64le | 2.44.50.20250706 | Yes | Yes | Yes
riscv64 | 2.44.50.20250706 | Yes | Yes | No (warns)
mips64 | 2.44.50.20250706 | No | No | No (warns)
m68k | 2.44.50.20250706 | No | No | No (warns)
sh4 | 2.44.50.20250706 | No | No | No (warns)
ia64 | 2.44.50.20250706 | No | No | No (warns)
Please see bottom for the test script I used. The tests were automated to
minimize the possibility of human error.
Observations:
* riscv64 behaves correctly in master.
* m68k, sh4, ia64 and mips64 still prune both weak and strong symbols
and warn on `-z dynamic-undefined-weak`.
I agree with you that following the gABI across *all* back-ends is the
saner approach, and aligns with what LLD already does (LLD treats weak
and strong undefined symbols identically and provides `-z
[no]dynamic-undefined-weak` for every target; thanks to Fangrui Song
for pointing this out). My plan is therefore:
1. Move the “retain / prune” decision into shared code (elflink.c).
2. Provide a single set of `-z dynamic-undefined[-weak]` flags, with
defaults matching gABI unless a psABI explicitly overrides.
3. Add test-suite coverage so the behaviour stays consistent.
I will start drafting patches along those lines. If there are any
concerns with that direction (or targets that *must* retain their
current behaviour), please let me know and I will adjust.
Thanks again for the guidance and reviews.
Best regards,
Hakan Candar
--------------------- gen-undef-table.sh ---------------------
#!/usr/bin/env bash
# gen-undef-table.sh -- Summarise ld.bfd undefined-symbol behaviour
# Requires cross toolchains: {aarch64,x86_64,m68k,ppc64le,riscv64,mips64,sh4,ia64}-none-linux-gnu
set -euo pipefail
targets=(
x86_64-none-linux-gnu
aarch64-none-linux-gnu
ppc64le-none-linux-gnu
riscv64-none-linux-gnu
mips64-none-linux-gnu
m68k-none-linux-gnu
sh4-none-linux-gnu
ia64-none-linux-gnu
)
printf -- "Target | Version | Keeps Weak | Keeps Strong | Accepts -z dynamic-undefined-weak\n"
printf -- "--------------+--------------------+------------+--------------+-------------------------\n"
for trip in "${targets[@]}"; do
CC=${trip}-gcc
OBJDUMP=${trip}-objdump
# Skip if toolchain missing
if ! command -v "$CC" >/dev/null 2>&1; then
printf "%-14s| n/a | n/a | n/a | n/a\n" "${trip%%-*}"
continue
fi
LD=$(${CC} -print-prog-name=ld)
# --- compile strong.c ----------------------------------------------------
"$CC" strong.c \
-Wl,--unresolved-symbols=ignore-all \
-Wl,-z,dynamic-undefined-weak \
-o strong."$trip" 2>strong."$trip".log || true
strong_warn=$(grep -F "dynamic-undefined-weak" strong."$trip".log || true)
strong_reloc=$("$OBJDUMP" -R strong."$trip" | grep -q somefunc && echo "Yes" || echo "No")
# --- compile weak.c ------------------------------------------------------
"$CC" weak.c \
-Wl,--unresolved-symbols=ignore-all \
-Wl,-z,dynamic-undefined-weak \
-o weak."$trip" 2>weak."$trip".log || true
weak_warn=$(grep -F "dynamic-undefined-weak" weak."$trip".log || true)
weak_reloc=$("$OBJDUMP" -R weak."$trip" | grep -q somefunc && echo "Yes" || echo "No")
# --- binutils version ----------------------------------------------------
version=$("$LD" --version | head -1 | awk '{print $NF}')
# --- summarise -----------------------------------------------------------
keeps_weak=$weak_reloc
keeps_strong=$strong_reloc
accepts_flag=$([[ -z $strong_warn && -z $weak_warn ]] && echo "Yes" || echo "No (warns)")
printf "%-14s| %-18s | %-10s | %-12s | %s\n" \
"${trip%%-*}" "$version" "$keeps_weak" "$keeps_strong" "$accepts_flag"
done
--------------------- weak.c ---------------------
extern void __attribute__((weak)) somefunc(void);
int main() { somefunc(); return 0; }
--------------------- strong.c ---------------------
extern void somefunc(void);
int main() { somefunc(); return 0; }
More information about the Binutils
mailing list