注意
この記事でビルドする OpenSSL は 1.0.1j という古いバージョンです!!
脆弱性を考慮して、可能であれば最新バージョンを使うべきです。
最新バージョンをビルドするため公式ドキュメントはこちら。
...のはずですが、ちっともビルドできんやんか
はじめに
訳あって、OpenSSL の 1.0.1j という古いバージョンを
Android 向けにビルドしてスタティックライブラリ(.a)を作成する必要があり、
試行錯誤してなんとかビルドできたので記録しておきます。
まぁ、遅ればせながら、64ビット対応 をしたということです。
ソースコードを取得
公式サイトの Downloads にある [source/old] (https://www.openssl.org/source/old/) からソースコード(tar.gz形式)をダウンロードして適当なところに展開します。
$ mkdir OpenSSL
$ cd OpenSSL
$ curl https://www.openssl.org/source/old/1.0.1/openssl-1.0.1j.tar.gz -o openssl-1.0.1j.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4329k 100 4329k 0 0 1888k 0 0:00:02 0:00:02 --:--:-- 1889k
$ ls -1
total 8664
openssl-1.0.1j.tar.gz
$ tar zxf openssl-1.0.1j.tar.gz
$ ls -1
openssl-1.0.1j/
openssl-1.0.1j.tar.gz
NDKを取得
公式サイトのDOWNLOADSからNDK(zip形式)をダウンロードして適当なところに解凍します。
ワタシの環境は mac なので darwin-x86_64 というのをダウンロードしました。
$ curl https://dl.google.com/android/repository/android-ndk-r20-darwin-x86_64.zip -o android-ndk-r20-darwin-x86_64.zip
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 804M 100 804M 0 0 10.8M 0 0:01:14 0:01:14 --:--:-- 11.1M
$ unzip -q android-ndk-r20-darwin-x86_64.zip
$ ls -1
android-ndk-r20/
android-ndk-r20-darwin-x86_64.zip
openssl-1.0.1j/
openssl-1.0.1j.tar.gz
Makefile生成
ビルド環境 = 動作環境 であれば、
$ cd openssl-1.0.1j/
$ ./config
とやれば、ビルド用の Makefile が生成されるというパターンが多いみたいですが、
今回の場合は動作環境が Android なわけで、Androd 上で動作するバイナリをビルドする為の Makefile を生成しなくてはなりません。
ツールチェイン
前項で取得してきた NDK にはツールチェインというものが含まれていて、ソースコードをコンパイル/リンクするにあたっては、mac の gcc コマンドではなく、ツールチェインが提供するものを代わりを使うようです。
ツールチェインは NDKを解凍したフォルダ/toolchains/
にあります。
$ ls -1 android-ndk-r20/toolchains/
aarch64-linux-android-4.9/
arm-linux-androideabi-4.9/
llvm/
renderscript/
x86-4.9/
x86_64-4.9/
しかし、
「armeabi-v7a 用をビルドするには arm-linux-androideabi-4.9 を使えばいいのね」
ではないんです。
現在の mac(というかXcode)は、gccではなくclangを使っているためか、llvm を使うようです。
で、使うツール類は mac なら prebuilt/darwin-x86_64/bin
の下に
$ ls android-ndk-r20/toolchains/llvm/prebuilt/darwin-x86_64/bin/
aarch64-linux-android-addr2line armv7a-linux-androideabi21-clang i686-linux-android27-clang
aarch64-linux-android-ar armv7a-linux-androideabi21-clang++ i686-linux-android27-clang++
aarch64-linux-android-as armv7a-linux-androideabi22-clang i686-linux-android28-clang
aarch64-linux-android-c++filt armv7a-linux-androideabi22-clang++ i686-linux-android28-clang++
aarch64-linux-android-dwp armv7a-linux-androideabi23-clang i686-linux-android29-clang
aarch64-linux-android-elfedit armv7a-linux-androideabi23-clang++ i686-linux-android29-clang++
aarch64-linux-android-gprof armv7a-linux-androideabi24-clang ld.lld
aarch64-linux-android-ld armv7a-linux-androideabi24-clang++ llvm-ar
aarch64-linux-android-ld.bfd armv7a-linux-androideabi26-clang llvm-as
aarch64-linux-android-ld.gold armv7a-linux-androideabi26-clang++ llvm-config
aarch64-linux-android-nm armv7a-linux-androideabi27-clang llvm-cov
aarch64-linux-android-objcopy armv7a-linux-androideabi27-clang++ llvm-dis
aarch64-linux-android-objdump armv7a-linux-androideabi28-clang llvm-link
aarch64-linux-android-ranlib armv7a-linux-androideabi28-clang++ llvm-modextract
aarch64-linux-android-readelf armv7a-linux-androideabi29-clang llvm-nm
aarch64-linux-android-size armv7a-linux-androideabi29-clang++ llvm-objcopy
aarch64-linux-android-strings bisect_driver.py llvm-profdata
aarch64-linux-android-strip clang llvm-readobj
aarch64-linux-android21-clang clang++ llvm-strip
aarch64-linux-android21-clang++ clang-check llvm-symbolizer
aarch64-linux-android22-clang clang-format sancov
aarch64-linux-android22-clang++ clang-tidy sanstats
aarch64-linux-android23-clang clang-tidy.real scan-build
aarch64-linux-android23-clang++ git-clang-format scan-view
aarch64-linux-android24-clang i686-linux-android-addr2line x86_64-linux-android-addr2line
aarch64-linux-android24-clang++ i686-linux-android-ar x86_64-linux-android-ar
aarch64-linux-android26-clang i686-linux-android-as x86_64-linux-android-as
aarch64-linux-android26-clang++ i686-linux-android-c++filt x86_64-linux-android-c++filt
aarch64-linux-android27-clang i686-linux-android-dwp x86_64-linux-android-dwp
aarch64-linux-android27-clang++ i686-linux-android-elfedit x86_64-linux-android-elfedit
aarch64-linux-android28-clang i686-linux-android-gprof x86_64-linux-android-gprof
aarch64-linux-android28-clang++ i686-linux-android-ld x86_64-linux-android-ld
aarch64-linux-android29-clang i686-linux-android-ld.bfd x86_64-linux-android-ld.bfd
aarch64-linux-android29-clang++ i686-linux-android-ld.gold x86_64-linux-android-ld.gold
arm-linux-androideabi-addr2line i686-linux-android-nm x86_64-linux-android-nm
arm-linux-androideabi-ar i686-linux-android-objcopy x86_64-linux-android-objcopy
arm-linux-androideabi-as i686-linux-android-objdump x86_64-linux-android-objdump
arm-linux-androideabi-c++filt i686-linux-android-ranlib x86_64-linux-android-ranlib
arm-linux-androideabi-dwp i686-linux-android-readelf x86_64-linux-android-readelf
arm-linux-androideabi-elfedit i686-linux-android-size x86_64-linux-android-size
arm-linux-androideabi-gprof i686-linux-android-strings x86_64-linux-android-strings
arm-linux-androideabi-ld i686-linux-android-strip x86_64-linux-android-strip
arm-linux-androideabi-ld.bfd i686-linux-android16-clang x86_64-linux-android21-clang
arm-linux-androideabi-ld.gold i686-linux-android16-clang++ x86_64-linux-android21-clang++
arm-linux-androideabi-nm i686-linux-android17-clang x86_64-linux-android22-clang
arm-linux-androideabi-objcopy i686-linux-android17-clang++ x86_64-linux-android22-clang++
arm-linux-androideabi-objdump i686-linux-android18-clang x86_64-linux-android23-clang
arm-linux-androideabi-ranlib i686-linux-android18-clang++ x86_64-linux-android23-clang++
arm-linux-androideabi-readelf i686-linux-android19-clang x86_64-linux-android24-clang
arm-linux-androideabi-size i686-linux-android19-clang++ x86_64-linux-android24-clang++
arm-linux-androideabi-strings i686-linux-android21-clang x86_64-linux-android26-clang
arm-linux-androideabi-strip i686-linux-android21-clang++ x86_64-linux-android26-clang++
armv7a-linux-androideabi16-clang i686-linux-android22-clang x86_64-linux-android27-clang
armv7a-linux-androideabi16-clang++ i686-linux-android22-clang++ x86_64-linux-android27-clang++
armv7a-linux-androideabi17-clang i686-linux-android23-clang x86_64-linux-android28-clang
armv7a-linux-androideabi17-clang++ i686-linux-android23-clang++ x86_64-linux-android28-clang++
armv7a-linux-androideabi18-clang i686-linux-android24-clang x86_64-linux-android29-clang
armv7a-linux-androideabi18-clang++ i686-linux-android24-clang++ x86_64-linux-android29-clang++
armv7a-linux-androideabi19-clang i686-linux-android26-clang yasm
armv7a-linux-androideabi19-clang++ i686-linux-android26-clang++
こんな感じでいっぱい入ってます。
たとえば、C言語のコンパイルは、APIレベル21以降を対象にする場合、
- armeabi-v7a なら
armv7a-linux-androideabi21-clang
- arm64-v8a なら
aarch64-linux-android21-clang
を使う、といった具合です。
環境変数を設定
OpenSSL の ./Configure
が取り込む環境変数を、動作環境別に設定します。
とりあえず NDK の解凍場所とツールチェインの場所を設定して後の作業をラクにします。
$ export ANDROID_NDK_HOME=`pwd`/android-ndk-r20
$ env | grep ANDROID_NDK_HOME
ANDROID_NDK_HOME=/tmp/OpenSSL/android-ndk-r20
$ export NDK_TOOLCHAIN_ROOT=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64
$ env | grep NDK_TOOLCHAIN_ROOT
NDK_TOOLCHAIN_ROOT=/tmp/OpenSSL/android-ndk-r20/toolchains/llvm/prebuilt/darwin-x86_64
ビルドされたブツの格納先もここで設定しておきます。
$ export OUTPUT_DIR=`pwd`/output/openssl-1.0.1j
armeabi-v7a 向け
ANDROID_API
の値は適宜、サポートする最低バージョンに置き換えてください。
$ export ARCH=android-armv7
$ export ANDROID_API=16
$ export CC=${NDK_TOOLCHAIN_ROOT}/bin/armv7a-linux-androideabi${ANDROID_API}-clang
$ export CXX=${NDK_TOOLCHAIN_ROOT}/bin/armv7a-linux-androideabi${ANDROID_API}-clang++
$ export LD=${NDK_TOOLCHAIN_ROOT}/bin/arm-linux-androideabi-ld
$ export AR=${NDK_TOOLCHAIN_ROOT}/bin/arm-linux-androideabi-ar
$ export RANLIB=${NDK_TOOLCHAIN_ROOT}/bin/arm-linux-androideabi-ranlib
$ export STRIP=${NDK_TOOLCHAIN_ROOT}/bin/arm-linux-androideabi-strip
$ export CFLAGS="-mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -mfpu=neon -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing"
$ export LDFLAGS="-Wl,--fix-cortex-a8"
arm64-v8a 向け
2019年8月1日以降は、64ビット対応していないと Play Store に登録できないので仕方ないですね。
Android の OS側 の64ビット対応は API 21 (Lolipop) かららしいです。
$ export ARCH=android
$ export ANDROID_API=21
$ export CC=${NDK_TOOLCHAIN_ROOT}/bin/aarch64-linux-android${ANDROID_API}-clang
$ export CXX=${NDK_TOOLCHAIN_ROOT}/bin/aarch64-linux-android${ANDROID_API}-clang++
$ export LD=${NDK_TOOLCHAIN_ROOT}/bin/aarch64-linux-android-ld
$ export AR=${NDK_TOOLCHAIN_ROOT}/bin/aarch64-linux-android-ar
$ export RANLIB=${NDK_TOOLCHAIN_ROOT}/bin/aarch64-linux-android-ranlib
$ export STRIP=${NDK_TOOLCHAIN_ROOT}/bin/aarch64-linux-android-strip
$ export CFLAGS="shared no-ssl2 no-ssl3 no-hw -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing"
$ export LDFLAGS=""
x86 向け
エミュレータで動かせると便利なときもあるので。
$ export ARCH=android-x86
$ export ANDROID_API=16
$ export CC=${NDK_TOOLCHAIN_ROOT}/bin/i686-linux-android${ANDROID_API}-clang
$ export CXX=${NDK_TOOLCHAIN_ROOT}/bin/i686-linux-android${ANDROID_API}-clang++
$ export LD=${NDK_TOOLCHAIN_ROOT}/bin/i686-linux-android-ld
$ export AR=${NDK_TOOLCHAIN_ROOT}/bin/i686-linux-android-ar
$ export RANLIB=${NDK_TOOLCHAIN_ROOT}/bin/i686-linux-android-ranlib
$ export STRIP=${NDK_TOOLCHAIN_ROOT}/bin/i686-linux-android-strip
$ export CFLAGS="-mtune=intel -msse3 -mfpmath=sse -m32 -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing"
$ export LDFLAGS=""
Configure 実行
CFLAGS
環境変数は自動で取り込まれないんで、引数として渡してやります。
Configure Options:
https://wiki.openssl.org/index.php/Compilation_and_Installation#Configure_Options
$ cd openssl-1.0.1j/
$ ./Configure \
${ARCH} \
${CFLAGS} \
--prefix=${OUTPUT_DIR} \
--with-zlib-include=${NDK_TOOLCHAIN_ROOT}/sysroot/usr/include \
--with-zlib-lib=${NDK_TOOLCHAIN_ROOT}/sysroot/usr/lib \
zlib no-asm no-shared no-unit-test
Configuring for android-armv7
(中略)
Configured for android-armv7.
で、Makefile が出来上がるわけですが、
その出来上がった Makefile に -mandroid
というコンパイルオプションが指定されています。
このオプションは clang には存在せず、コンパイル時にエラーになるので除去します。
$ sed -i -e s/\-mandroid//g Makefile
$ diff Makefile-e Makefile
63c63
< CFLAG= -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -mfpu=neon -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -march=armv7-a -mandroid -I$(ANDROID_DEV)/include -B$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall
---
> CFLAG= -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -mfpu=neon -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -march=armv7-a -I$(ANDROID_DEV)/include -B$(ANDROID_DEV)/lib -O3 -fomit-frame-pointer -Wall
ビルド
やっとビルドです。
$ make
(中略)
making all in tools...
make[1]: Nothing to be done for `all'.
$ make install_sw
created directory `/tmp/OpenSSL/output'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/bin'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/lib'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/lib/engines'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/lib/pkgconfig'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/include'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/include/openssl'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/ssl'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/ssl/misc'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/ssl/certs'
created directory `/tmp/OpenSSL/output/openssl-1.0.1j/ssl/private'
making install in crypto...
making install in crypto/objects...
making install in crypto/md4...
making install in crypto/md5...
making install in crypto/sha...
making install in crypto/mdc2...
making install in crypto/hmac...
making install in crypto/ripemd...
making install in crypto/whrlpool...
making install in crypto/des...
making install in crypto/aes...
making install in crypto/rc2...
making install in crypto/rc4...
making install in crypto/idea...
making install in crypto/bf...
making install in crypto/cast...
making install in crypto/camellia...
making install in crypto/seed...
making install in crypto/modes...
making install in crypto/bn...
making install in crypto/ec...
making install in crypto/rsa...
making install in crypto/dsa...
making install in crypto/ecdsa...
making install in crypto/dh...
making install in crypto/ecdh...
making install in crypto/dso...
making install in crypto/engine...
making install in crypto/buffer...
making install in crypto/bio...
making install in crypto/stack...
making install in crypto/lhash...
making install in crypto/rand...
making install in crypto/err...
making install in crypto/evp...
making install in crypto/asn1...
making install in crypto/pem...
making install in crypto/x509...
making install in crypto/x509v3...
making install in crypto/conf...
making install in crypto/txt_db...
making install in crypto/pkcs7...
making install in crypto/pkcs12...
making install in crypto/comp...
making install in crypto/ocsp...
making install in crypto/ui...
making install in crypto/krb5...
making install in crypto/cms...
making install in crypto/pqueue...
making install in crypto/ts...
making install in crypto/srp...
making install in crypto/cmac...
making install in ssl...
making install in engines...
making install in engines/ccgost...
[ -n "/tmp/OpenSSL/output/openssl-1.0.1j" ] # should be set by top Makefile...
if [ -n "" ]; then \
set -e; \
echo installing gost; \
pfx=lib; \
if [ "android-armv7" != "Cygwin" ]; then \
case "-I../../include -DZLIB -DOPENSSL_THREADS -D_REENTRANT -DDSO_DLFCN -DHAVE_DLFCN_H -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -mfpu=neon -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -march=armv7-a -I/include -B/lib -O3 -fomit-frame-pointer -Wall" in \
*DSO_BEOS*) sfx=".so";; \
*DSO_DLFCN*) sfx=`expr ".so.1.0.0" : '.*\(\.[a-z][a-z]*\)' \| ".so"`;; \
*DSO_DL*) sfx=".sl";; \
*DSO_WIN32*) sfx="eay32.dll"; pfx=;; \
*) sfx=".bad";; \
esac; \
cp ${pfx}gost$sfx /tmp/OpenSSL/output/openssl-1.0.1j/lib/engines/${pfx}gost$sfx.new; \
else \
sfx=".so"; \
cp cyggost.dll /tmp/OpenSSL/output/openssl-1.0.1j/lib/engines/${pfx}gost$sfx.new; \
fi; \
chmod 555 /tmp/OpenSSL/output/openssl-1.0.1j/lib/engines/${pfx}gost$sfx.new; \
mv -f /tmp/OpenSSL/output/openssl-1.0.1j/lib/engines/${pfx}gost$sfx.new /tmp/OpenSSL/output/openssl-1.0.1j/lib/engines/${pfx}gost$sfx; \
fi
making install in apps...
installing openssl
installing CA.sh
installing CA.pl
installing tsget
making install in test...
make[1]: Nothing to be done for `install'.
making install in tools...
installing libcrypto.a
installing libssl.a
cp libcrypto.pc /tmp/OpenSSL/output/openssl-1.0.1j/lib/pkgconfig
chmod 644 /tmp/OpenSSL/output/openssl-1.0.1j/lib/pkgconfig/libcrypto.pc
cp libssl.pc /tmp/OpenSSL/output/openssl-1.0.1j/lib/pkgconfig
chmod 644 /tmp/OpenSSL/output/openssl-1.0.1j/lib/pkgconfig/libssl.pc
cp openssl.pc /tmp/OpenSSL/output/openssl-1.0.1j/lib/pkgconfig
chmod 644 /tmp/OpenSSL/output/openssl-1.0.1j/lib/pkgconfig/openssl.pc
さて、終わりました。
ライブラリファイルは出来ているでしょうか...
$ ls -1 ../output/openssl-1.0.1j/lib/
engines/
libcrypto.a
libssl.a
pkgconfig/
できてるゥ
念のため、中身を確認してみます。
$ ${NDK_TOOLCHAIN_ROOT}/bin/arm-linux-androideabi-readelf -h ../output/openssl-1.0.1j/lib/libssl.a | grep Machine | uniq
Machine: ARM
ちゃんと ARM
になってますね。
arm64-v8a でビルドした場合も確認してみます。
$ ${NDK_TOOLCHAIN_ROOT}/bin/aarch64-linux-android-readelf -h ../output/openssl-1.0.1j/lib/libssl.a | grep Machine | uniq
Machine: AArch64
これも AArch64 になっている!
ヘッダファイルも OUTPUT_DIR に出力されているので、
ライブラリを使う側で include するようにしましょう。
まとめ
各種コンパイルオプションの効能がいまだによくわかりませんが、
とりあえず動いているからヨシ!
これを取っ掛かりにして、最新バージョンのビルドを試みる所存。
こちらを参考にしました
https://github.com/cocochpie/android-openssl
https://github.com/leenjewel/openssl_for_ios_and_android