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:
- Compile sources using some headers
- Perform (static) link using some
.o
and.so
files and - 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
How to Find Which Position a Word Is in a String
Maximum Number of Threads Per Process in Linux
Get Exit Code of a Background Process
How to Install Gcc 4.9.2 on Rhel 7.4
How to Run Script as Another User Without Password
Calling Printf in X86_64 Using Gnu Assembler
Setting Environment Variables in Linux Using Bash
Run an Untrusted C Program in a Sandbox in Linux That Prevents It from Opening Files, Forking, etc.
How to Open a Socket and Pass It to Another Process in Linux
Assembly Segmentation Fault After Making a System Call, At the End of My Code
Remove a Specific Character Using Awk or Sed
How to Grep For Contents After Pattern
How to Pass the Password to Su/Sudo/Ssh Without Overriding the Tty
How to Recursively Find All Files in Current and Subfolders Based on Wildcard Matching
How to Get the Total Cpu Usage of an Application from /Proc/Pid/Stat
Linux Default Behavior of Executable .Data Section Changed Between 5.4 and 5.9