Coder Perfect

To provide more application coverage, we’re linking against an older version of libc.

Problem

The core system library is generally dynamically linked to Linux binaries (libc). This reduces the binary’s memory footprint, but binaries that rely on the most recent libraries will not operate on older platforms. Binaries linked to earlier libraries, on the other hand, will happily run on the most recent systems.

As a result, in order to ensure that our program has adequate distribution coverage, we must determine the oldest libc we can support and link our binary against it.

How can we figure out the version of libc we can link to?

Asked by Gearoid Murphy

Solution #1

Determine which symbols in your executable are causing the dependency on the glibc version you don’t want.

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691972 0x00 05 GLIBC_2.3
    0x09691a75 0x00 03 GLIBC_2.2.5

$ objdump -T myprog | fgrep GLIBC_2.3
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   realpath

Look through the depended-upon library to check if any symbols from previous versions can be linked to:

$ objdump -T /lib/libc.so.6 | grep -w realpath
0000000000105d90 g    DF .text  0000000000000021 (GLIBC_2.2.5) realpath
000000000003e7b0 g    DF .text  00000000000004bf  GLIBC_2.3   realpath

We’re in luck!

In your code, request the version from GLIBC 2.2.5:

#include <limits.h>
#include <stdlib.h>

__asm__(".symver realpath,realpath@GLIBC_2.2.5");

int main () {
    realpath ("foo", "bar");
}

It’s worth noting that GLIBC 2.3 is no longer required:

$ objdump -p myprog
...
Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

$ objdump -T myprog | grep realpath
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 realpath

See http://web.archive.org/web/20160107032111/ for more information. http://www.trevorpounds.com/blog/?p=103.

Answered by Sam Morris

Solution #2

Unfortunately, in my case, @Sam’s method does not work. But, in his opinion, I came up with my own solution.

This is my predicament:

I’m using the Thrift framework (an RPC middleware) to write a C++ program. Because I prefer static links to dynamic links, my program is statically linked to libthrift.a rather than libthrift.so. However, because libthrift.a is dynamically linked to glibc, and because my libthrift.a was built with glibc 2.15 on my machine, it utilizes memcpy version 2.14(memcpy@GLIBC 2.14) given by glibc 2.15.

However, our server machines only have glibc version 2.5, which only includes memcpy@GLIBC 2.2.5. It is significantly less than memcpy@GLIBC 2.14. So, of course, my server program can’t run on those machines.

And then I came across this solution:

The code for stages 1 and 2 may be found here: https://gist.github.com/nicky-zs/7541169

Answered by nicky_zs

Solution #3

You may use the following script to build a list of all the symbols that are newer in your GLIBC than in a particular version in a more automatic manner (set on line 2). It generates a glibc.h file (the name of which is determined by the script parameter) that contains all of the required.symver declarations. Then, in your CFLAGS, add -include glibc.h to ensure that it is included everywhere in your compilation.

If you don’t utilize any static libraries that were not compiled with the aforementioned include, this will suffice. You can use objcopy to build a copy of the library with the symbols renamed to the old versions if you don’t want to recompile. The script’s second to last line builds a version of libstdc++.a for your system that will link against the old glibc symbols. If you add -L. (or -Lpath/to/libstdc++.a/) to your program, it will statically link libstdc++ instead of linking in a load of new symbols. Remove the last two lines, as well as the printf… redeff line, if you don’t require it.

#!/bin/bash
maxver=2.9
headerf=${1:-glibc.h}
set -e
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 libresolv.so.2 librt.so.1; do
objdump -T /usr/lib/$lib
done | awk -v maxver=${maxver} -vheaderf=${headerf} -vredeff=${headerf}.redef -f <(cat <<'EOF'
BEGIN {
split(maxver, ver, /\./)
limit_ver = ver[1] * 10000 + ver[2]*100 + ver[3]
}
/GLIBC_/ {
gsub(/\(|\)/, "",$(NF-1))
split($(NF-1), ver, /GLIBC_|\./)
vers = ver[2] * 10000 + ver[3]*100 + ver[4]
if (vers > 0) {
    if (symvertext[$(NF)] != $(NF-1))
        count[$(NF)]++
    if (vers <= limit_ver && vers > symvers[$(NF)]) {
        symvers[$(NF)] = vers
        symvertext[$(NF)] = $(NF-1)
    }
}
}
END {
for (s in symvers) {
    if (count[s] > 1) {
        printf("__asm__(\".symver %s,%s@%s\");\n", s, s, symvertext[s]) > headerf
        printf("%s %s@%s\n", s, s, symvertext[s]) > redeff
    }
}
}
EOF
)
sort ${headerf} -o ${headerf}
objcopy --redefine-syms=${headerf}.redef /usr/lib/libstdc++.a libstdc++.a
rm ${headerf}.redef

Answered by patstew

Solution #4

Glibc 2.2 is the most often used minimal version. Finding a build platform for that version, on the other hand, may be difficult.

It’s probably a better idea to start with the oldest OS you wish to support and expand from there.

Answered by Douglas Leeder

Post is based on https://stackoverflow.com/questions/4032373/linking-against-an-old-version-of-libc-to-provide-greater-application-coverage