Android (Command Line)
This page will provide instructions for cross-compiling Crypto++ on the command line for Android. The instructions below use setenv-android.sh
and GNUmakefile-cross
for cross-compiles. If you want to try an Android.mk
and Application.mk
, then see the Android.mk wiki article.
There are four steps to building Crypto++ for Android, and the process will create an Android version of cryptest.exe
, the dynamic library, and the static library. After building the library, you can push cryptest.exe
and the test vectors to the device, and then execute the tests using a remote shell.
While you can use the Crypto++ shared object directly though JNI, you will likely provide an application specific shared object with C-style external interfaces which depends upon the static Crypto++ library. The C-style interface will make your shared object easy to call using JNI; while the Crypto++ static library will allow you to aggressively strip unused code. See Wrapper DLL for details.
setenv-android.sh
is a moving target. It is a moving target because Android keeps changing the KDK, the toolchains and the paths to the tools. One version of the script tuned for a particular NDK probably won't work with another NDK version. You can thank the Android team for that.
Three related pages for Android are Android Setup (Command Line), Android.mk and Android Activity. Android Setup (Command Line) discusses how to setup an Android build machine. Android.mk builds the library using Android.mk and Application.mk. Android Activity uses the Crypto++ shared object in an Android activity.
setenv-android.sh
Before we begin, the history of setenv-android.sh
needs to be explained. From about 2011 or 2012 until about 2018 the project used setenv-android.sh
. It supported Android NDK r6 or so up to about NDK r15. At around NDK r15 the script needed to be rewritten due to changes in the NDK.
In 2018 setenv-android.sh
was modified to support the updated NDK. The updated NDK is r15 or r16 and above. A lot of breaking changes occurred in the NDK so the script had a lot of reworking. Thanks to Skycoder for doing the work. Also see PR #546, Apply updated android build rules.
In 2018 Android announced Clang was the new default compiler, and GCC support was going to be dropped. In response our recently updated setenv-android.sh
was updated again.
Between the second and third iteration of the script we tried to supply setenv-android-clang.sh
and setenv-android-gcc.sh
. The GCC and Clang versions of the script were broken experiments. You should not use them.
When you see setenv-android.sh
used below in the procedures, please know it is one of the variations listed above.
Set the Environment
Before you begin you must set the cross-compilation environment. Setting the environment will do two things. First, it will ensure the Android toolchain is on-path. Second, it will ensure some environmental variables are set so the makefile picks up specific device flags. For example, the script will set IS_ANDROID
, AOSP_SYSROOT
, AOSP_STL_INC
and AOSP_STL_LIB
.
You must set ANDROID_NDK_ROOT
prior to running the script. You should also set ANDROID_SDK_ROOT
for that matter, though its not used for cross-compiling. Android recommends setting both ANDROID_NDK_ROOT
and ANDROID_SDK_ROOT
because the NDK and SDK use the variables internally. For details, see Recommended NDK Directory?.
Perform the following to run the script. The source
command is required to make changes to the current shell.
$ source ./setenv-android.sh ANDROID_NDK_ROOT is /opt/android-ndk ANDROID_NDK_ROOT: /opt/android-ndk AOSP_TOOLCHAIN_PATH: /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64//bin/ AOSP_API: 23 AOSP_RUNTIME: libc++ AOSP_SYSROOT: /opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64//sysroot AOSP_FLAGS: -march=armv7-a -mthumb -mfloat-abi=softfp -funwind-tables -fexceptions -frtti CPU FEATURES: cpu-features.h and cpu-features.c are present
setenv-android.sh
takes two optional arguments. The first optional argument is the architecture. The default is armeabi-v7a
. Architectures can be one of:
- armeabi-v7a
- arm64-v8a
- x86
- x86_64
The second optional argument is the Android API level. The default API is 21. If you don't specify an API then 21 will be used.
# Build for Android 6.0 AOSP_API="android-23" source setenv-android.sh # Build for Android 6.1 AOSP_API="android-24" source setenv-android.sh # Build for ARM64 (AARCH64) $ source setenv-android.sh arm64 # Build for ARM64 (again) $ source setenv-android.sh armv8a
Note that if more than one library requires the C++ runtime or STL library, then you must use the dynamic version of the library. For example, if you are using Qt and Crypto++, then you must use libc++_shared.so
.
Build the Library
Building the library consists of running the following command after setting the environment:
$ make -f GNUmakefile-cross static dynamic armv7a-linux-androideabi23-clang++ -DNDEBUG -g2 -O3 -fPIC -pipe -Wall -march=arm v7-a -mthumb -mfloat-abi=softfp -funwind-tables -fexceptions -frtti -stdlib=libc ++ -DANDROID -D__ANDROID_API__=23 --sysroot=/opt/android-ndk/toolchains/llvm/pre built/linux-x86_64//sysroot -Wa,--noexecstack -c cryptlib.cpp armv7a-linux-androideabi23-clang++ -DNDEBUG -g2 -O3 -fPIC -pipe -Wall -march=arm v7-a -mthumb -mfloat-abi=softfp -funwind-tables -fexceptions -frtti -stdlib=libc ++ -DANDROID -D__ANDROID_API__=23 --sysroot=/opt/android-ndk/toolchains/llvm/pre built/linux-x86_64//sysroot -Wa,--noexecstack -c cpu.cpp armv7a-linux-androideabi23-clang++ -DNDEBUG -g2 -O3 -fPIC -pipe -Wall -march=arm v7-a -mthumb -mfloat-abi=softfp -funwind-tables -fexceptions -frtti -stdlib=libc ++ -DANDROID -D__ANDROID_API__=23 --sysroot=/opt/android-ndk/toolchains/llvm/pre built/linux-x86_64//sysroot -Wa,--noexecstack -c integer.cpp ...
You can verify the library was built for the correct architecture with the following.
$ find . -name cryptest.exe ./cryptest.exe $ readelf -h ./cryptest.exe | grep -i 'class\|machine' Class: ELF32 Machine: ARM $ $ find . -name libcryptopp.so ./libcryptopp.so $ readelf -h ./libcryptopp.so | grep -i 'class\|machine' Class: ELF32 Machine: ARM
You can run ndk-depends
to verify dependencies:
$ $ANDROID_NDK_ROOT/ndk-depends libcryptopp.so libcryptopp.so libstdc++.so libc++.so libm.so libdl.so libc.so
If you want to use SOLIB versioning, then you must include HAS_SOLIB_VERSION=1
in the make command. For example:
$ make -f GNUmakefile-cross lean HAS_SOLIB_VERSION=1
Strip the Library
To perform dead-code stripping, be sure to link your final executable with -Wl,--gc-sections
. If you link your final application against libcryptopp.a
then you will see significant gains. If you link your final application against libcryptopp.so
then you will experience a small gains.
You can also strip debug symbols from your final executable, but it will make back traces useless. To do so, you can strip by running arm-linux-androideabi-strip
with --strip-debug
, --strip-unneeded
or --strip-all
. arm-linux-androideabi-strip
is available on path after running setenv-android.sh
. See arm-eabi-strip and binary size for details.
$ ls -al cryptest.exe -rwxr-xr-x 1 jwalton staff 42860268 Jul 29 00:41 cryptest.exe $ $ arm-linux-androideabi-strip --strip-debug cryptest.exe $ ls -al cryptest.exe -rwxr-xr-x 1 jwalton staff 6112963 Jul 29 00:41 cryptest.exe $ $ arm-linux-androideabi-strip --strip-all cryptest.exe $ ls -al cryptest.exe -rwxr-xr-x 1 jwalton staff 2903712 Jul 29 00:41 cryptest.exe
$ ls -al libcryptopp.so -rwxr-xr-x 1 jwalton staff 28668308 Jul 29 00:52 libcryptopp.so $ $ arm-linux-androideabi-strip --strip-debug libcryptopp.so $ ls -al libcryptopp.so -rwxr-xr-x 1 jwalton staff 5478765 Jul 29 00:52 libcryptopp.so $ $ arm-linux-androideabi-strip --strip-all libcryptopp.so $ ls -al libcryptopp.so -rwxr-xr-x 1 jwalton staff 3808428 Jul 29 00:52 libcryptopp.so
Additionally, there are other tricks for modest gains. See Android NDK: How to Reduce Binaries Size for details.
Push to Device
To test on the device, you will use adb
to push cryptest.exe
and the test vectors to the device. Once on a device, the program will be executed with the remote shell. To begin, ensure Android's platform-tools
are on path (i.e., ANDROID_SDK_ROOT/platform-tools
), and your device is recognized:
$ adb devices List of devices attached 0380614543dfd297 device
With a device available, push cryptest.exe
, TestData
, and TestVectors
to /data/local/tmp
.
$ adb push cryptest.exe /data/local/tmp 1295 KB/s (43249212 bytes in 32.591s) $ $ adb push TestData /data/local/tmp/TestData push: TestData/xtrdh342.dat -> /data/local/tmp/TestData/xtrdh342.dat push: TestData/xtrdh171.dat -> /data/local/tmp/TestData/xtrdh171.dat push: TestData/usage.dat -> /data/local/tmp/TestData/usage.dat ... 55 files pushed. 0 files skipped. 471 KB/s (57449 bytes in 0.119s) $ $ adb push TestVectors /data/local/tmp/TestVectors push: TestVectors/whrlpool.txt -> /data/local/tmp/TestVectors/whrlpool.txt push: TestVectors/wake.txt -> /data/local/tmp/TestVectors/wake.txt push: TestVectors/vmac.txt -> /data/local/tmp/TestVectors/vmac.txt ... 33 files pushed. 0 files skipped. 3643 KB/s (1314149 bytes in 0.352s)
If you linked against libstlport_shared.so
or libc++_shared.so
, then you will need to push it also:
$ adb push "$AOSP_STL_LIB" /data/local/tmp 3261 KB/s (3547204 bytes in 1.062s)
After pushing, open a remote shell with adb shell
and verify all files are present on the device:
$ adb shell shell@android: $ ls -l /data/local/tmp drwxrwxr-x shell shell 2015-12-07 00:37 TestData drwxrwxr-x shell shell 2015-12-07 00:38 TestVectors -rwxrwxrwx shell shell 26930220 2015-12-07 00:36 cryptest.exe -rwxrwxrwx shell shell 5630724 2014-10-17 02:40 libgnustl_shared.so -rwxrwxrwx shell shell 3547204 2014-12-10 05:01 libstlport_shared.so
Execute the Program
With the program and test vectors on the device, execute the test program:
shell@android:/ $ cd /data/local/tmp shell@android:/data/local/tmp $ ./cryptest.exe v Using seed: 1374978640 Testing Settings... passed: Your machine is little endian. passed: sizeof(byte) == 1 passed: sizeof(word16) == 2 passed: sizeof(word32) == 4 passed: sizeof(word64) == 8 passed: sizeof(hword) == 2, sizeof(word) == 4, sizeof(dword) == 8 ...
If you linked against libstlport_shared.so
or libc++_shared.so
, then you will need to modify LD_LIBRARY_PATH
when running cryptest.exe
:
shell@android:/ $ cd /data/local/tmp shell@android:/data/local/tmp $ LD_LIBRARY_PATH=./; ./cryptest.exe v Using seed: 1374994654 Testing Settings... ...
If you link to both libstlport_shared.so
and libcryptopp.so
, then you will have to preload libstlport_shared.so
:
shell@android:/ $ cd /data/local/tmp shell@android:/data/local/tmp $ LD_LIBRARY_PATH=./; ./cryptest.exe v link_image[1936]: 5431 could not load needed library './libcryptopp.so' for './cryptest.exe' (reloc_library[1285]: 5431 cannot locate '_ZNSt12__node_alloc11_M_allocateERj'...) CANNOT LINK EXECUTABLE shell@android:/data/local/tmp $ shell@android:/data/local/tmp $ export LD_PRELOAD=`pwd`/libstlport_shared.so shell@android:/data/local/tmp $ LD_LIBRARY_PATH=./; ./cryptest.exe v Using seed: 1375005963 Testing Settings... ...
If all goes well, the tail of cryptest.exe v
will display "All tests passed!" as shown below.
Install the Library
Once the library has been verified on a device, copy the header files (*.h
), the dynamic library (libcryptopp.so
), and the static library (libcryptopp.a
) to a directory for use in other projects. You can run sudo make -f GNUmakefile-cross install PREFIX=/usr/local/cryptopp/android-armeabi-v7a
since the libraries are for API 21 and ARMv7 architecture.
Under this scheme, headers will be found at /usr/local/cryptopp/android-armeabi-v7a/include
, and the libraries will be found at /usr/local/cryptopp/android-armeabi-v7a/lib
. You can use the following with ndk-build
. ndk-build
will set $(TARGET_ARCH_ABI)
:
CRYPTOPP_INCL := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/include
CRYPTOPP_LIB := /usr/local/cryptopp/android-$(TARGET_ARCH_ABI)/lib
All Architectures
If you need to build all the architectures and install them, then the following script will accomplish it for you. Place the script in the root of the Crypto++ folder. Prior to running the script, ensure both ANDROID_NDK_ROOT
and ANDROID_SDK_ROOT
are set.
$ cat build-all-android.sh #!/usr/bin/env bash for arch in armeabi armeabi-v7a armeabi-v7a-hard arm64-v8a mips mips64 x86 x86_64 do source setenv-android.sh $arch if [ "$?" -eq "0" ]; then make -f GNUmakefile-cross distclean make -f GNUmakefile-cross static dynamic sudo make -f GNUmakefile-cross install PREFIX=/usr/local/cryptopp/android-$arch fi done
To build arm64-v8a
and other new additions, you will need the NDK r10e, and you will need to use the Android 21 API. The script uses them by default.
Crypto++ or your shared object wrapper can be loaded in a number of different ways on Android. Development environments, like QT, can further complicate matters because of the way it calls dlopen
. If you are experiencing an UnsatisfiedLinkError: dlopen failed: library "./obj/local/armeabi-v7a/libcryptopp.so"
, then please see the following:
- Linux | Note for Shared Object Callers (Crypto++ wiki)
- Two QT issues with Crypto++ on Android (Crypto++ mailing list)
- Android 6.0 Changes | Runtime (Android Developer)
- Library not found due to targetSdkVersion (armeabi-v7a and libcryptopp.so) (Stack Overflow)
- Android Linker code (AOSP code on Google Source)
- Cannot load native libraries in Android 6.0 which report "libfoo.so has text relocations" (AOSP Bug Report)
Android Activity
You can find an example of using prebuilt Crypto++ shared objects on the wiki at Android Activity. Android Activity uses a sample project called Android-PRNG, and it demonstrates two topics. First, it shows you how to integrate a prebuilt Crypto++ library shared object into a Ant-based or NDK-based project. It may work for Android Studio, too. It may even work for Eclipse, depending on how well it imports an Android java project with JNI.
Second, it shows you how to accumulate seed data from sensors that can be used to seed your software based random number generators. A generator should have a fresh seed applied with IncorporateEntropy before each call to GenerateBlock to ensure the fitness of the generator.
If you are using Android Studio, then you may need to add the following. Also see JNI and Gradle in Android Studio on Stack Overflow.
sourceSets.main { jni.srcDirs = [] jniLibs.srcDir 'src/main/libs' }
Keep in mind you must statically load the libraries in the order that satisfies link dependencies. An example of doing that is:
public class MyClass { static { System.loadLibrary("stlport_shared"); System.loadLibrary("cryptopp"); System.loadLibrary("mylib"); } ... }
Android's C++ Library Support now recommends using ReLinker to relieve you of manually loading libraries in the correct order.
NDK Build Flags
The following are taken from the NDK. They show Android's standard build flags.
You can test the current Android flags using the following from Google's JNI sample. It will build the sample Hello-JNI all of the architectures.
android$ git clone https://github.com/googlesamples/android-ndk android$ cd android-ndk android$ git checkout android-mk android$ cd hello-jni android:hello-jni$ ndk-build APP_PLATFORM=android-20 V=1 ...
Options like -DNDEBUG
, -Os
, -g
and -fmessage-length=0
were omitted. Some flags, like -fexception
, must be used to ensure exceptions pass through C routines. Other flags must not be used, like -fno-exceptions
and -fno-rtti
.
NDK r12 flags
The r12 flags are tailored for GCC. Android had not switched to Clang yet.
armeabi
- -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -march=armv5te -mtune=xscale -msoft-float -mthumb -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
- Do not use: -fno-exceptions -fno-rtti
armeabi-v7a
- -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb -Wl,--fix-cortex-a8 -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
- Do not use: -fno-exceptions -fno-rtti
arm64-v8a
- -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector-strong -fomit-frame-pointer -fstrict-aliasing -funswitch-loops -finline-limit=300 -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
- Do not use: -fno-exceptions -fno-rtti
mips
- -MMD -MP -MF -fpic -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -fomit-frame-pointer -funswitch-loops -finline-limit=300 -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
- Do not use: -fno-exceptions -fno-rtti
mips64
- -MMD -MP -MF -fpic -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers -fomit-frame-pointer -funswitch-loops -finline-limit=300 -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
- Do not use: -fno-exceptions -fno-rtti
x86
- -MMD -MP -MF -ffunction-sections -funwind-tables -fstack-protector -fomit-frame-pointer -fstrict-aliasing -funswitch-loops -finline-limit=300 -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
- Do not use: -fno-exceptions -fno-rtti
x86_64
- -MMD -MP -MF -ffunction-sections -funwind-tables -fstack-protector-strong -fomit-frame-pointer -fstrict-aliasing -funswitch-loops -finline-limit=300 -DANDROID -Wa,--noexecstack -Wformat -Werror=format-security
- Do not use: -fno-exceptions -fno-rtti
NDK r16 flags
The r16 flags are tailored for GCC. Android had not switched to Clang yet.
armeabi-v7a
- -MMD -MP -MF -fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -fno-integrated-as -g -target armv7-none-linux-androideabi19 -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -DANDROID -D__ANDROID_API__=19 -Wa,--noexecstack -Wformat -Werror=format-security --sysroot /opt/android-ndk-r16b/sysroot -isystem /opt/android-ndk-r16b/sysroot/usr/include/arm-linux-androideabi
arm64-v8a
- -MMD -MP -MF ./obj/local/arm64-v8a/objs-debug/hello-jni/hello-jni.o.d -gcc-toolchain /opt/android-ndk-r16b/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64 -target aarch64-none-linux-android -ffunction-sections -funwind-tables -fstack-protector-strong -fpic -no-canonical-prefixes -DANDROID -D__ANDROID_API__=21 -Wa,--noexecstack -Wformat -Werror=format-security --sysroot /opt/android-ndk-r16b/sysroot -isystem /opt/android-ndk-r16b/sysroot/usr/include/aarch64-linux-android
x86
- -MMD -MP -MF -target i686-none-linux-android -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC -no-canonical-prefixes -DANDROID -D__ANDROID_API__=19 -Wa,--noexecstack -Wformat -Werror=format-security -mstackrealign --sysroot /opt/android-ndk-r16b/sysroot -isystem /opt/android-ndk-r16b/sysroot/usr/include/i686-linux-android
x86_64
- -MMD -MP -MF -target x86_64-none-linux-android -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC -no-canonical-prefixes -DANDROID -D__ANDROID_API__=21 -Wa,--noexecstack -Wformat -Werror=format-security --sysroot /opt/android-ndk-r16b/sysroot -isystem /opt/android-ndk-r16b/sysroot/usr/include/x86_64-linux-android
NDK r19 flags
The r19 flags are tailored for Clang. Android dropped support for GCC.
TODO...
Downloads
None. The files are present in GitHub.