Re: llvm ld vs binutils ld

From: Konstantin Belousov <kostikbel_at_gmail.com>
Date: Fri, 02 Feb 2024 20:48:54 UTC
On Fri, Feb 02, 2024 at 12:07:35PM -0800, Steve Kargl wrote:
> On Sun, Jan 28, 2024 at 12:04:48PM +0200, Konstantin Belousov wrote:
> > On Sat, Jan 27, 2024 at 09:22:59PM -0800, Steve Kargl wrote:
> > > On Sat, Jan 27, 2024 at 10:29:34PM +0100, Dimitry Andric wrote:
> > > > On 27 Jan 2024, at 18:08, Steve Kargl <sgk@troutmask.apl.washington.edu> wrote:
> > > > > 
> > > > > In an attempt to cleanup a bit of src/lib/msun, I ran into
> > > > > a small issue that I cannot explain at the moment.  If I have
> > > > > /usr/bin/ld in my path prior to /usr/local/bin/ld everything
> > > > > works
> > > > > 
> > > > > % which ld
> > > > > /usr/bin/ld
> > > > > % make clean && make cleandepend
> > > > > % make
> > > > > 
> > > > > and I have a libm.so.5.  But if /usr/local/bin/ld is found, I
> > > > > see
> > > > > 
> > > > > % cd msun
> > > > > % make clean && make cleandepend
> > > > > % make
> > > > > ..
> > > > > ld: error: version script assignment of 'FBSD_1.0' to symbol 'fabs' \
> > > > >    failed: symbol not defined
> > > > > cc: error: linker command failed with exit code 1 (use -v to see invocation)
> > > > > *** Error code 1
> > > > > 
> > > > > Stop.
> > > > > make: stopped in /usr/src/lib/msun
> > > > > 
> > > > > % grep fabs /usr/src/lib/msun/Symbol.map 
> > > > >        fabs;
> > > > >        fabsf;
> > > > >        fabsl;
> > > > > 
> > > > > But, if one looks in msun/Makefile, one see
> > > > > 
> > > > > # FreeBSD's C library supplies these functions:
> > > > > #COMMON_SRCS+=  s_fabs.c s_frexp.c s_isnan.c s_ldexp.c s_modf.c
> > > > > 
> > > > > so fabs is not built with libm.  
> > > > > 
> > > > > % nm --dynamic /lib/libc.so.7 | grep fabs
> > > > > 00000000000ba600 T fabs
> > > > > % nm --dynamic /lib/libm.so.5 | grep fabs
> > > > > 000000000001fa90 T fabsf
> > > > > 00000000000252e0 T fabsl
> > > > > 
> > > > > 
> > > > > Is this a known issue?  Should fabs be removed from Symbol.map?
> > > > 
> > > > Yes, fabs is excluded in msun's Makefile:
> > > > 
> > > > # FreeBSD's C library supplies these functions:
> > > > #COMMON_SRCS+=  s_fabs.c s_frexp.c s_isnan.c s_ldexp.c s_modf.c
> > > 
> > > Thanks for the quick response.  I knew this, but
> > > 
> > > > so it should not have been in Symbol.map at all.
> > > 
> > > it has been this way for a very long time.  
> > > 
> > > > The comment is also
> > > > incorrect, since s_frexp.c and s_isnan.c *are* actually in COMMON_SRCS,
> > > > see lines 79 and 80 of the Makefile. (They are indeed also in libc, so
> > > > which one is chosen is only known by the linker. :)
> > > 
> > > I would it depends on the search order of the libraries.  For
> > > static linking, it's the order on the commandline.  For rtld,
> > > it's the order of the libraries in the cache.
> > > 
> > > % nm --dynamic /lib/libc.so.7 | grep snan
> > > 00000000000ace30 T __isnan@@FBSD_1.0
> > > 00000000000ace60 T __isnanf@@FBSD_1.0
> > > 00000000000ace30 W isnan@@FBSD_1.0
> > > 00000000000ace60 W isnanf@@FBSD_1.0
> > > % nm --dynamic /lib/libm.so.5  | grep snan
> > > 00000000000220a0 T __isnanf@@FBSD_1.2
> > > 00000000000220d0 T __isnanl@@FBSD_1.0
> > > 00000000000220a0 W isnanf@@FBSD_1.0
> > > 
> > > Not quite.  isnan is in libc but libm.  isnanf seems to be
> > > in both, and isnanl is only in libm.  Does FBSD_1.2 trump
> > > FBSD_1.0 for __isnanf?
> > No, the binary specifies which version of the symbol it wants, either
> > __isnanf@FBSD_1.2 or __isnanf@FBSD_1.0, and the dynamic linker binds to
> > the version.
> > 
> > Which version is recorded into the consumer binary, is up to the static
> > linker (ld), and there it is normally defined by the order of the libraries
> > specified on the command line.
> 
> Thanks for the explanation, but I think I now have a conundrum.
> Suppose I have two shared libraries libfoo.so and libbar.so, and
> suppose bah@@XXX_1.0 is in libbar.so's symbol map.  If my main
> program uses bah() and I compile with 'cc -o z main.c -lfoo -lbar',
> then the linker finds bah@XXX_1.0.
> 
> Now suppose a developer adds a new procedure to libfoo.so and she
> just so happens to name her new function bah() with a symbol map
> entry of bah@YYY_a.b.
> 
> Which bah@ is linked when rebuilding or loading 'z'?  Does it
> depend on the search order for ld-config.so.1?

In the 'z' binary either bah@XXX_1.0 or bah@YYY_1.0 is specified as
the symbol to resolve at the call site. Dynamic linker searches for
corresponding versioned symbol in the global namespace, typically.

So now the question is reduced to what version of the symbol bah get
recorded into the 'z' binary.  It is done by static linker, which looks
up symbols linearly in the order of object files and libraries specified
on the command line (*).  If you did 'cc -o z main.c -lfoo -lbar', and
both libfoo.so and libbar.so provided the bah symbol, the first found
definition is recorded, together with its version.  The final answer
is lookup finds bah in libfoo.so and bah@YYY_1.0 is recorded.

* There are many ways to direct static linker to find specific version
of the symbol, changing the default behavior.