What Does Version Info in Ldd -V Mean

What does version info in ldd -v mean?

What does that mean?

It means that liblapack.so requires versioned symbols from libc.so.6 with versions GLIBC_2.2.5, GLIBC_2.4 and GLIBC_2.14. You can read about versioned symbols here.

What version of libc.so.6 does this liblapack.so require?

It requires 2.14 or newer. In general, GLIBC never removes symbols, only adds new ones, and so will still provide symbols versioned at GLIBC_2.2.5 even in the latest GLIBC-2.24.

If it did ever remove such "old" versioned symbol, that would break any old binaries that depended on that symbol (which is why it's not done).

How can I get liblapack.so's version?

It doesn't look like liblapack.so itself is using any versioned symbols. You can look at your package manager to find out what version of liblapack.so you have. Something like:

dpkg -S /usr/lib/lapack/liblapack.so
liblapack-dev: /usr/lib/lapack/liblapack.so

dpkg -l liblapack-dev
...
ii liblapack-dev 3.5.0-2ubuntu1 amd64 Library of linear algebra routines 3 - static version

understanding ldd verbose: what are the multiple versions?

What does it mean when multiple versions are listed?

It means that your binary references symbols with these versions. See also this and this answer.

Does that mean that any of those versions workable, or that it needs all those versions?

The latter. Or rather, it needs a library that provides all of these symbols, which generally means GLIBC_2.27 or later for GLIBC, and GCC_4.2.0 or later for libgcc.

Hierarchical ldd(1)

If you are running Portage≥2.2 with FEATURES=preserve-libs, you should rarely ever need revdep-rebuild anymore as old .so.vers will be preserved as needed (though you still need to rebuild carefully, as stuff still goes kaboom when libA.so.0 wants libC.so.0 and libB.so.0 wants libC.so.1 and some binary wants both libA.so.0 and libB.so.0).


That being said, what ldd does is to get the dynamic linker to do load the executable or library as it usually would, but print out some info along the way. This is a recursive "binary needs library needs other library&hellip" search, because that's what the dynamic linker does.

I'm currently running Linux/ppc32; on Linux/x86, the dynamic linker is usually /lib/ld-linux.so.2, and on Linux/x86_64, the dynamic linker is usually /lib/ld-linux-x86-64.so.2. Here, I call it directly just to hammer in the point that all ldd is nothing more than a shell script that calls upon the dynamic linker to perform its magic.


$ /lib/ld.so.1 /sbin/badblocks
Usage: /sbin/badblocks [-b block_size] [-i input_file] [-o output_file] [-svwnf]
[-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]
[-p num_passes] [-t test_pattern [-t test_pattern [...]]]
device [last_block [first_block]]
$ LD_TRACE_LOADED_OBJECTS=1 /lib/ld.so.1 /sbin/badblocks
linux-vdso32.so.1 => (0x00100000)
libext2fs.so.2 => /lib/libext2fs.so.2 (0x0ffa8000)
libcom_err.so.2 => /lib/libcom_err.so.2 (0x0ff84000)
libc.so.6 => /lib/libc.so.6 (0x0fdfa000)
libpthread.so.0 => /lib/libpthread.so.0 (0x0fdc0000)
/lib/ld.so.1 (0x48000000)
$ LD_TRACE_LOADED_OBJECTS=1 /lib/ld.so.1 /lib/libcom_err.so.2
linux-vdso32.so.1 => (0x00100000)
libpthread.so.0 => /lib/libpthread.so.0 (0x6ffa2000)
libc.so.6 => /lib/libc.so.6 (0x6fe18000)
/lib/ld.so.1 (0x203ba000)
$ grep -l pthread /sbin/badblocks /lib/libcom_err.so.2
/lib/libcom_err.so.2

/sbin/badblocks doesn't list libpthread.so.0 as a library dependency, but it gets pulled in by libcom_err.so.2.

Is your problem that ldd doesn't output a nice-looking dependency tree? Use ldd -v.


$ LD_TRACE_LOADED_OBJECTS=1 LD_VERBOSE=1 /lib/ld.so.1 /sbin/badblocks
linux-vdso32.so.1 => (0x00100000)
libext2fs.so.2 => /lib/libext2fs.so.2 (0x0ffa8000)
libcom_err.so.2 => /lib/libcom_err.so.2 (0x0ff84000)
libc.so.6 => /lib/libc.so.6 (0x0fdfa000)
libpthread.so.0 => /lib/libpthread.so.0 (0x0fdc0000)
/lib/ld.so.1 (0x201f9000)

Version information:
/sbin/badblocks:
libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
libc.so.6 (GLIBC_2.3.4) => /lib/libc.so.6
/lib/libext2fs.so.2:
libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
libc.so.6 (GLIBC_2.3) => /lib/libc.so.6
libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
/lib/libcom_err.so.2:
ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
libpthread.so.0 (GLIBC_2.1) => /lib/libpthread.so.0
libpthread.so.0 (GLIBC_2.0) => /lib/libpthread.so.0
libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/libc.so.6
/lib/libc.so.6:
ld.so.1 (GLIBC_PRIVATE) => /lib/ld.so.1
ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
/lib/libpthread.so.0:
ld.so.1 (GLIBC_2.3) => /lib/ld.so.1
ld.so.1 (GLIBC_2.1) => /lib/ld.so.1
ld.so.1 (GLIBC_PRIVATE) => /lib/ld.so.1
libc.so.6 (GLIBC_2.1.3) => /lib/libc.so.6
libc.so.6 (GLIBC_2.3.4) => /lib/libc.so.6
libc.so.6 (GLIBC_2.4) => /lib/libc.so.6
libc.so.6 (GLIBC_2.1) => /lib/libc.so.6
libc.so.6 (GLIBC_2.3.2) => /lib/libc.so.6
libc.so.6 (GLIBC_2.2) => /lib/libc.so.6
libc.so.6 (GLIBC_PRIVATE) => /lib/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/libc.so.6

If you want, you can read the ELF headers directly instead of depending on the dynamic linker.


$ readelf -d /sbin/badblocks | grep NEEDED
0x00000001 (NEEDED) Shared library: [libext2fs.so.2]
0x00000001 (NEEDED) Shared library: [libcom_err.so.2]
0x00000001 (NEEDED) Shared library: [libc.so.6]
$ readelf -d /lib/libcom_err.so.2 | grep NEEDED
0x00000001 (NEEDED) Shared library: [libpthread.so.0]
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld.so.1]

