lld and GNU ld can pull different archive members
I assumed two linkers fed the same archives in the same order would produce the same binary. Debugging a build divergence, I found out they don’t — ld.lld (LLVM’s linker) pulled an object out of one archive, ld.bfd (the GNU linker, what most people mean by “GNU ld”) pulled a same-named object out of a different one. Same sources, same link line.
Both archives defined the same symbol with different bodies — that’s the underlying bug. It only stayed hidden because each linker happened to pick a different one.
- GNU ld searches each archive once, where it sits on the link line, and only pulls members for symbols that are already undefined at that point. A reference that shows up later never sends it back to an earlier archive (short of
--start-group/--end-group). - lld remembers every archive symbol it has seen, so a later reference can pull a member out of an archive that came earlier.
- So with
main.o liba.a use.o libb.a, whereuse.ocallswhich()and both archives define it, GNU ld (and gold) takewhichfromlibb.a, and lld takes it fromliba.a. Neither linker is broken; they just follow different rules for archives.
Haven’t confirmed that’s what bit me, but it’s the best-known way the two disagree. To see which member each linker took, pass -Wl,-y,which (--trace-symbol) to both links. lld also has --why-extract=-, and GNU ld’s map file (-Wl,-Map=out.map) lists each pulled member under “Archive member included to satisfy reference by file (symbol)”. lld’s --warn-backrefs doesn’t help here: it warns when GNU ld would fail to link, and stays quiet when GNU ld would just pick another member.
For now: don’t ship same-name symbols with different bodies across archives and expect the linker to disambiguate them. Which one wins is an implementation detail, and switching linkers (or versions) can flip it silently.