[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