Runtime Path
A chronic problem with Linux distributions is compiling and linking against one version of a library, and then runtime linking against a different version of a library. Other operating systems managed to fix the problems for users, including AIX, BSDs, OS X, Solaris and even Windows. Linux made a bone headed decision back in the 1990's and Linux users have been suffering the problem ever since. The idiots who decided the default behavior deserve a Darwin award. This wiki article will tell you how to fix the problem.
The discussion below assumes a Linux distro provides Crypto++ in /usr/include
and /usr/lib
; while the user installed a newer version of the library at /usr/local/include
and /usr/local/lib
. This is a common scenario when the user wants the latest version of the library for new classes.
A related wiki page is Linux (Command Line). The page discusses how to build the library and user programs. The Crypto++ test program does not suffer the problem because it uses static linking, but user programs may suffer the problem if dynamic libraries are used like with Debian or Fedora.
RPATH vs RUNPATH
There are two different "runtime path" options available to a program. The old one is rpath
and the new one is runpath
. The difference between rpath
and runpath
is, rpath
is not overridden by LD_LIBRARY_PATH
; while runpath
can be overridden by LD_LIBRARY_PATH
.
Using linker option --enable-new-dtags
provides the new runtime path. It is usually recommended you use runpath
, which means you use --enable-new-dtags
.
Local Install
A new or different version of the library will be installed at /usr/local/include
and /usr/local/lib
by default. You can chage the default location; see GNUmakefile. The steps to perform a local install using the default locations are shown below.
$ cd cryptopp $ make $ make test $ sudo make install
You can verify the installation by running the newly installed cryptest.exe
program.
$ /usr/local/bin/cryptest.exe v ... All tests passed! Seed used was 1555002643 Test started at Thu Apr 11 13:10:43 2019 Test ended at Thu Apr 11 13:10:47 2019
Test Program
The test program used in the examples is shown below. There is not much to it. It generates two random 64-bit integers, and then prints their product.
$ cat myprog.cxx #include <cryptopp/cryptlib.h> #include <cryptopp/osrng.h> #include <cryptopp/integer.h> #include <iostream> #include <iomanip> int main(int argc, char* argv[]) { using namespace CryptoPP; AutoSeededRandomPool prng; Integer x(prng, 64), y(prng, 64); std::cout << "x = " << std::hex << x << std::endl; std::cout << "y = " << std::hex << y << std::endl; std::cout << "x*y = " << std::hex << x*y << std::endl; return 0; }
Static Linking
The easiest way to sidestep runtime linking problems is to avoid runtime linking. The commands below shows you how to static link.
$ g++ -I/usr/local/include myprog.cxx /usr/local/lib/libcryptopp.a -o myprog.exe
Or you can compile and link in two separate commands.
$ g++ -I/usr/local/include myprog.cxx -c $ g++ myprog.o /usr/local/lib/libcryptopp.a -o myprog.exe
Running the program results output similar to the following.
$ ./myprog.exe x = ac469320f16f659ah y = 76661cb77ff066fh x*y = 4fad3f43030d1c99248c2401614a9c6h
And more importantly, no runtime dependencies on the Crypto++ library.
$ ldd myprog.exe linux-vdso.so.1 (0x00007ffeeb951000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fe9b69f8000) libm.so.6 => /lib64/libm.so.6 (0x00007fe9b6874000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fe9b6859000) libc.so.6 => /lib64/libc.so.6 (0x00007fe9b6693000) /lib64/ld-linux-x86-64.so.2 (0x00007fe9b6ba9000)
You can also use -L
and -l:libcryptopp.a
to achieve the same effect. Also see --library=namespec
in the ld(1)
man page.
$ g++ -I/usr/local/include myprog.cxx -o myprog.exe -L/usr/local/lib -l:libcryptopp.a $ ldd myprog.exe linux-vdso.so.1 (0x00007f817cc2f000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f817ca7b000) libm.so.6 => /lib64/libm.so.6 (0x00007f817c8f7000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f817c8dc000) libc.so.6 => /lib64/libc.so.6 (0x00007f817c716000) /lib64/ld-linux-x86-64.so.2 (0x00007f817cc30000)
Dynamic Linking
Sometimes you must use dynamic linking, like when two components both depend on Crypto++. That is, your program and another library depend on Crypto++. In this case you should use the shared object to avoid subtle memory problems.
To avoid compiling and linking against one version of a library, and then runtime linking against a different version of a library, you should use a Runtime Path or RUNPATH on Linux. The proper way to use a runtime path is use both -Wl,-R,<path>
and -Wl,--enable-new-dtags
. The -Wl
is used to pass options through the compiler to the linker. The -R
is used from -rpath
. -R
works on Linux and Solaris, while -rpath
does not work on Solaris. You must also use -L
during compile.
$ g++ -I/usr/local/include myprog.cxx -o myprog.exe -L/usr/local/lib \ -Wl,-R,/usr/local/lib -lcryptopp -Wl,--enable-new-dtags
Running the program results output similar to the following.
$ ./myprog.exe x = beafc80177632095h y = 6ace56e66bf3641ah x*y = 4f8e7afe3c2410b1f82cf85d0d3a8322h
Now there is a runtime dependency on the Crypto++ library.
$ ldd myprog.exe linux-vdso.so.1 (0x00007ffea199d000) libcryptopp.so.8 => /usr/local/lib/libcryptopp.so.8 (0x00007f431cb39000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f431c98a000) libm.so.6 => /lib64/libm.so.6 (0x00007f431c806000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f431c7eb000) libc.so.6 => /lib64/libc.so.6 (0x00007f431c625000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f431c603000) /lib64/ld-linux-x86-64.so.2 (0x00007f431d04b000)
And there is a DT_RUNPATH
section present in the ELF headers.
$ objdump -x myprog.exe | egrep '(RPATH|RUNPATH)' RUNPATH /usr/local/lib
LD_LIBRARY_PATH
If you use dynamic linking and omit -Wl,-R,<path>
then you have to hack things using LD_LIBRARY_PATH
. You may also need LD_LIBRARY_PATH
to run self-tests in-place before an install. LD_LIBRARY_PATH
is probably the worst contraption to work around the chronic Linux path problems but it is all you have.
An example of using LD_LIBRARY_PATH
is shown below. First compile with a RUNPATH.
$ g++ -I/usr/local/include test.cxx -o test.exe -L/usr/local/lib -lcryptopp
However, when we check the runtime libraries we see the wrong library will be used at runtime. Notice /usr/lib/libcryptopp.so.8
is used instead of /usr/local/lib/libcryptopp.so.8
. Derp...
$ ldd myprog.exe linux-vdso.so.1 (0x00007ffe6299c000) libcryptopp.so.8 => /usr/lib/libcryptopp.so.8 (0x00007ff85924d000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007ff85909e000) libm.so.6 => /lib64/libm.so.6 (0x00007ff858f1a000) libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ff858eff000) libc.so.6 => /lib64/libc.so.6 (0x00007ff858d39000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007ff858d17000) /lib64/ld-linux-x86-64.so.2 (0x00007ff85975f000)
To fix the path problem you need to provide a wrapper shell script that sets LD_LIBRARY_PATH
.
$ cat myprog.sh #!/usr/bin/env bash export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH" shift ./myprog.exe "$@"
Running the program results output similar to the following.
$ ./myprog.sh x = 221f84259480140ah y = 721486335b095d59h x*y = f34c53266a615abf5db91a16a28997ah