0

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:

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
Alexis
  • 591
  • 2
  • 8
  • 2
    man gnu_get_libc_version() – stark Feb 10 '22 at 19:26
  • gnu_get_libc_version() would be a solution for guessing the system libc version if the binary is not statically linked (this is a requirement). Yet we need to find the version of the libc required by binaries inside the AppImage payload. – Alexis Feb 10 '22 at 20:16
  • In the statically linked case can you explain what is failing other than your incorrect version test? – stark Feb 10 '22 at 20:27
  • In a statically linked binary gnu_get_libc_version() returns the version of the statically linked libc not the one of the system (which is the one required). – Alexis Feb 10 '22 at 20:41
  • 1
    Required by what? The statically linked binary doesn't use the one from the system. – stark Feb 10 '22 at 20:52
  • Exactly, therefore the resulting value would not be accurate. I update the limitations in the original question. – Alexis Feb 11 '22 at 14:54
  • 1
    So use the statically linked call for the included version and the dynamically linked call for the system version. It can even be the same code. – stark Feb 11 '22 at 14:56

2 Answers2

2

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.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Thanks @KamilCuk, this seems promising, yet I would like to find a way of getting that information by reading it directly from the binary file. – Alexis Feb 11 '22 at 01:11
  • ? `grep` is reading directly from the binary file. I do not understand. – KamilCuk Feb 11 '22 at 06:50
  • My bad, I thought that your were actually runing libc.so.6. This could be a solution but we have no warranties (at least not from the glibc team) that the string format will not change. – Alexis Feb 11 '22 at 14:51
2

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.).

Employed Russian
  • 199,314
  • 34
  • 295
  • 362