Linux (Command Line)

From Crypto++ Wiki
Jump to navigation Jump to search

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.

Cross compile variables
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