[RFC] Standardizing undefined symbol relocation retention in ELF ld.bfd

Hakan Candar hakancandar@protonmail.com
Wed Jul 2 07:09:31 GMT 2025


Thanks for the detailed breakdown and the links, I really appreciate it.

Below are the simple commands I used to reproduce the inconsistencies
across back-ends and versions.  Feel free to tweak or expand them as
needed.

---------------------------------  strong  ---------------------------------

/* strong.c */
extern void somefunc (void);
int main () { somefunc (); return 0; }

/* link & inspect */
gcc strong.c \
    -Wl,--unresolved-symbols=ignore-all \
    -Wl,-z,dynamic-undefined-weak \
    -o strong

objdump -R strong | grep somefunc          # relocation retained?
objdump --disassemble=main strong        # plt called correctly?

----------------------------------  weak  ----------------------------------

/* weak.c */
extern void __attribute__((weak)) somefunc (void);
int main () { somefunc (); return 0; }

/* link & inspect */
gcc weak.c \
    -Wl,--unresolved-symbols=ignore-all \
    -Wl,-z,dynamic-undefined-weak \
    -o weak

objdump -R weak | grep somefunc
objdump --disassemble=main weak

---------------------------  runtime verification  -------------------------

/* stub.c */
void somefunc (void) { }

/* build preload stub */
gcc -shared -fPIC stub.c -o libstub.so

# native run (in my case, x86-64 host)
LD_PRELOAD=./libstub.so ./strong
LD_PRELOAD=./libstub.so ./weak

# cross-run (riscv64, aarch64, ppc64le, m68k)
LD_PRELOAD=./libstub.so qemu-user-<arch> ./strong
LD_PRELOAD=./libstub.so qemu-user-<arch> ./weak

---------------------------------------------------------------------------

With older riscv64 (binutils-2.41) the PLT/GOT entries existed but the
call in main jumped to 0 instead of somefunc@plt.  In 2.44 this is fixed.
x86-64 prunes strong undefineds unless
--export-dynamic is used, while ppc64le keeps both weak and strong.
m68k prunes both strong and weak unless -fPIE and -pie is specified.

Please let me know if you need additional repro steps or if there are
edge cases I’ve missed.  Happy to iterate.

Best regards,
Hakan Candar

On 02/07/2025 07:20, Fangrui Song <maskray@sourceware.org> wrote:

>  Thanks for the table. Could you share the command used for testing?
>  I explored the behavior when writing
>  https://maskray.me/blog/2021-04-25-weak-symbol and recently revisited
>  it, confirming the inconsistency.
>  
>  For LLD I recently added -z [no]dynamic-undefined-weak for all
>  supported targets.
>  
>  * Static -no-pie: no-op
>  * Dynamic -no-pie: nodynamic-undefined-weak suppresses GLOB_DAT/JUMP_SLOT
>  * Static -pie: dynamic-undefined-weak generates
>  ABS/GLOB_DAT/JUMP_SLOT.
>  https://discourse.llvm.org/t/lld-weak-undefined-symbols-in-vdso-only/86749
>  * Dynamic -pie: nodynamic-undefined-weak suppresses ABS/GLOB_DAT/JUMP_SLOT
>  
>  Dynamic: -shared is specified, or there is at least one link-time shared object.
>  
>  (The -no-pie (!ctx.arg.isPic in isStaticLinkTimeConstant) behavior
>  will likely change in the future.)
>  
>  LLD treats undefined non-weak symbols the same as undefined weak
>  symbols. If we ever add -z dynamic-undefined, it will be an alias for
>  -z dynamic-undefined-weak.
>  


More information about the Binutils mailing list