How to extract and compare the libc versions at runtime? - linux

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.

Related

Building older GLIBC on newer system

I have a question and I hope some guru here could help me out :) My goal is simple : (GOAL1) build a older glibc on a newer system and (GOAL2) build old software that can run on older glibc. I am on gcc4.9, glibc2.19, amd64 system. I did compile glibc2.14 and gcc4.7.3 on my system.
(convention : /path/to/libc2.14_dir = $LIBC214, /path/to/gcc4.7.3 = $GCC473)
I am trying to compile bash4.2.53 ( and other softwares coreutil, binutil, qt3.3...) using newly built glibc2.14. My configure and make looks like this : ( I am being in object/build directory )
$ cd /path/to/build/bash-4.2.53
$ CC=$GCC473/bin/gcc CXX=$GCC473/bin/g++ CPP=$GCC473/bin/cpp \
/path/to/source/bash-4.2.53/configure \
--prefix=/path/to/installation/bash-4.2.53
$ make all V=1 \
CFLAGS="-Wl,--rpath=$LIBC214/lib -Wl,--dynamic-linker=$LIBC214/lib/ld-linux-x86-64.so.2" \
CPPFLAGS="-Wl,--rpath=$LIBC214/lib -Wl,--dynamic-linker=$LIBC214/lib/ld-linux-x86-64.so.2" \
CXXFLAGS="-Wl,--rpath=$LIBC214/lib -Wl,--dynamic-linker=$LIBC214/lib/ld-linux-x86-64.so.2"
When make is done in bash4.2.53 build (object) directory, I tried 2 scenarios :
SCENARIO1. Use system's libc2.19
$ ldd ./bash
linux-vdso.so.1 (0x00007ffe677c3000)
libtinfo.so.5 => $MY_PREBUILT_NCURSE/lib/libtinfo.so.5 (0x00007f5d108e3000)
libdl.so.2 => $LIBC214/lib/libdl.so.2 (0x00007f5d106df000)
libc.so.6 => $LIBC214/lib/libc.so.6 (0x00007f5d10354000)
$LIBC214/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f5d10b0a000)
This line is weird :$LIBC214/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007f5d10b0a000)
$ $LIBC214/bin/ldd ./bash
not a dynamic executable
SCENARIO2. Add $LIBC214/bin to my PATH, $LIBC214/lib to my LD_LIBRARY_PATH
$ ldd ./bash
/bin/bash: $LIBC214/lib/libc.so.6: version `GLIBC_2.15' not found (required by /bin/bash)
$ export SHELL=$PWD/bash
$ ldd --version
/bin/bash: $LIBC214/lib/libc.so.6: version `GLIBC_2.15' not found (required by /bin/bash)
$ ldd ./bash
/bin/bash: $LIBC214/lib/libc.so.6: version `GLIBC_2.15' not found (required by /bin/bash)
As far as I can tell, SCENARIO2 is the right way to run commands. My assumption is that I built glibc2.14 wrongly, that it uses /bin/bash which in turn uses glibc2.19. I am not really sure.
My question:
Could you please explain for me the weird line above and the following line that out put not a dynamic executable?
Could you share me your steps to properly build glibc (GOAL1) and build software using that glibc (GOAL2) ?
What could I do next to solve 2 above scenario?
Thank you.
when you link ELFs, the absolute path to the ldso is hardcoded in it. if you want to use a different one, you can use the -Wl,--dynamic-linker=/path/to/ldso flag to override it. this path is absolute though, so if you try to move your local glibc somewhere else, the ELFs you linked will fail to execute (with errors like no such file). there's no way around this as the system, by design, must have an absolute path in it to the interpreter.
the reason you get weird not a dynamic executable output is that ldd works by actually executing the target ELF and extracting (via debug hooks) the libraries that get loaded. so when you attempt to use a diff ldd like that, glibc gets confused. if you want a stable dependency lister, you might consider something like lddtree from the pax-utils project (most distros have this bundled nowadays). or run readelf -d on the file directly and look at all the DT_NEEDED entries.
trying to build & maintain your own toolchain (glibc, gcc, etc...) is a huge hassle. unless you want to dedicate a lot of time to this, you really should use a project like crosstool-ng to manage it for you. that will allow you to build a full toolchain using an older glibc, and then you can use that to build whatever random packages you like.
if you really really want to spend time doing this all by hand, you should refer to the glibc wiki for building your own glibc and then linking apps against that glibc.

version `GLIBC_2.11' not found while using gcc

