How to Link to a Specific Glibc Version

How can I link to a specific glibc version?

You are correct in that glibc uses symbol versioning. If you are curious, the symbol versioning implementation introduced in glibc 2.1 is described here and is an extension of Sun's symbol versioning scheme described here.

One option is to statically link your binary. This is probably the easiest option.

You could also build your binary in a chroot build environment, or using a glibc-new => glibc-old cross-compiler.

According to the http://www.trevorpounds.com blog post Linking to Older Versioned Symbols (glibc), it is possible to to force any symbol to be linked against an older one so long as it is valid by using the same .symver pseudo-op that is used for defining versioned symbols in the first place. The following example is excerpted from the blog post.

The following example makes use of glibc’s realpath, but makes sure it is linked against an older 2.2.5 version.

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

__asm__(".symver realpath,realpath@GLIBC_2.2.5");
int main()
{
const char* unresolved = "/lib64";
char resolved[PATH_MAX+1];

if(!realpath(unresolved, resolved))
{ return 1; }

printf("%s\n", resolved);

return 0;
}

Link with specific GLIBC version

After a lot of experimentation I came across this solution. It's possible to update the libc version to 2.17 on the Beaglebone Black. First download either libc6_2.17-0ubuntu5_armhf.deb (for compiling with hard floats) or libc6-armel_2.17-0ubuntu5.1_armhf.deb (for compiling with soft floats) from https://launchpad.net/ubuntu/raring/armhf/libc6/2.17-0ubuntu5 or https://launchpad.net/ubuntu/raring/armhf/libc6-armel/2.17-0ubuntu5.1 respectively.

Next, use scp to copy the file to the BBB.

scp libc6_2.17-0ubuntu5_armhf.deb root@beaglebone.local:/root

scp libc6-armel_2.17-0ubuntu5.1_armhf.deb root@beaglebone.local:/root

Finally, install the package.

sudo dpkg --install libc6_2.17-0ubuntu5_armhf.deb

sudo dpkg --install libc6-armel_2.17-0ubuntu5.1_armhf.deb

It worked for functions requiring the new version on my device, despite ldd --version still displaying as 2.13. You might also consider using sudo apt-get -f install to get rid of the old version.

Hope it helps!

Linking against an old version of libc to provide greater application coverage

Work out which symbols in your executable are creating the dependency on the undesired version of glibc.

$ 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 within the depended-upon library to see if there are any symbols in older versions that you can link against:

$ 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!

Request the version from GLIBC_2.2.5 in your code:

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

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

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

Observe that GLIBC_2.3 is no longer needed:

$ 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

For further information, see http://web.archive.org/web/20160107032111/http://www.trevorpounds.com/blog/?p=103.

How to link a program with a handmade compiled version of glibc?

In order to verify that I can compile some code with gcc linking to this version of the glibc,

You didn't compile and link code with this version of glibc.

I compiled the [GHOST vulnerability check] supplied by Qualys, thanks to the following command (inspired from those two answers from StackOverflow: [1] and [2]):

You appear to have lost references [1] and [2].

gcc vulnerability_check.c -o vulnerability_check \
-Wl,--rpath=/usr/local/lib \
-Wl,--dynamic-linker=/usr/local/lib/ld-linux-x86-64.so.2

Above command makes GCC use standard locations for include (i.e. /usr/include) and lib (i.e. /usr/lib), but arranges for the alternate version of GLIBC (the one from /usr/local) to be used at runtime.

That is, there are 3 distinct steps:

  1. Compile sources using some headers
  2. Perform (static) link using some .o and .so files and
  3. Perform runtime linking/loading just before passing control to the application.

You've only arranged for step 3 to use your newly-built GLIBC.

To make use of newly-built GLIBC (assuming it was configured to install into /usr/local), you should use something like:

gcc -o t -nostdlib -nostartfiles -I/usr/local/include \
/usr/local/lib/crt1.o \
/usr/local/lib/crti.o \
$(gcc --print-file-name=crtbegin.o) \
t.c \
/usr/local/lib/libc.so.6 \
/usr/local/lib/libc_nonshared.a \
/usr/local/lib/ld-linux-x86-64.so.2 \
$(gcc --print-file-name=crtend.o) \
/usr/local/lib/crtn.o \
-Wl,--rpath=/usr/local/lib \
-Wl,--dynamic-linker=/usr/local/lib/ld-linux-x86-64.so.2`

CFLAGS="-Wl,--rpath=/usr/local/lib -Wl,--dynamic-linker=/usr/local/lib/ld-linux-x86-64.so.2"

Above command is again wrong: it uses mismatched ld-linux.so and libc.so.6 at static link time (at step 2 above), and fails.

TL;DR: linking against non-standard GLIBC is complicated. You'll be much better off using a chroot environment or a virtual machine instead.

Update:

CFLAGS="-I/usr/local/include -Wl,--rpath=/usr/local/lib -Wl,--dynamic-linker=/usr/local/lib/ld-linux-x86-64.so.2"

Above setting makes little sense: the -Wl,... are linker flags, they belong in LDFLAGS.

LDFLAGS="-nostdlib -nostartfiles /usr/local/lib/crt1.o /usr/local/lib/crti.o $(gcc --print-file-name=crtbegin.o) /usr/local/lib/libc.so.6 /usr/local/lib/libc_nonshared.a /usr/local/lib/ld-linux-x86-64.so.2 $(gcc --print-file-name=crtend.o) /usr/local/lib/crtn.o"

... Anything wrong ?

Yes. Note that the order of crti.o, your objects/sources, libc.so.6, and crtn.o matters a great deal, and must be exactly the order I showed.

I don't believe there is any way to achieve such order by specifying it in LDFLAGS. You'll need to either execute the command line by hand, or heavily modify Makefile for the program you are building.

You really are better off doing this in a chroot or a VM.

Multiple glibc libraries on a single host

It is very possible to have multiple versions of glibc on the same system (we do that every day).

However, you need to know that glibc consists of many pieces (200+ shared libraries) which all must match. One of the pieces is ld-linux.so.2, and it must match libc.so.6, or you'll see the errors you are seeing.

The absolute path to ld-linux.so.2 is hard-coded into the executable at link time, and can not be easily changed after the link is done (Update: can be done with patchelf; see this answer below).

To build an executable that will work with the new glibc, do this:

g++ main.o -o myapp ... \
-Wl,--rpath=/path/to/newglibc \
-Wl,--dynamic-linker=/path/to/newglibc/ld-linux.so.2

The -rpath linker option will make the runtime loader search for libraries in /path/to/newglibc (so you wouldn't have to set LD_LIBRARY_PATH before running it), and the -dynamic-linker option will "bake" path to correct ld-linux.so.2 into the application.

If you can't relink the myapp application (e.g. because it is a third-party binary), not all is lost, but it gets trickier. One solution is to set a proper chroot environment for it. Another possibility is to use rtldi and a binary editor. Update: or you can use patchelf.

Link against non-default glibc

Thanks to Nemo's comment, the issue could be resolved by appending the C++ runtimes to the rpath. In my case, that is

-Wl,--rpath=/path/glibc-elision/build/lib:/usr/lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu

The paths can be set by using export LD_LIBRARY_PATH=/your/path as well.

It also turns out that I falsely used ; instead of : to append the paths in the original post.



Related Topics



Leave a reply



Submit