I read the following questions:
And I still cannot get, why there is W
for e.g. puts
?
nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep puts
0000000000080e50 W puts@@GLIBC_2.2.5
Maybe, it somehow relates to LD_DYNAMIC_WEAK
, but what's the point, if libc.so
is loaded last by dynamic linker anyway, so there is no problem in overriding it by some another shared library: I checked that I can override even T
symbols of libc.so
(both by putting alternative definitions into a main program and into another shared library linked to the main program).
One could ask the same question about the ~3K weak function symbols in libstdc++.so.6
. In general
when a shared library defines weak dynamic symbols they arise this way: There is a good
reason for the compiler of the library source code to emit these symbols as weak, and
they pass as such through the linkage of the shared library into its dynamic symbol table because
there is no good reason to bother differentiating the shared library build in a way that
would turn them into strong ones. The motives for emitting weak symbols at all are various.
The weak API symbols of GLIBC don't originate from considerations of dynamic linkage but from considerations of static linkage. Usually they they arise from macro-invocations in the source code of the form:
weak_alias(stub_name,default_name)
being expanded to:
extern __typeof (default_name) stub_name \
__attribute__ ((weak, alias ("default_name"))) \
__attribute__ ((__copy__ (default_name)));
The source file glibc/libio/ioputs.c
provides an example,
which expands to:
extern __typeof (_IO_puts) puts \
__attribute__ ((weak, alias ("_IO_puts"))) \
__attribute__ ((__copy__ (_IO_puts)));
The effect of that in a static linkage that consumes libc.a(ioputs.o)
is to make puts
become a weak symbol such that,
if the linkage provides a strong definition then that strong definition is linked and
otherwise the definition of _IO_puts
is linked with puts
as an alias thereof.
This weak alias enables a shared library or program that links libc.a(ioputs.o)
to
provide an overriding strong definition of puts
, which itself may invoke the default implemention
_IO_puts
, by linking that strong definition either unconditionally from an object file
or on demand from an archive member any point after a reference to puts
has accrued. If that definition
invokes _IO_puts
it does not matter that this reference will trigger the linkage of
libc.a(ioputs.o)
, in which both IO_puts
and puts
are defined, because the latter is defined as a weak alias
of the former: no multiple definition error will ensue.
That is how the weak_alias
symbols of GLIBC are useful when the library is
statically linked. The only issue that they implicate for dynamic linkage is
the wholly general one of whether the dynamic linker (ld.so
) honours the static linker's
semantic distinction of weak and strong symbols. Per the documentation you refer to,
when ld.so
was built with GLIBC < 2.2, it did so; since GLIBC 2.2 (2000-11-09 ) it did not,
unless LD_DYNAMIC_WEAK
was defined in the runtime environment, and since GLIBC 2.3.4 (2004-12-29) it
does not, unless LD_DYNAMIC_WEAK
is defined in the runtime environment and secure-execution
is not operative. The only scenario in which the legacy behaviour bears upon dynamically
overriding weak symbols of GLIBC is that in which we contrive for libc.so
to be loaded before the
shared library intended to do the overriding. In that case by default the overriding will of course
not happen, but if we coerce the pre-2.2 legacy behavior then it will.
In this light, why would the maintainers as a matter of policy take the trouble of configuring the libc.so
build so as to make the compiler emit strong symbols in lieu of weak ones that weak_alias
emits by default?
By default, the dynamic linker makes no distinction between weak and strong. And as long as the
LD_DYNAMIC_WEAK
behaviour hangs on at all, its affect on the weak APIs of libc.so
is just per manual, however unlikely
it is to be useful in the case of libc.so
.
On the other hand it is far from an exceptionless rule that symbols that are weak aliases in libc.a
are also
weak in libc.so
:
$ readelf --syms --wide /usr/lib/x86_64-linux-gnu/libc.a | egrep 'FUNC.*WEAK.*DEFAULT' | wc -l
1015
versus:
$ readelf --dyn-syms --wide /usr/lib/x86_64-linux-gnu/libc.so.6 | egrep 'FUNC.*WEAK.*DEFAULT' | wc -l
749
Sometimes a reason arises to differentiate the shared library build in a way that
that turns an otherwise weak symbol into a strong one. In a comment, @rustyx identified fclose
as one of those 266
exceptions to the rule. In that case, investigation
shows that the methods of defining fclose
differ between the static and
shared builds, with the former using:
extern __typeof (__new_fclose) fclose \
__attribute__ ((weak, alias ("__new_fclose"))) \
__attribute__ ((__copy__ (__new_fclose)));;
while the latter uses:
__asm__ (".symver " "__new_fclose" "," "fclose" "@@" "GLIBC_2.2.5");
and where in both cases __new_fclose
is a strong alias for _IO_new_fclose
.
Taking the motives for such differentiation as read, there's
no reason why the shared library build should fight the fact that fclose@@GLIBC_2.2.5
comes out strong rather than weak if the dynamic linker is indifferent.
If we contrive the far-fetched runtime scenario where LD_DYNAMIC_WEAK
is in force,
secure-execution is not, and libc.so
is forced to load before some shared library that defines
both puts
(weak) and fclose
(strong), we indeed see that GLIBC's puts
is overriden but
its fclose
is not, so GLIBC is inconsistent in this scenario, but I suspect that
the maintainers have long ceased to resist the rot of their LD_DYNAMIC_WEAK
support.
All told, the dynamic symbols of GLIBC are weak or strong according to the path of least resistance, because it makes no difference to the dynamic linker (except in that far-fetched scenario).