I have problem using gcc/g++ after I changed the machine I use,
I installed gcc-4.9.2 in my previous machine, when I moved to the new machine, I copied gcc folder to the new machine.
When I try to use I get this error :
/data/obenchek/gcc-4.9.2/bin/g++: /lib64/libc.so.6: version `GLIBC_2.11' not found (required by /data/obenchek/gcc-4.9.2/bin/g++)
I have already checked this question :
`GLIBC_2.11' not found
If it says right, the version of libc and g++ are not compatible , libc version :
/lib/libc.so.6
GNU C Library stable release version 2.4 (20090904), by Roland McGrath et al.
...
So I should reinstall gcc completely or there is a easier way to resolve this ??
I copied gcc folder to the new machine.
That's your problem: don't copy, install appropriate GCC package instead.
Most UNIX systems, including Linux, guarantee backward compatibility: a binary compiled on an older system continues to run on a newer one.
The reverse is not true: a binary compiled on a newer system often will not run on an older one. This is working "as designed".
In this particular instance, you copied GCC compiled with GLIBC-2.11 or newer to a machine that has GLIBC-2.10 or older. And that doesn't work.

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.

Compiling GHC 7.2+ on Linux with libc version < 2.7

I will like to install GHC 7.2.2 on a Redhat x86_64 (RHEL v5) server at work (in my user account. I don't have root access). I downloaded the generic binary for Linux x86_64 from GHC download page. But, when I run configure, it throws glibc version exception for ghc-pwd since the glibc version on Linux is 2.6. The required glibc version is 2.7.
I googled around but didn't find any pre-configured GHC binaries for Redhat 5. I will appreciate pointers on how to work around the glibc version issue if any one has figured it out for RHEL 5 (or any Linux flavor if the workaround is general). Alternatively, if there are zipped binaries for Redhat x86_64 available somewhere, I can download and unzip them, if you point me to them. Otherwise it looks like I am stuck with GHC 6.12 at work.
I built ghc 7.4.1 on stock RHEL 5.3, by starting with a binary build of ghc 6.6 or 6.8 (forget which one) which ran fine on the platform. Then I used that to build ghc 6.12 from source, and then used 6.12 to build 7.4.1 from source. Had to use gcc 4.3 to build 7.4.1, but that's fine as gcc 4.3 is available on RHEL 5.3.
It's not much work, just a fair amount of time for all the builds to complete.
I had a similar problem, so I compile ghc myself. Doing so is non-trivial because you need newer binutils and gcc. But t can be done (all in user-land).
GHC needs a GHC binary available to compile itself. There are precompiled GHC binaries available, but they have been built against newer glibc versions
RHEL 5 has glibc 2.5, so you can use GHC 6.8 and bootstrap from there. Generally GHC can be bootstrapped with $VERSION-2 or newer (the precise version is documented with the source downloads).
You will also need a newer version of gcc. #alex-iliev suggests that gcc 4.3 is sufficient, which is available on RHEL 5. Your alternative is to use Gentoo Prefix to install an up-to-date gcc.
Download and install the precompiled 6.8 in a directory:
wget http://www.haskell.org/ghc/dist/6.8.3/ghc-6.8.3-x86_64-unknown-linux.tar.bz2
bunzip2 ghc-6.8.3-x86_64-unknown-linux.tar.bz2
tar -xf ghc-6.8.3-x86_64-unknown-linux.tar
cd ghc-6.8.3
mkdir ~/ghc_bootstrap_6_8
./configure --prefix=/home/wilfred/ghc_bootstrap_6_8/
make install
Compile 6.12:
wget http://www.haskell.org/ghc/dist/6.12.3/ghc-6.12.3-src.tar.bz2
bunzip2 ghc-6.12.3-src.tar.bz2
tar -xf ghc-6.12.3-src.tar
cd ghc-6.12.3
mkdir ~/ghc_bootstrap_6_12
PATH=/home/wilfred/ghc_bootstrap_6_8/bin:$PATH ./configure --prefix=/home/wilfred/ghc_bootstrap_6_12/
make
make install
Compiling 7.2 and 7.6 is the same process as 6.12. Compiling can take several hours so you may want to look at quick builds (although you'll want a normal build for the final GHC version).
If you do go down the Gentoo Prefix root, just bootstrap your way to GHC 7.2. You can then modify $EPREFIX/usr/portage/eclass/ghc-package.eclass to add the line:
PATH=/home/wilfred/ghc_bootstrap_7_2/bin:$PATH
then simply add ghcbootstrap to your USE flags and:
emerge --nodeps ghc
Its ghc 6.8 to compile 6.12 from source

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

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.

Resources