In ELF linkers, undefined symbols from DSO participate in symbol resolution, just like undef from .o. The differences with undef from .o are:
- undef purely from DSO do not need .symtab entries
- --no-allow-shlib-undefined can error for such undef (-rpath-link ("load dependent libraries") behavior can suppress some errors.
ELF linkers extract an archive member to satisfy an undefined symbol from a shared object. Here is an example:
echo '.globl _start; _start: call bb' > a.s
c.a(c.o) and exports
the dynamic symbol table. ld.bfd, gold, and ld.lld have the same
This archive extraction behavior intrigued me because link.exe (PE/COFF) and ld64 (Mach-O) don't inspect undefined symbols in DLL/dylib for symbol resolution.
# On macOS
a.out doesn't define or reference
On ELF, an undefined symbol from a shared object can interact with a
.o definition. The .o definition needs to be exported to
.dynsym in case the shared object relies on symbols in the
The rationale is that a shared object may have incomplete
DT_NEEDED entries. An undefined symbol may need to be
resolved to the executable or another shared object. we can imagine that
not extracting archive members to satisfy an undefined symbol from a
shared object can cause ld.so "unresolved symbol" errors.
--no-allow-shlib-undefined may catch such fragile
However, if every shared object is linked with
--no-undefined), we can make sure a shared object
won't have unresolved symbols (provided that the runtime shared objects
have the same symbol set, which is a reasonable request.) Not extracting
archive members will be very robust.
Due to circular dependency or other reasons, it can be difficult to
make every shared object
-z defs happy. Unfortunately
--no-allow-shlib-undefined doesn't provide a way to allow
some undefined symbols.
The above reasoning might make not extracting an archive member
plausible. However, a more important factor is at play here: indirect
dependency should not affect symbol resolution. A shared object built
-z defs is not a good justification because it
involves indirect dependency. The linker can only reasonably check
direct dependencies for symbol resolution.
One definition rule violation
One definition rule is C++'s, but we can reuse the concept: multiple definitions have more or less unreliability.
ld -rpath=. a.o b.so c.a ELF example, if we add
DF_SYMBOLIC flag to
a.out will have definitions trying to interpose
c.so definitions in vain. Linkers don't seem to have any
ability to detect such fragile links.
Did "dynamic linking should be similar to static linking" affect the design?
The ELF pioneers had "dynamic linking should be similar to static linking" in mind when designing dynamic linking. I think it means two things: (a) no source-level annotation, (b) emulating archive member extraction.
ld a.o -lb c.a,
-lb may refer to either
libb.so. One might argue that the
c.a should be extracted to satisfy
b, in case
b refers to
However, an archive and a shared object have a major difference: a
shared object tracks dependencies while an archive does not. If we let
b.a track dependencies, the executable linker command line
may probably be
ld a.o -lb -lb0 c.a where
libb0.a is another archive satisfying undefined symbols in
libb.a. In this case, not extracting
members is still fine.
- https://reviews.llvm.org/D108006 (propose