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

It's well known that glibc (and, as far as I know, glibstd++ also) uses symbol versioning mechanism. (For the details refer: How can I link to a specific glibc version.)
The question is how to determine exact versions of GLIBC and GLIBCXX will be chosen by linker for names from libc and libstdc++? For example, how to get something like this:
time -> time#GLIBC_2_5
...
gethostbyname -> gethostbyname#GLIBC_2_3
Why do we need this? It seems to me that it can be useful if you want to minimize required versions of glibc/libstdc++.

One thing you can try is running objdump -T on your binary. For example, here's how to see that the /usr/sbin/nordvpnd binary on my system depends on GLIBC version at least 2.18:
$ objdump -T /usr/sbin/nordvpnd | grep GLIBC | sed 's/.*GLIBC_\([.0-9]*\).*/\1/g' | sort -Vu
2.2.5
2.3
2.3.2
2.3.3
2.3.4
2.4
2.7
2.8
2.9
2.10
2.14
2.15
2.16
2.17
2.18
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

How to extract and compare the libc versions at runtime?

How to extract and compare the libc versions at runtime with the following restrictions?
stable solution (commands output parsing is discarded as this may vary)
should not rely on executing external tools like ldd, gcc or others
must work on a statically linked binary (AppRun)
will be implemented in C
Context:
In the AppImage project have been working on a feature to allow creating backward compatible bundles. To achieve this we created a program named AppRun. Things program compares the system glibc version with the one shipped in the bundle and configures the bundle to use the newer at runtime. This program is statically linked and should not depend on external tools.
Right now we scan the libc.so file for occurrences of GLIBC_X.XX and store the greater. But this is no longer working in the latest binary include in Ubuntu 20.04. The binary is 2.31 but there are no GLIBC_2.31 strings in the file therefore the method fails.
Related issues:
https://github.com/AppImageCrafters/AppRun/issues/35
https://github.com/AppImageCrafters/appimage-builder/issues/145
This program compares the system glibc version with the one shipped in the bundle and configures the bundle to use the newer at runtime.
Note that "configuring at runtime" involves more than just pointing the programs to the "correct" libc.so.6, as this answer explains.
Right now we scan the libc.so file for occurrences of GLIBC_X.XX and store the greater. But this is no longer working in the latest binary include in Ubuntu 20.04.
You appear to have a fundamental misunderstanding of how symbol versioning works. This answer may help understanding this better.
Your method is working.
The binary is 2.31 but there are no GLIBC_2.31 strings in the file therefore the method fails.
As above referenced answer explains, this means that GLIBC-2.31 didn't introduce any new ABI-incompatible symbols, and programs linked against that version of GLIBC will work when older GLIBC version is present at runtime (but may run into GLIBC bugs fixed between versions 2.30 and 2.31, etc.).
the libc versions
There are alternatives to glibc.
Right now we scan the libc.so file for occurrences of GLIBC_X.XX
Search for the string that glibc prints - GNU C Library <pkg+release_name> release version <version>[,.]..., and version has format [0-9]+\.[0-9]+(\.[0-9]+)?.
Just find libc.so.6 in /etc/ld.so.cache.
Do this in C:
libcfile="$(grep -azm1 '/libc.so.6$' /etc/ld.so.cache | tr -d '\0')"
grep -aoP 'GNU C Library [^\n]* release version \K[0-9]*.[0-9]*' "$libcfile"
Looking at commits, this should work since forever:
https://github.com/bminor/glibc/blame/b7eb84454020c59d528e826ae1889f411ed69e26/version.c#L28
https://github.com/bminor/glibc/blame/9f70e81bcaa12b0673cd0879d6f4a21ad6dddce5/version.c#L28
https://github.com/bminor/glibc/blame/92e4b6a92716f8b2457376291171a6330d072b0d/csu/version.c#L27
https://github.com/bminor/glibc/blame/50c66c7acd90f257b295e58bf938ed120cbc27c7/csu/version.c#L27
https://github.com/bminor/glibc/blame/b92a49359f33a461db080a33940d73f47c756126/csu/version.c#L27
Seems to work on the oldest ubuntu in docker I can find:
$ cmd='grep -aoP "GNU C Library [^\n]* release version \K[0-9]*.[0-9]*" "$(grep -azm1 "/libc.so.6$" /etc/ld.so.cache | tr -d "\0")"'
$ echo -n 'local: '; sh -c "$cmd"; for i in 13.04 16.04 14.04 22.04 21.10 20.04 18.04; do echo -n "ubuntu:$i "; docker run -ti --rm ubuntu:$i sh -c "$cmd"; done
local: 2.33
ubuntu:13.04 2.17
ubuntu:16.04 2.23
ubuntu:14.04 2.19
ubuntu:22.04 2.34
ubuntu:21.10 2.34
ubuntu:20.04 2.31
ubuntu:18.04 2.27
I also tested opensuse12.2 with glibc2.15, libc.so.6 prints GNU C Library stable release version 2.15 (20120628), by Roland McGrath et al. there.

