OpenSSL 1.0.1j を Android 向けに mac でビルドする

:bangbang: **注意** :bangbang: 

この記事でビルドする OpenSSL は 1.0.1j という古いバージョンです！！
脆弱性を考慮して、可能であれば最新バージョンを使うべきです。

[最新バージョン](https://www.openssl.org/source/)をビルドするため公式ドキュメントは[こちら](https://wiki.openssl.org/index.php/Android)。
...のはずですが、ちっともビルドできんやんか :bangbang::anger::anger::anger:  


# はじめに

訳あって、OpenSSL の 1.0.1j という古いバージョンを
Android 向けにビルドしてスタティックライブラリ（.a）を作成する必要があり、
試行錯誤してなんとかビルドできたので記録しておきます。

まぁ、遅ればせながら、**64ビット対応** をしたということです。


# ソースコードを取得

公式サイトの [Downloads](https://www.openssl.org/source/) にある [source/old] (https://www.openssl.org/source/old/) からソースコード（tar.gz形式）をダウンロードして適当なところに展開します。

```bash
$ 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を取得

[公式サイト](https://developer.android.com/ndk)の[DOWNLOADS](https://developer.android.com/ndk/downloads)からNDK（zip形式）をダウンロードして適当なところに解凍します。

ワタシの環境は mac なので darwin-x86_64 というのをダウンロードしました。

```bash
$ 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生成

ビルド環境 = 動作環境 であれば、

```bash
$ cd openssl-1.0.1j/
$ ./config
```
とやれば、ビルド用の Makefile が生成されるというパターンが多いみたいですが、
今回の場合は動作環境が Android なわけで、Androd 上で動作するバイナリをビルドする為の Makefile を生成しなくてはなりません。

## ツールチェイン

前項で取得してきた NDK にはツールチェインというものが含まれていて、ソースコードをコンパイル/リンクするにあたっては、mac の gcc コマンドではなく、ツールチェインが提供するものを代わりを使うようです。

ツールチェインは `NDKを解凍したフォルダ/toolchains/` にあります。

```bash
$ 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を使っている](https://qiita.com/kuboshu83/items/265264551193da079ad2)ためか、llvm を使うようです。

で、使うツール類は mac なら `prebuilt/darwin-x86_64/bin` の下に

```bash
$ 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 の解凍場所とツールチェインの場所を設定して後の作業をラクにします。

```bash
$ export ANDROID_NDK_HOME=`pwd`/android-ndk-r20
$ env | grep ANDROID_NDK_HOME
ANDROID_NDK_HOME=/tmp/OpenSSL/android-ndk-r20
```
```bash
$ 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
```

ビルドされたブツの格納先もここで設定しておきます。

```bash
$ export OUTPUT_DIR=`pwd`/output/openssl-1.0.1j
```

### armeabi-v7a 向け

`ANDROID_API` の値は適宜、サポートする最低バージョンに置き換えてください。

```bash
$ 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) かららしいです。

```bash
$ 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 向け

エミュレータで動かせると便利なときもあるので。

```bash
$ 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

```bash
$ 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 には存在せず、コンパイル時にエラーになるので除去します。

```bash
$ 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
```

## ビルド

やっとビルドです。

```bash
$ make
（中略）
making all in tools...
make[1]: Nothing to be done for `all'.
```

```bash
$ 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
```
さて、終わりました。
ライブラリファイルは出来ているでしょうか...


```bash
$ ls -1 ../output/openssl-1.0.1j/lib/
engines/
libcrypto.a
libssl.a
pkgconfig/
```

できてるゥ:exclamation: 

念のため、中身を確認してみます。

```bash
$ ${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 でビルドした場合も確認してみます。

```bash
$ ${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