You can also man ld.so for other cute tricks you can play with glibc's dynamic linker.

ldd hex number in parentheses

The hexadecimal numbers are the memory addresses the respective library gets loaded into. See https://stackoverflow.com/a/5130690/637284 for further explanation.

Understanding ldd output

It is recorded inside application binary itself (specified at compile time, more exactly at link step, done with ld):

$ readelf -d /bin/echo

Dynamic section at offset 0x5f1c contains 21 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
...

(there are some additional columns for how elf does store information in dynamic section. but you can see that libc.so.6 is hardcoded with .6 suffix because of SONAME)

or even without any knowledge of ELF file format:

$ strings /bin/echo |grep libc.so
libc.so.6

To find, how does linker find a library (it is done at final step of compilation), use gcc option -Wl,--verbose (this asks gcc to pass option --verbose to ld):

$ gcc a.c -Wl,--verbose

...
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.so failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.a failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.so failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/libc.a failed
attempt to open /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/../../../libc.so succeeded
opened script file /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/../../../libc.so
opened script file /usr/lib/gcc/i686-pc-linux-gnu/4.4.4/../../../libc.so
attempt to open /lib/libc.so.6 succeeded
/lib/libc.so.6

Linker doesn't know anything about .digit suffix, it just iterate over all library search directories trying to open libLIBNAME.so and libLIBNAME.a, where LIBNAME is a string after -l option. ( -lc option is added by default).

First success is /usr/lib/libc.so which itself is not a library, but a linker script (text file). Here is content from typical libc.so script:

$ cat /usr/lib/libc.so
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf32-i386)
GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a AS_NEEDED ( /lib/ld-linux.so.2 ) )

So, script /usr/lib/libc.so is found earlier than actual library, and this script says, what file will be linked, libc.so.6 in this case.

In more common case, lib___.so is symlink to some version like lib___.so.3.4.5 and there is SONAME field filled in lib___.so.3.4.5 which says to ld link not to lib___.so but to lib___.so.3.4 which is another symlink to lib___.so.3.4.5. The .3.4 name will be recorded in NEEDED field of binary.

ld to library without version information in Linux

I was able to build the library as-desired, without the version information.

What I ended up doing was creating a dummy library with the appropriate exported symbols--without version (or soname) information--and linking to that dummy library while compiling and linking my library. At runtime, the loader loads the real library (libxul.so) without failing based on the versioning problems since my library does not contain version information for the real library. To figure out which exported symbols I needed, I first linked to the real libxul.so, then used readelf --dyn-syms to determine dwhich symbols were actually needed.

Where ARCH is 32 or 64 (for 32-bit or 64-bit compilation):
gcc -o $ARCH/libxul.so -fPIC -shared -DM$ARCH -m$ARCH xulstubs.c

xulstubs.c:

/*
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_Realloc@xul26 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_UTF16ToCString@xul26 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_CStringCloneData@xul26 (2)
13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_GetMemoryManager@xul26 (2)
...
*/
void NS_Realloc() {}
void NS_UTF16ToCString() {}
void NS_CStringCloneData() {}
void NS_GetMemoryManager() {}
/* ... etc. ... */

Now, the imported symbols from my library do not have the version appendage:

23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND NS_Realloc
29: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_UTF16ToCString
33: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_CStringCloneData
37: 0000000000000000 0 FUNC GLOBAL DEFAULT UND NS_GetMemoryManager

and it runs as desired.

I would still like a more “elegant” solution, one that does not require creating a dummy library. If this is the only workaround using the standard toolchain, however, I can live with it.

How to determine version of glibc (glibcxx) binary will depend on?

One thing you can try is running objdump -T on your binary.

If you are considering linking to older versions of symbols, be aware that these older versions may depend on older, different structures or other definitions as well. To avoid this, compile and link with older, matching header files and libraries.



Related Topics



Leave a reply



Submit