September 2024
There is now a long standing issue with the latest versions of OpenSSL. The handwritten assembly is incompatible with Position Independent Code. While this issue is not resolved it means the latest versions of OpenSSL do not run on Android.
As a side note, the NDK port of OpenSSL is still on version 1.1.1 which is EOL. This is a huge vunerability for Android but it seems the NDK team has no priority on updating this.
In a client project I’m using Rust which depends on OpenSSL. This has left me no choice other than to patch OpenSSL myself, so I get it to compile to Android platforms.
Here is a script that will download and compile OpenSSL for Android directly. It already includes the patching described in the GitHub issue to patch the handcrafted assembly.
#!/bin/bash
# Clone or download the sources (it's done for you at below)
# You should most definitely read the ANDROID notes to see the exact command. A lot of the scripts online are outdated for the 1.X branches
# You basically need to set the ANDROID_NDK_HOME variable for this script to work
# Generating the dylibs was failing for me, so disabled it for now
#set -v
set -ex
export OPENSSL_VERSION="openssl-3.3.2"
rm -rf ${OPENSSL_VERSION}
# Sometimes this link breaks, you can manually download the sources yourself
# curl -O "https://github.com/openssl/openssl/releases/download/${OPENSSL_VERSION}/${OPENSSL_VERSION}.tar.gz"
tar xfz "${OPENSSL_VERSION}.tar.gz"
PROJECT_HOME=`pwd`
PATH_ORG=$PATH
OUTPUT_DIR="libs"
# Clean output:
rm -rf $OUTPUT_DIR
mkdir $OUTPUT_DIR
build_android_clang() {
echo ""
echo "----- Build libcrypto & libssl.so for "$1" -----"
echo ""
ARCHITECTURE=$1
TOOLCHAIN=$2
stl="libc++"
# Set toolchain
export TOOLCHAIN_ROOT=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/darwin-x86_64
export SYSROOT=$TOOLCHAIN_ROOT/sysroot
export CC=${TOOLCHAIN}21-clang
export CXX=${TOOLCHAIN}21-clang++
export CXXFLAGS="-fPIC"
export CPPFLAGS="-DANDROID -fPIC"
export PATH=$TOOLCHAIN_ROOT/bin:$SYSROOT/usr/local/bin:$PATH
cd "${OPENSSL_VERSION}"
./Configure $ARCHITECTURE no-asm no-shared -D__ANDROID_API__=21
make clean
# Apply patch that fixes the armcap instruction
# Linux version
# sed -e '/[.]hidden.*OPENSSL_armcap_P/d; /[.]extern.*OPENSSL_armcap_P/ {p; s/extern/hidden/ }' -i -- crypto/*arm*pl crypto/*/asm/*arm*pl
# macOS version
sed -E -i '' -e '/[.]hidden.*OPENSSL_armcap_P/d' -e '/[.]extern.*OPENSSL_armcap_P/ {p; s/extern/hidden/; }' crypto/*arm*pl crypto/*/asm/*arm*pl
make
mkdir -p ../$OUTPUT_DIR/${ARCHITECTURE}/lib
mkdir -p ../$OUTPUT_DIR/${ARCHITECTURE}/include
cp libcrypto.a ../$OUTPUT_DIR/${ARCHITECTURE}/lib/libcrypto.a
cp libssl.a ../$OUTPUT_DIR/${ARCHITECTURE}/lib/libssl.a
cp -R include/openssl ../$OUTPUT_DIR/${ARCHITECTURE}/include
cd ..
}
build_android_clang "android-arm" "armv7a-linux-androideabi"
build_android_clang "android-x86" "i686-linux-android"
build_android_clang "android-x86_64" "x86_64-linux-android"
build_android_clang "android-arm64" "aarch64-linux-android"
This script will create a libs
folder, which contains all the files in the necessary structure.
In order to compile this in your Rust crate you need to first add the crates:
openssl = "0.10"
openssl-sys = "0.9.103"
openssl
is a bindings crate for the Rust code, but the openssl-sys takes care of compiling/detecting OpenSSL itself.
With this crates added, we can then invoke the compilation command with an added flag OPENSSL_DIR
which openssl-sys
will detect and instead of compiling OpenSSL from source will use the precompiled static libraries we generated.
Here I’m using cargo-ndk
which is a crate that does the SDK/NDK discovery for you so you can easily compile for android. This is a Makefile
version, but you can use whatever you want to point to it to the correct folder that matches the architecture:
android-build-release-%:
@ if [ "$*" = "aarch64-linux-android" ]; then \
OPENSSL_DIR=$(CURDIR)/libs/android-arm64; \
elif [ "$*" = "x86_64-linux-android" ]; then \
OPENSSL_DIR=$(CURDIR)/libs/android-x86_64; \
elif [ "$*" = "armv7-linux-androideabi" ]; then \
OPENSSL_DIR=$(CURDIR)/libs/android-arm; \
elif [ "$*" = "i686-linux-android" ]; then \
OPENSSL_DIR=$(CURDIR)/libs/android-x86; \
else \
echo "Unknown target: $*"; \
exit 1; \
fi; \
OPENSSL_DIR=$$OPENSSL_DIR cargo ndk --target $* --platform 31 build --release --all-features
Once everything is ready, will not only the Android binary be correct, you will actually be using the latest version of OpenSSL instead of the outdated NDK ports.