Linux (Command Line)
This article will discuss building Crypto++ from the command line using GNUmakefile
on Linux. The Crypto++ makefile and GNU Make is the preferred method of building the library on Unix and Linux. It is preferred over other build systems for several reasons. The most important is, GNU Make and the makefile is the only build system that actually works as expected on the platforms and compilers supported by the library.
The makefile build attempts to follow the GNU Make Book and the GNU Coding Standards. Following the standards means things "just work" as expected for users. Users don't need to learn a new way to do things using existing toolchains and they don't need to learn a new build system. We deviate from the instructions on occasion, but the transgressions are usually oversights that we will fix.
The makefile sometimes breaks from tradition due to technical limitations on some platforms. For example, Stallman says the makefile should use the install
command during make install
. The problem is, it breaks on AIX and Solaris so we have to manually copy files and set permissions.
The makefile is mostly setup to perform cross-compiles but there are some sharp edges. For example, the makefile uses $CXX -dumpmachine
to detect the build target but Clang misreports the build target in some version of the compiler. That starts a cascade of problems as we target the wrong architecture. The problem is discussed below in Cross Compiles.
There is also a specialized makefile setup for Android and iOS cross-compiles in GNUmakefile-cross
. GNUmakefile-cross
depends upon setenv-xxx.sh
scripts. There are several scripts, including setenv-android.sh
, setenv-ios.sh
and setenv-embedded.sh
.
Some related wiki pages are GNUmakefile and Linux. GNUmakefile attempts to discuss the design of the makefile, and document the various variables and defines used by it. Linux is an early wiki page that holds a lot of information but it is less useful to readers because it covers a lot of material in paragraph bites. BASE+SIMD also discusses files with special needs, and how the makefile hndles the compiler options.
Building the Library
The library does a good job of configuring itself out of the box. Usually you can perform the following and things work fine:
$ wget https://www.cryptopp.com/cryptopp810.zip $ unzip -aoq cryptopp810.zip -d cryptopp $ cd cryptopp $ make
Or using GitHub:
$ git clone git://github.com/weidai11/cryptopp.git $ cd cryptopp $ make
A typical output from above on an x86_64 machine looks as follows.
$ make g++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c cryptlib.cpp g++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c cpu.cpp g++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c integer.cpp g++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c 3way.cpp ...
If you want to perform dead code stripping, then you should issue make lean
instead of make
. See Makefile Targets for more information.
In the past you needed to set CRYPTOPP_DATA_DIR
to ensure self tests would run after installation, but that is no longer needed since Crypto++ 8.0. See Data Directory for more information.
CXX and CXXFLAGS
The library does its best to honor your configuration choices. For example:
$ export CXX=clang++ $ make clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c cryptlib.cpp clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c cpu.cpp clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c integer.cpp clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c 3way.cpp ...
And:
$ CXX=clang++ make clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c cryptlib.cpp clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c cpu.cpp clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c integer.cpp clang++ -DNDEBUG -g2 -O3 -fPIC -pthread -pipe -c 3way.cpp ...
And:
$ export CXXFLAGS="-DNDEBUG -g2 -O3 -fstack-protector-all" $ make g++ -DNDEBUG -g2 -O3 -fstack-protector-all -fPIC -pthread -pipe -c cryptlib.cpp g++ -DNDEBUG -g2 -O3 -fstack-protector-all -fPIC -pthread -pipe -c cpu.cpp g++ -DNDEBUG -g2 -O3 -fstack-protector-all -fPIC -pthread -pipe -c integer.cpp g++ -DNDEBUG -g2 -O3 -fstack-protector-all -fPIC -pthread -pipe -c 3way.cpp ...
Notice the library uses your CXXFLAGS
and then adds other flags to it, like -fPIC
and -pipe
.
If you are building for OS X or iOS under Xcode, then you probably want to ensure -stdlib=libc++
is present because Xcode uses LLVM's runtime (libc++
). Xcode does not use GNU's runtime (libstdc++
) by default:
$ export CXXFLAGS="-DNDEBUG -g2 -O2 -stdlib=libc++" $ make g++ -DNDEBUG -g2 -O2 -stdlib=libc++ -fPIC -pipe -c cryptlib.cpp g++ -DNDEBUG -g2 -O2 -stdlib=libc++ -fPIC -pipe -c cpu.cpp ...
You can instruct make
to use only your CXXFLAGS
by providing them on the command line as an override. Below is a native compile on a LeMaker Banana Pro, and it avoids -pipe
because the device does not have the RAM to process some files in-memory:
$ make CXXFLAGS="-DNDEBUG -g2 -O3 -fPIC -mtune=cortex-a7" g++ -DNDEBUG -g2 -O3 -fPIC -mtune=cortex-a7 -c cryptlib.cpp g++ -DNDEBUG -g2 -O3 -fPIC -mtune=cortex-a7 -c ida.cpp ...
Testing the Library
You should always test the library after you build it. The library can have trouble on a number of compilers, especially when the optimizer starts inlining functions. Optimizations and bad code generation have been the cause of a number of Crypto++ bug reports. See, for example, Crash on Cygwin-x64 with -DDEBUG -Os and Hang on Debian ARM64 QEMU Chroot.
You test the library using cryptest.exe
using both the Validation Suite (v
or vv
option) and the Test Vectors (tv
option). Here's how to invoke it.
# Validation Suite $ ./cryptest.exe v ... All tests passed! Test ended at Sun Jan 3 08:50:06 2016
And:
# Validation Suite, thorough tests $ ./cryptest.exe vv ... All tests passed! Test ended at Sun Jan 3 08:50:06 2016
And:
# Test Vectors $ ./cryptest.exe tv all ... Tests complete. Total tests = 17549. Failed tests = 0.
If you want a more comprehensive or thorough testing of the library under a compiler on a platform, then run the cryptest.sh
script. The cryptest.sh
script repeatedly builds the library and executes the self tests under different configurations. More details can be found at Release Process.
Installing the Library
Use the make install
recipe to install the library. The recipe will install the header and libraries based on PREFIX
. The library will also install test data files when the recipe is executed.
Cross Compiling
Cross-compiles mostly works but there are some sharp edges. When cross-compiling you should set the variables CC
, CXX
, CXXFLAGS
, AR
, RANLIB
, AS
, LD
, and LDFLAGS
. The makefile uses CXX
and CXXFLAGS
in combination with -dumpmachine
to determine the build target. CXXFLAGS
should include --sysroot
.
The table below uses values for Android.mk and NDK r19c. The programs, like the C++ compiler and assembler, should use a full path like /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin
.
Variable | Meaning | Example |
---|---|---|
CC | C compiler | armv7a-linux-androideabi28-clang
|
CXX | C++ compiler | armv7a-linux-androideabi28-clang++
|
CXXFLAGS | C++ compiler flags | Include -target armv7-none-linux-androideabi28
|
AR | Archive program | arm-linux-androideabi-ar
|
RANLIB | Index program | arm-linux-androideabi-ranlib
|
AS | Assembler program | arm-linux-androideabi-as
|
LDFLAGS | Linker flags | Include -Wl,-target=armv7-none-linux-androideabi28
|
The library uses the compiler to drive link, so be sure your LDFLAGS
includes -Wl
so the compiler does not remove options intended for the linker.
Here is one of the sharp edges from Clang and iOS. Notice Clang reports the host architecture of x86_64
, and not the iPhone architecture of armv7-a
:
$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -march=armv7-a -dumpmachine x86_64-apple-darwin13.4.0
The fix is to invoke make with internal make variables correctly set (other variables omitted for clarity):
$ CXX=... CXXFLAGS=... make IS_X64=0 IS_ARM32=1
User Programs
After building and installing the library you need to compile and link against it. Assuming you installed the library in the default location of /usr/local
, then headers are located at /usr/local/include
and libraries are located at /usr/local/lib
. Your compile command should use -I/usr/local/include
and your link command should use -L/usr/local/lib
.
Linking correctly can be tricky due to chronic path problems on Linux. You can use one of two strategies. First, use static library linking and avoid runtime problems. Second, use dynamic library linking and additional linker flags. If dynamic linking, then your link command should also include RUNPATH options to ensure you use the correct library at runtime; see Runtime Path for details.
CXX and CXXFLAGS
You should build the library and your programs using the same C++ compiler (CXX
), the same C++ compiler flags (CXXFLAGS
), including the same C++ runtime library. Do not mix and match options or runtime libraries.
Runtime libraries tend to be more of a problem on Apple, OS X and iOS platforms. On APple platforms it is usually best to use LLVM's libc++
. Note that the GNU C++ runtime library is specified with -std=libstdc++
, and the LLVM C++ runtime library is specified with -std=libc++
.
Test Program
The test program used in the examples is shown below. The program multiplies two 64-bit integers and prints the result. The sample program is taken from the Runtime Path wiki article.
$ 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.
$ g++ -I/usr/local/include myprog.cxx /usr/local/lib/libcryptopp.a -o myprog.exe $ ./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 and hard to diagnose memory problems at runtime.
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 | grep -E '(RPATH|RUNPATH)' RUNPATH /usr/local/lib