GNUmakefile is the primary way to configure and build the library and test program from sources on AIX, BSD, Linux, OS X, Solaris, Unix, Cygwin and MinGW. It is used to natively build targets like libcryptopp.a, libcryptopp.so, libcryptopp.dylib and cryptest.exe. Natively means the artifact, like libcryptopp.a or cryptest.exe, is targeted for the host computer or the computer you are building on, like your i686 or x86_64 desktop.
The GNUmakefile is not used to build cross-platform or used with cross-compiles. A different makefile called GNUmakefile-cross is used for targeting an architecture like ARM or MIPS. A different makefile is used because GNUmakefile assumes the target architecture is the same as the host architecture, and that does not hold in a cross-compile.
GNUmakefile-cross does not have a separate page. Once you understand the GNUmakefile, then you will understand GNUmakefile-cross because its a simpler makefile with fewer pieces. Some sections of this page have information dedicated to the difference when cross-compiling. You can read more detailed information about cross-compiling at Android (Command Line), iOS (Command Line), ARM (Command Line) , ARM Embedded (Command Line) and ARM Embedded (Bare Metal).
Using either GNUmakefile or GNUmakefile-cross requires GNU Make 3.81 or higher. Newer platforms, like Debian 7, Fedora 15, OS X 10 and Ubuntu 9 will have it. You will have to install GNU Make on older ones, like Fedora 1. Posix's make is too anemic, so BSD and Solaris users should install the gmake package.
When building the library, everything is driven through the compiler driver, including link. Compiler flags can be specified directly by CXXFLAGS = -foo and the option can be passed to the linker with CXXFLAGS = -Wl,-foo. LDFLAGS is not used in every recipe, so you should try to use CXXFLAGS if possible.
A closely related page is config.h, which explains configuration options and preprocessor macros. If a setting is not available in the GNUmakefile, then its likely present in config.h. Also of interest is configure.sh, which performs compile tests rewrites the results of ASM and C++11 feature tests. configure.sh was added for Crypto++ 8.3 to help with mobile devices, but it applies to desktop builds as well.
When building the Crypto++ library, the three workhorse targets are static, dynamic and test. static builds the static version of the library, libcryptopp.a. dynamic builds the shared object version of the library, which is libcryptopp.so on Linux and libcryptopp.dylib on OS X. test builds the Crypto++ driver program cryptest.exe.
The standard all target, used if you just type make, enlists static and test.
Autotools and Cmake
Crypto++ primary build system is Make. The community supplies additional systems, like Autotools and CMake. The Crypto++ project works with the community to help with maintenance, but we can't take on official support because we don't have the spare cycles.
If you are porting to another build system, like Autotools or CMake, then you should ensure all, static, dynamic and test work as expected. That's because nearly all the official Crypto++ docs reference them, and its a disservice to ask users to learn something different or do something different.
More targets are available, and they are detailed below in Makefile Targets.
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/cryptopp700.zip unzip -aoq cryptopp700.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 below.
If you want to ensure the self tests run after installation, then you need to set CRYPTOPP_DATA_DIR. See Data_Directory below.
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 ...
$ 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 ...
$ 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 ...
If you are cross-compiling, then you ran a setenv-*.sh script for a cross-compile (for example, setenv-android.sh or setenv-ios.sh). Your next step is to use -f GNUmakefile-cross option:
$ source ./setenv-ios.sh Configuring for Device (ARMv7) XCODE_SDK: iPhoneOS8.2.sdk ... $ make -f GNUmakefile-cross clang++ -DNDEBUG -g2 -Os -fPIC -pipe -Wall -arch armv7 -isysroot /Applications/Xcode.app/ Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.2.sdk -stdlib=libc++ -c cryptlib.cpp clang++ -DNDEBUG -g2 -Os -fPIC -pipe -Wall -arch armv7 -isysroot /Applications/Xcode.app/ Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.2.sdk -stdlib=libc++ -c cpu.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 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
# Test Vectors $ ./cryptest.exe tv all ... Tests complete. Total tests = 4111. 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.
If you are cross-compiling, then you have to manually test the build on a device. Under Android, you have to use adb to push the cryptest.exe program, shared objects, Test Data and Test Vectors to the device. With iOS, you have to use SSH to copy cryptest.exe program, Test Data and Test Vectors to the device.
Once on the device, you can run things like a traditional desktop. Since this is a cross-compile, you cannot use cryptest.sh to drive things because the script is designed to run natively.
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.
Care must be taken to ensure the self tests run after installation if its important to you. Care must be taken because Crypto++ does not use a configuration file per se (like, say openssl.cnf), and the paths need to be wired in during compile. The data directory is wired into the sources via the C/C++ macro CRYPTOPP_DATA_DIR, and more information is available in Data Directory below. If you don't care about it, then you don't need to worry about it.
The table below provides the components and their installation locations. Its based on an install or root path of PREFIX=/usr/local.
|cryptest.exe||$PREFIX/bin||/usr/local/bin||cryptest.exe is only installed if it was built.|
|libcryptopp.a||$PREFIX/lib||/usr/local/lib||libcryptopp.a is only installed if it was built. Its always built when cryptest.exe is a target.|
|libcryptopp.so||$PREFIX/lib||/usr/local/lib||libcryptopp.so is used on Unix and Linux compatibles. It is only installed if it was built. If SONAMES are in effect, then libcryptopp.so is a soft link and the actual library will be libcryptopp.so.5.6.3 (or similar).|
|libcryptopp.dylib||$PREFIX/lib||/usr/local/lib||libcryptopp.dylib is used on OS X. It is only installed if it was built.|
|Headers||$PREFIX/include/cryptopp||/usr/local/include/cryptopp||Header files are always installed.|
|Test Data||$PREFIX/share/cryptopp/TestData||/usr/local/share/cryptopp/TestData||Test Data are only installed if cryptest.exe was built.|
|Test Vectors||$PREFIX/share/cryptopp/TestVectors||/usr/local/share/cryptopp/TestVectors||Test Vectors are only installed if cryptest.exe was built.|
When the library was tested in Testing the Library, the test data and test vectors were in the build directory. It worked fine for building and testing in place. However, you must perform additional steps if you want cryptest.exe to run after installations because cryptest.exe will be in a /usr/bin/ directory, while the test data and test vectors will be in a /usr/share/ directory.
Building and installing the library is a three-step process when using CRYPTOPP_DATA_DIR. First, you need to determine the root directory for the install. By default the root directory is /usr/local/. Second, you need to pass the data directory via CXXFLAGS (or export CRYPTOPP_DATA_DIR or change it in config.h). Third, you need to pass the install directory during the make install.
In the example that follows, the install will occur in /tmp/local/ to test things. First, start by cleaning the previous build.
# Clean previous stuff $ make clean ...
Second, set your flags to suit your taste. There are three note well's in this step:
- you must specify the actual data directory, and not the install directory
- you must include the trailing slash because simple string concatenation is used
- the string must have quotes, so the quote is escaped (\"), and a single quote is added to guard the escaped quote
# Data directory, not install directory $ export CXXFLAGS="-DNDEBUG -g2 -O2 -DCRYPTOPP_DATA_DIR='\"/tmp/local/share/cryptopp/\"'" $ make g++ -DNDEBUG -g2 -O2 -DCRYPTOPP_DATA_DIR='"/tmp/local/share/cryptopp/"' -fPIC -march=native -pipe -c cryptlib.cpp g++ -DNDEBUG -g2 -O2 -DCRYPTOPP_DATA_DIR='"/tmp/local/share/cryptopp/"' -fPIC -march=native -pipe -c cpu.cpp ...
Third, perform the install using PREFIX. This is the same prefix used with GNU make variables, but its capitalized. It also lacks the share/ that CRYPTOPP_DATA_DIR included.
# Install directory, not the data directory $ make install PREFIX=/tmp/local/ mkdir -p /tmp/local/include/cryptopp cp *.h /tmp/local/include/cryptopp chmod 755 /tmp/local/include/cryptopp chmod 644 /tmp/local/include/cryptopp/*.h mkdir -p /tmp/local/lib cp libcryptopp.a /tmp/local/lib chmod 644 /tmp/local/lib/libcryptopp.a mkdir -p /tmp/local/bin cp cryptest.exe /tmp/local/bin chmod 755 /tmp/local/bin/cryptest.exe mkdir -p /tmp/local/share/cryptopp cp -r TestData /tmp/local/share/cryptopp cp -r TestVectors /tmp/local/share/cryptopp chmod 755 /tmp/local/share/cryptopp chmod 755 /tmp/local/share/cryptopp/TestData chmod 755 /tmp/local/share/cryptopp/TestVectors chmod 644 /tmp/local/share/cryptopp/TestData/*.dat chmod 644 /tmp/local/share/cryptopp/TestVectors/*.txt
After installing the library, everything works as expected:
$ cd /tmp/local/bin/ $ ./cryptest.exe v Using seed: 1451851092 Testing Settings... ... All tests passed! Test ended at Sun Jan 3 08:50:06 2016
If desired, you can also export the CRYPTOPP_DATA_DIR (and not fiddle with CXXFLAGS). In this case, the macro will only be used in source files that are sensitive to it. The source files that use the macro are:
- test.cpp, datatest.cpp
- bench1.cpp, bench2.cpp
- validat1.cpp, validat2.cpp, validat3.cpp
$ export CRYPTOPP_DATA_DIR=\"/tmp/local/share/cryptopp/\" $ make g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cryptlib.cpp g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -c cpu.cpp ... g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -DCRYPTOPP_DATA_DIR=\""/tmp/local/share/cryptopp/"\" -c bench.cpp g++ -DNDEBUG -g2 -O2 -fPIC -march=native -pipe -DCRYPTOPP_DATA_DIR=\""/tmp/local/share/cryptopp/"\" -c bench2.cpp ... $ make install PREFIX=/tmp/local/ ...
If you are cross-compiling, then you have to manually install the artifacts on a device. Under Android, you have to use adb to push the cryptest.exe program, shared objects, Test Data and Test Vectors to the device. With iOS, you have to use SSH to copy cryptest.exe program, Test Data and Test Vectors to the device.
The last step in using the library is creating programs and apps. The recommendations for building the Crypto++ 5.6.4 and below library and applications are:
- copy config.recommend to config.h
- build the library and program with the same compiler, options and defines
- define NDEBUG for production apps or programs
At Crypto++ 5.6.5, config.recommend was made the default configuration. Nothing special is required to put the library in a recommended configuration. The recommendations for building the Crypto++ 5.6.5 and above library and applications are:
- build the library and program with the same compiler, options and defines
Compilers and C++ Runtimes
Crypto++ builds under a number of different compilers, including GNU's GCC, LLVM's Clang and Intel's ICC. If a distribution provides Crypto++ built with GCC, then you should use GCC for your program. Conversely, if a distribution provides Crypto++ built with Clang, then you should use Clang for your program. You will encounter both on Linux with Apt, Yum, Emerge, etc; and on OS X with MacPorts, HomeBrew, etc.
If you mix and match compilers like GCC and Clang, then you will probably encounter undefined symbols because different compilers use different C++ runtimes by default. Examples of unexplained missing symbols include the following. It happens all the time because of LLVM's inlined namespace, so you should be aware of it (and its not limited to Crypto++):
- Apple Mach-O linker and multiple "undefined symbols" error using OpenCV
- Undefined symbols in Crypto++/iOS 64-bit project
- Linking error against Crypto++ on OS X Yosemite
- "duplicate section" error while creating DLL
GCC 5.1 and the C++11 ABI change created some additional problems for Clang. In this situation, the library is compiled by a distro with GCC, and the user attempts to compile and link a program with Clang:
You can sometimes overcome the missing symbols using -stdlib=libstdc++ for GNU's C++ runtime, and -stdlib=libc++ for LLVM's C++ runtime. You add the -stdlib=... option to CXXFLAGS. But in the end, its probably best to build the library and program with the same compiler, options and defines. Things will usually "just work" for you when using the the same compiler, options and defines.
Compiling and Linking
A final problem in building your application is mixing and matching version of the library. Here, the mixing and matching occurs when the distribution provides the library and you build the library from sources.
Good: the following will compile and link against the distribution's version of the Crypto++ library:
$ g++ -DNDEBUG -g2 -O2 test.cxx -o test.exe -lcryptopp
Good: the following will compile and link against your copy of the library. Your copy of the library is located in ./cryptopp.
$ ls cryptopp test.cxx $ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe ./cryptopp/libcryptopp.a
Bad: the following will create problems because it uses the distro's copy of library headers, but links against your copy of the library:
$ cd cryptopp $ g++ -DNDEBUG -g2 -O2 test.cxx -o test.exe ./libcryptopp.a
Bad: the following will create problems because you compile and link against your copy of the library, but at runtime it links to the distro's copy of the library:
$ ls cryptopp test.cxx $ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe -L ./cryptopp -lcryptopp
Good: to avoid the problems with runtime linking, use the static archive. This is what the cryptest.exe program does to avoid the problem:
$ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe ./cryptopp/libcryptopp.a
Good: or, use LD_LIBRARY_PATH:
$ g++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe -L ./cryptopp -lcryptopp $ LD_LIBRARY_PATH="./cryptopp:$LD_LIBRARY_PATH" ./test.exe
Good: on OS X, which probably uses Clang by default, you would use clang++ and DYLD_LIBRARY_PATH:
$ clang++ -DNDEBUG -g2 -O2 -I . test.cxx -o test.exe -L ./cryptopp -lcryptopp $ DYLD_LIBRARY_PATH="./cryptopp:$DYLD_LIBRARY_PATH" ./test.exe
Good: if you installed the library in /usr/local, then the following will work with static linking. Again, this is what the cryptest.exe program does to avoid the problem:
$ g++ -DNDEBUG -g2 -O2 -I /usr/local/include test.cxx -o test.exe /usr/local/lib/libcryptopp.a
Good: if you installed the library in /usr/local, then the following will work with dynamic linking:
$ g++ -DNDEBUG -g2 -O2 -I /usr/local/include test.cxx -o test.exe -L /usr/local/lib -lcryptopp $ LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH" ./test.exe
Good: and the same holds on OS X with dynamic linking:
$ clang++ -DNDEBUG -g2 -O2 -I /usr/local/include test.cxx -o test.exe -L /usr/local/lib -lcryptopp $ DYLD_LIBRARY_PATH="/usr/local/lib:$DYLD_LIBRARY_PATH" ./test.exe
The benchmark program uses the b subcommand. Below is a benchmark with a time of 2 seconds for each test and 3.0 GHz as the frequency.
$ make $ ./cryptest.exe b 2 3.0
Also see Benchmarks on the Crypto++ wiki.
The method above is naïve because it allows unaligned data access (sometimes slower), it uses the default optimization level (-O2) and the default CPU speed of 0.5 GHz (which is probably not correct!). A better way to benchmark is:
# Turn up optimizations export CXXFLAGS="-DNDEBUG -g2 -O3" # Build it make -j 4 # Execute benchmarks with CPU speed ./cryptest.exe b 2 3.0
The makefile has section headers to delineate among the sections as depicted below.
============================== Variables ============================== X86/X32/X64 options ============================== Other Platform options ============================== Common options ============================== Sources and objects ============================== Targets/Recipes ==============================
For example, the Variables section is demarcated with:
########################################################### ##### General Variables ##### ###########################################################
The makefile uses a number of variables, including CXX, CXXFLAGS, IS_X86, IS_X86_64, IS_LINUX, IS_DARWIN, GCC_COMPILER and CLANG_COMPILER, PREFIX and DESTDIR. A comprehensive list is not available, but they are easy to find by scanning the GNUmakefile.
Variables can show up at any place in the makefile, and they often do. For example, LIB_MAJOR, LIB_MINOR and LIB_PATCH are in the middle of the makefile and used to build the string 5.6.3, 5.7.0, etc. The string is built by parsing config.h and CRYPTOPP_VERSION on the fly.
The most important variables are probably CXX and CXXFLAGS because they drive the compile and link process. The first line of makefile begins with respecting a user's choice for CXXFLAGS. You can specify anything you want CXXFLAGS, and the library will attempt to honor it.
# Base CXXFLAGS used if the user did not specify them CXXFLAGS ?= -DNDEBUG -g2 -O2
Around line 15, the makefile sets up common commands in variables. Again, the makefile attempts to respect your choice:
AR ?= ar ARFLAGS ?= -cr RANLIB ?= ranlib CP ?= cp CHMOD ?= chmod MKDIR ?= mkdir EGREP ?= egrep ...
After the basic commands are determined, the makefile proceeds to detect the host's platform around line 25. This is the primary reason there is a separate GNUmakefile-cross:
UNAME := $(shell uname) IS_X86 := $(shell uname -m | $(EGREP) -i -c "i.86|x86|i86|amd64") IS_X86_64 := $(shell uname -m | $(EGREP) -i -c "(_64|d64)") IS_AARCH64 := $(shell uname -m | $(EGREP) -i -c "aarch64") IS_SUN := $(shell uname | $(EGREP) -i -c "SunOS") IS_LINUX := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Linux") IS_MINGW := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "MinGW") IS_CYGWIN := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Cygwin") IS_DARWIN := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Darwin")
Sometimes, the makefile does not honor your choice (but its not out of disrespect). For example, if Apple's OS X is detected, then AR is changed to libtool and ARFLAGS is changed to static -o. If want to override the defaults, you will have to tune it by hand:
IS_DARWIN := $(shell $(CXX) -dumpmachine 2>&1 | $(EGREP) -i -c "Darwin") ... ifneq ($(IS_DARWIN),0) CXX ?= c++ AR = libtool ARFLAGS = -static -o endif
There are some makefile variables that allow finer grain control over installation directories. Recall from Installing the Library you issue make install PREFIX=/usr/local. The makefile derives other locations from PREFIX, and you are allowed to override each one of them.
ifeq ($(PREFIX),) PREFIX = /usr/local endif # http://www.gnu.org/prep/standards/html_node/Directory-Variables.html ifeq ($(DATADIR),) DATADIR := $(PREFIX)/share endif ifeq ($(LIBDIR),) LIBDIR := $(PREFIX)/lib endif ifeq ($(BINDIR),) BINDIR := $(PREFIX)/bin endif ifeq ($(INCLUDEDIR),) INCLUDEDIR := $(PREFIX)/include endif
The above means that you can install the libraries into, say, lib64 by explicitly setting it to a value:
make install PREFIX=/usr/local LIBDIR=/usr/local/lib64
Again, these are GNU make variables, but they are capitalized. The reason for capital letters is historically, Crypto++ used them, so the tradition was unchanged. When the library added DESTDIR, DATADIR, LIBDIR and friends, it favored consistency so the rule to remember is: always use capitol letters.
The X86/X32/X64 options are a big block that simply tweaks variables used in recipes based on command goals. For example, if you issue make ubsan to build and test under an Clang or GCC's Undefined Behavior Sanitizer, then around line 200, this is activated:
# Undefined Behavior Sanitizer (UBsan) testing. There's no sense in # allowing unaligned data access. There will too many findings. ifeq ($(findstring ubsan,$(MAKECMDGOALS)),ubsan) ifeq ($(findstring -fsanitize=undefined,$(CXXFLAGS)),) CXXFLAGS += -fsanitize=undefined endif # CXXFLAGS endif # UBsan
The above block ensures the proper flags are set. It still honors your CXXFLAGS, but it supplements them as required so you get what you ask for. If it was not done, then the compile or test could fails, and that could lead to frustration and waste more time than it deserved.
There are lots of supplements like above, and they include:
- Adding -fPIC for x86_64 targets
- Adding -march=native so AES-NI, RDRAND and RDSEED are available
- Disabling ASM with -DCRYPTOPP_DISABLE_ASM due to down-level assemblers
- Disabling SSE3 with -DCRYPTOPP_DISABLE_SSSE3 due to down-level assemblers
- Disabling AES-NI with -DCRYPTOPP_DISABLE_AESNI due to down-level assemblers
Other Platform Options
Other platform options is a small section, and it includes building PowerPC, ARM and MIPS natively, like you usually build i686 or x86_64. What differentiates this case from GNUmakefile-cross uses is you are targeting the host and the host is ARM or MIPS. You will encounter this when, say, SSH'ing into a BeagleBone Black and running the compiler from the BeagleBone host.
In this case, the platform specific option is simply to add -fPIC to avoid a link error due to missing relocation R_ARM_THM_MOVW_ABS_NC against `a local symbol`:
# Add PIC ifeq ($(findstring -fPIC,$(CXXFLAGS)),) CXXFLAGS += -fPIC endif
There are some tweaks and supplements that are common to all platform. They follow the specific platform options and include:
- Adding -pthread and -lgomp as required
- Adding -fsanitize=undefined for ubsan target
- Adding -fsanitize=address for asan target
- Adding -Wl,-fuse-ld=gold if LD is ld.gold
- Adding -D_GLIBCXX_DEBUG
and -D_GLIBCXX_CONCEPT_CHECKSfor libstdc++ debug builds
Note well: common options include -pipe. The -pipe option uses memory and avoids temporary files during compilation. However, resource constrained devices and boards sometimes lack enough memory to keep things in-memory. If you get an unexplained GCC compiler panic, like described at Linaro Bug 1997: GCC crash using -mtune=cortex-a53 with message "Warning: end of file in string; '"' inserted", then you should remove the -pipe option.
Sources and Objects
The sources and objects list simply builds the list of source and object files used byte the targets and recipes. The order of the source files and objects in the list is important to help manage C++ static initialization problems, and that's why cryptlib.cpp is at the head of the list:
# List cryptlib.cpp first and cpu.cpp second in an attempt to tame C++ static initialization problems. # The issue spills into POD data types of cpu.cpp due to the storage class of the bools, so cpu.cpp # is the second candidate for explicit initialization order. SRCS := cryptlib.cpp cpu.cpp $(filter-out cryptlib.cpp cpu.cpp pch.cpp simple.cpp winpipes.cpp cryptlib_bds.cpp,$(wildcard *.cpp)) # No need for CPU or RDRAND on non-X86 systems. X32 is represented with X64. ifeq ($(IS_X86)$(IS_X86_64),00) SRCS := $(filter-out cpu.cpp rdrand.cpp, $(SRCS)) endif ifneq ($(IS_MINGW),0) SRCS += winpipes.cpp endif ...
Targets and their recipes are what most people think of when discussing a makefile. Generally speaking, Crypto++ tries to follow GNU's standard targets for users.
Sometimes a recipe can be called by a few names. For example, GNU calls out the target check, while the makefile responds to both check and test. Below is a list of the targets with comments and notes.
|all||default target, builds libcryptopp.a and cryptest.exe.|
|static, libcryptopp.a||builds the static archive libcryptopp.a.|
|shared, dynamic, libcryptopp.so||builds the shared object libcryptopp.so on BSD, Unix and Linux.|
|shared, dynamic, libcryptopp.dylib||builds the dynamic library libcryptopp.dylib on OS X.|
|check, test||builds libcryptopp.a and cryptest.exe, and then executes the validation suite with cryptest.exe v.|
|clean||cleans the Crypto++ directory of most intermediate objects and output artifacts; for example, *.o, libcryptopp.a, libcryptopp.so and libcryptopp.dylib.|
|distclean||cleans the Crypto++ directory of nearly all things that were created and not unzipped; for example, it removes intermediate files, output artifacts, the documentation directory (ref563) and dependencies (GNUmakefile.deps).|
|install||installs the output artifacts into their customary location using PREFIX; for example, cryptest.exe is placed in $PREFIX/bin and libcryptopp.a is placed in $PREFIX/lib. Header files are placed in $PREFIX/include/cryptopp. This recipe also sets permissions on directories and artifacts to either 0755 (read and execute) or 0644 (read-only).|
|remove, uninstall||removes output artifacts from their installed location using PREFIX.|
|zip, dist||builds the distribution ZIP file (not a TAR file) using Filelist.txt. Prior to zip'ing, the convert recipe is run.|
|iso||builds the ZIP distribution, and then packages it in a ISO file. This is used to transfer sources to old VMs, like Fedora 1. The old VMs don't have network card drivers, but they do have CDROMs. This is primarily used for Testing and QA.|
|deps||creates the makefile dependencies in GNUmakefile.deps. If you don't issue make deps, then they are not available.|
|bench, benchmark, benchmarks||executes the test program with cryptest.exe b 3 2.4 using max duration of 3 seconds and CPU speed of 2.4 GHz (2.4+1e9)|
|lean||builds libcryptopp.a, libcryptopp.so (libcryptopp.dylib on OS X) and cryptest.exe with -ffunction-sections and -fdata-sections to allow dead code stripping. Applications will need to link with -Wl,--gc-sections (-Wl,-dead_strip on OS X) to perform the stripping.|
|asan||builds libcryptopp.a and cryptest.exe with Address Sanitizer support. This is primarily used for Testing and QA.|
|ubsan||builds libcryptopp.a and cryptest.exe with Undefined Behavior Sanitizer support. This is primarily used for Testing and QA.|
|sources||lists the source files used for (1) libcryptopp.a and (2) cryptest.exe. The file list can then be used for Autotools, Cmake, etc.|
|docs, html||builds the HTML-based Doxygen documentation. The output directory is based on the library version; for example, ref563, ref57, and so on. Its named like this because the web server uses it that way.|
|convert||converts text files to CRLF endings, and converts Unix-compatible scripts ot LF endings. That's simply what the project uses as its standard, and its been that way for about 20 years.|
The makefile has a standard recipe it uses for object files and its shown below. It is at the end of the makefile and is a "catch all" rule. It will be used if a more specific rule is not found to build an object file.
%.o : %.cpp $(CXX) $(CXXFLAGS) -c $<
In addition, workarounds for some platforms are applied via recipes. For example, the following is used for Macport GCC compilers because they lack init_priority. It proceeds the standard object file recipe so make will use it when activated:
ifeq ($(GCC_COMPILER)$(MACPORTS_COMPILER),11) ifeq ($(findstring -DMACPORTS_GCC_COMPILER,$(CXXFLAGS)),) cryptlib.o: $(CXX) $(CXXFLAGS) -DMACPORTS_GCC_COMPILER=1 -c cryptlib.cpp cpu.o: $(CXX) $(CXXFLAGS) -DMACPORTS_GCC_COMPILER=1 -c cpu.cpp endif endif ... %.o : %.cpp $(CXX) $(CXXFLAGS) -c $
The recipe to build the static archive is as follows. Making $(LIBOBJS) a order-relative prerequisite means the library is only made if needed, and not every time make is invoked.
libcryptopp.a: $(LIBOBJS) | public_service $(AR) $(ARFLAGS) $@ $(LIBOBJS) $(RANLIB) $@
The recipe to build the shared object on BSD, Linux and Unix compatibles is as follows. If versioning is being used and HAS_SOLIB_VERSION is in effect, then the library will be named like libcryptopp.so.5.6.3 and libcryptopp.so will be soft linked to it. Otherwise, the artifact will be named libcryptopp.so.
libcryptopp.so$(SOLIB_VERSION_SUFFIX): $(LIBOBJS) | public_service $(CXX) -shared $(SOLIB_FLAGS) -o $@ $(CXXFLAGS) $(GOLD_OPTION) $(LIBOBJS) $(LDLIBS) ifeq ($(HAS_SOLIB_VERSION),1) -$(LN) libcryptopp.so$(SOLIB_VERSION_SUFFIX) libcryptopp.so -$(LN) libcryptopp.so$(SOLIB_VERSION_SUFFIX) libcryptopp.so$(SOLIB_COMPAT_SUFFIX) endif
The recipe to build the dynamic library on OS X is as follows.
libcryptopp.dylib: $(LIBOBJS) $(CXX) -dynamiclib -o $@ $(CXXFLAGS) -install_name "$@" -current_version "$(LIB_MAJOR).$(LIB_MINOR).$(LIB_PATCH)" \ -compatibility_version "$(LIB_MAJOR).$(LIB_MINOR)" $(LIBOBJS)
The recipe to build the cryptest program is as follows. Notice it uses the compiler driver to perform the link:
cryptest.exe: libcryptopp.a $(TESTOBJS) | public_service $(CXX) -o $@ $(CXXFLAGS) $(TESTOBJS) ./libcryptopp.a $(LDFLAGS) $(GOLD_OPTION) $(LDLIBS)
There's one final recipe that is intended to help Crypto++ package maintainers, and it is the use of DESTDIR for the install rule. All installation recipes use DESTDIR:
.PHONY: install install: $(MKDIR) -p $(DESTDIR)$(INCLUDEDIR)/cryptopp $(CP) *.h $(DESTDIR)$(INCLUDEDIR)/cryptopp -$(CHMOD) 0755 $(DESTDIR)$(INCLUDEDIR)/cryptopp -$(CHMOD) 0644 $(DESTDIR)$(INCLUDEDIR)/cryptopp/*.h ifneq ($(wildcard libcryptopp.a),) $(MKDIR) -p $(DESTDIR)$(LIBDIR) $(CP) libcryptopp.a $(DESTDIR)$(LIBDIR) -$(CHMOD) 0644 $(DESTDIR)$(LIBDIR)/libcryptopp.a endif ifneq ($(wildcard cryptest.exe),) $(MKDIR) -p $(DESTDIR)$(BINDIR) $(CP) cryptest.exe $(DESTDIR)$(BINDIR) -$(CHMOD) 0755 $(DESTDIR)$(BINDIR)/cryptest.exe $(MKDIR) -p $(DESTDIR)$(DATADIR)/cryptopp $(CP) -r TestData $(DESTDIR)$(DATADIR)/cryptopp $(CP) -r TestVectors $(DESTDIR)$(DATADIR)/cryptopp -$(CHMOD) 0755 $(DESTDIR)$(DATADIR)/cryptopp -$(CHMOD) 0755 $(DESTDIR)$(DATADIR)/cryptopp/TestData -$(CHMOD) 0755 $(DESTDIR)$(DATADIR)/cryptopp/TestVectors -$(CHMOD) 0644 $(DESTDIR)$(DATADIR)/cryptopp/TestData/*.dat -$(CHMOD) 0644 $(DESTDIR)$(DATADIR)/cryptopp/TestVectors/*.txt endif ...
It used to work!!!
If Crypto++ used to work for you under Crypto++ 5.6.2, but fails to work as expected under 5.6.3 or 5.6.4, then its probably due to GNUmakefile changes or changes in code. In this case, you can use Git to go back in time. The example below uses the Crypto++ 5.6.2 makefile to build the latest sources.
$ git clone https://github.com/weidai11/cryptopp cryptopp-past-and-present Cloning into 'cryptopp-past-and-present'... ... $ cd cryptopp-past-and-present $ git checkout CRYPTOPP_5_6_2 Note: checking out 'CRYPTOPP_5_6_2'. You are in 'detached HEAD' state... ... $ cp GNUmakefile GNUmakefile-5.6.2 $ git checkout CRYPTOPP_5_6_4 # or 'checkout master' $ cp bench1.cpp bench.cpp # account for the bench.cpp -> bench1.cpp rename $ make -f GNUmakefile-5.6.2 # use the old makefile c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c shacal2.cpp c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c md5.cpp c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c shark.cpp c++ -DNDEBUG -g -O2 -DCRYPTOPP_DISABLE_ASM -pipe -c zinflate.cpp ...
We use the technique above often to determine if we introduced a break.
cryptest-sh.zip - archive with script to test Crypto++ using various configurations. The configurations include Debug, Release, -O1, -O2, -O3, -Os, C++03, C++11, C++14, C++17, GNU's libstdc++, LLVM's libc++, Undefined Behavior sanitizer, Address sanitizer and Valgrind.