How to find which part of my Rust project uses GLIBC 2.18

With cargo-tree, I can see my project depends on libc v0.2.62
$ cargo tree -p libc -i | grep libc
libc v0.2.62
But it actually requires two versions GLIB_2.14 and GLIBC2.18.
ldd error messages are as follows:
/lib64/libc.so.6: version `GLIBC_2.18' not found
/lib64/libc.so.6: version `GLIBC_2.14' not found
I am able to get GLIBC_2.14 but not GLIBC_2.18. So I plan to switch to older versions of Rust or some crates I use. I need to find out which one depends on GLIBC_2.18 first. Can anyone help me?
To see which symbols reference GLIBC_2.18, you can use eu-readelf:
$ eu-readelf -s /usr/bin/cargo | grep -F #GLIBC_2.18
157: 0000000000000000 0 FUNC WEAK DEFAULT UNDEF __cxa_thread_atexit_impl#GLIBC_2.18 (19)
This symbol is used by the Rust runtime itself to implement destructors (the Drop trait) for TLS variables.
If this is about getting Rust to work on Red Hat Enterprise Linux, you should be using Rust Toolset. It is regularly rebased against the latest upstream version, so its Rust version is fairly current most of the time.

Working with lower version of GLIBC: version `GLIBC_2.11` not found (required by g++)

I've installed GCC 7.1 on my machine and tried to use g++ on it, but it didn't work, saying this:
g++: /lib64/libc.so.6: version `GLIBC_2.11` not found (required by g++)
So then I did these:
$ strings /lib64/lib.so.6 | grep GLIB
GLIBC_2.2.5
GLIBC_2.2.6
GLIBC_2.3
GLIBC_2.3.2
GLIBC_2.3.3
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.5
GLIBC_PRIVATE
$ strings `which g++` | grep GLIB
GLIBC_2.3
GLIBC_2.11
GLIBC_2.2.5
Two things can be noted here:
The string GLIBC_2.11 isn't common to both of these outputs.
However, GLIBC_2.3 is common to both.
Questions:
1. What exactly do these strings mean? Why are there more than one string in both of them? What do they tell us?
2. My guess is that the absence of GLIBC_2.11 in libc explains why g++ doesn't work, as g++ requires it (as the error says itself). However, I'm confused what does the presence of GLIBC_2.3 in both actually mean? Does it mean that g++ can be instructed to use this instead of GLIBC_2.11? If so, how exactly? What is the command?
GLIBC_2.3 and GLIBC_2.11 are symbol versions. The dynamic linker uses them to perform a quick check for library compatibility at program startup: the system glibc must provide all symbol versions referenced by the program. Your glibc is apparently 2.5 (which would match Red Hat Enterprise Linux 5). This version lacks quite a few features added in later versions, and it turns out the pre-compiled GCC binary you are trying to run needs some of them.
To resolve this, you'll need a version of GCC compiled specifically for Red Hat Enterprise Linux 5. I haven't tried if the current GCC upstream sources build with the system compiler, which is … quite ancient by this time (although upstream still sticks with C++03 for exactly such needs). Some libstdc++ functionality may also need a newer kernel than 2.6.18, and some care is necessary to preserve compatibility between the new libstdc++ and the one installed with the system.

How to view glibc compilation options

Glibc 2.10(or any >2.10) with compile flag PER_THREAD and ATOMIC_FASTBINS behaves totally different then glibc 2.10 without those flags.
If my Linux is using glibc 2.10 I still don't know the exact version because it doesn't say anything about compilation flags. Ubuntu may use those flags in theirs glibc and Debian not?
How to list used compilation parameters, having glibc shared library file?
You won't find this information in /lib/libc.so.6. Though, if you're running Debian or Ubuntu you can still grab the source package (apt-get source libc6) and have a look at debian/rules file.
You can also write a quick test that checks glibc behavior and conclude if it has been compiled with these flags or not.

Impact on upgrade gcc or binutils

I want to use Red Hat Enterprise Linux 5.8 to compile folly, and gcc is 4.1.2. But folly needs gcc >=4.6. So I need to upgrade gcc to 4.6, maybe binutils etc. If new toolchain generates binary files such as *.a *.so, would running them on old Red Hat Enterprise Linux 5.8 bring any problem or unstabitily?
Probably no big trouble (because GCC 4.1 & soon released GCC 4.8 produces compatible code), at least for C code.
For C++ code I believe there is a incompatibility. Certainly the libstdc++ is tied to the compiler version. Sometimes it may be compatible in practice, sometimes not (but the C++ ABI slightly changed too). And this incompatibility may spread to every C++ library.
I believe you should try. If you recompile -with the same newest GCC- all the C++ dependencies, you'll probably be quite safe. So you could compile the newest GCC, then compile folly and all its dependencies with it.
BTW, I would suggest going to the latest GCC (today 4.7, in a few weeks 4.8).

Resources