AWS IoT Device SDK for embedded C or C++をARM向けにクロスコンパイルする

# はじめに
AWS IoT SDKをデバイス向けにホスト環境からクロスコンパイルして利用するというようなユースケースが結構あるかと思いますが、あまり試してみたような記事がなかったため、試してみたいと思います

# AWS IoT Device SDK for embedded CをARM向けにクロスコンパイル

今回はホスト環境としてAWSのCloud9を使用します。Cloud9ではUbuntu 18.04 LTSを使用しました。

```
$ lsb_release -a                                                                                                    
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.6 LTS
Release:        18.04
Codename:       bionic
```

まず、適当なディレクトリを作り、[本家のGithub](https://github.com/aws/aws-iot-device-sdk-embedded-C)からSDKをCloneします。Git submoduleを使っているようなので、Clone時に```--recurse-submodules```のオプションを付けます。

```
$ git clone --recurse-submodules https://github.com/aws/aws-iot-device-sdk-embedded-C.git
```
### X86_64環境でのビルド
まずは普通にビルドしてみます。ビルドに必要となるcmakeやgccなどをインストールします。

```
$ sudo apt install build-essential cmake libssl-dev
```
あとはGithubのREADMEに従ってビルドします。

```
$ cmake -S . -Bbuild -DBUILD_DEMOS=0 -DBUILD_TESTS=0
cd build/
sudo make install
```
ビルドが成功し、インストールが成功すると、projectディテクトリの下にSDKのライブラリが作成されます。fileコマンドで確認すると、X86-64のShared Libraryができていることが確認できます。

```
$ file lib/libaws_iot_mqtt.so 
lib/libaws_iot_mqtt.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=2e36b1b47f4ac1811fd8f73bdaace7673d4ed2ad, not stripped
```

### ARM向けクロスコンパイル
次にARM向けのクロスコンパイルを行います。ARM向けのToolchainをインストールします。

```
$ sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
$ sudo apt-get install gcc-arm-linux-gnueabi g++-arm-linux-gnueabi

```
次のARM向けのツールチェーンファイルを作成します。```arm-toolchain.cmake``` という名前のファイルを作成し、以下のように記述します。

```
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabi-gcc)
```
ビルド用のディレクトリを作成し、ツールチェーンファイルを指定してcmakeコマンドを実行します。

```
$ mkdir cross-build
$ cd cross-build/
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=../arm-toolchain.cmake -DBUILD_DEMOS=0 -DBULD_TESTS=0
```

ビルドを実行します。

```
$ sudo make install
[  7%] Built target aws_iot_ota
[  8%] Built target aws_iot_json
[  9%] Built target aws_iot_ota_http
[ 10%] Built target aws_iot_jobs
==== 中略 ======
[ 83%] Building C object CMakeFiles/ota_posix.dir/platform/posix/ota_pal/source/ota_pal_posix.c.o
In file included from /home/ubuntu/environment/SDKs/aws-iot-device-sdk-embedded-C/platform/posix/ota_pal/source/ota_pal_posix.c:38:0:
/usr/include/openssl/evp.h:13:11: fatal error: openssl/opensslconf.h: No such file or directory
 # include <openssl/opensslconf.h>
           ^~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
```
opensslのヘッダが見つからないというエラーになってしまいました。OpenSSLをgitからダウンロードしてARM用にコンパイルしてインストールします。

```
git clone https://github.com/openssl/openssl
git checkout OpenSSL_1_0_2       
export CROSS=arm-linux-gnueabi
export AR=${CROSS}-ar
export AS=${CROSS}-as
export CC=${CROSS}-gcc
export CXX=${CROSS}-g++
export LD=${CROSS}-ld

./Configure --prefix=/usr/${CROSS} os/compiler:${CC}
make
sudo make install
````
これで、```/usr/arm-linux-gnueabi/include/openssl/opensslconf.h``` にヘッダが格納されました。
再度ビルド実行。

```
$ cd cross-build
$ sudo make install
...
[ 95%] Linking C shared library ../../../lib/libopenssl_posix.so
/usr/lib/x86_64-linux-gnu/libssl.so: file not recognized: File format not recognized
collect2: error: ld returned 1 exit status
platform/posix/transport/CMakeFiles/openssl_posix.dir/build.make:97: recipe for target 'lib/libopenssl_posix.so' failed
make[2]: *** [lib/libopenssl_posix.so] Error 1
CMakeFiles/Makefile2:762: recipe for target 'platform/posix/transport/CMakeFiles/openssl_posix.dir/all' failed
make[1]: *** [platform/posix/transport/CMakeFiles/openssl_posix.dir/all] Error 2
Makefile:129: recipe for target 'all' failed
make: *** [all] Error 2
```
ARM用のlibssl.soがないようです。確認すると確かに、libssl.aしかありません。Shared libraryを作成するために以下のようにビルドします。

```
$ ./Configure linux-generic32 shared -DL_ENDIAN --prefix=/usr/${CROSS}
$ make
$ sudo make install
```
libssl.soがあることを確認します。

```
$ sudo find / -name libssl.so
/usr/lib/x86_64-linux-gnu/libssl.so
/usr/arm-linux-gnueabi/lib/libssl.so
```
再度、cross-buildのディレクトリに戻って、ビルド

```
$ cd aws-iot-device-sdk-embedded-C/cross-build
$ sudo make install
...
-- Up-to-date: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/include
-- Up-to-date: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/include/ota_os_posix.h
-- Up-to-date: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/include/ota_os_freertos.h
-- Installing: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libclock_posix.so
-- Installing: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libopenssl_posix.so
-- Set runtime path of "/home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libopenssl_posix.so" to ""
-- Installing: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libplaintext_posix.so
-- Set runtime path of "/home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libplaintext_posix.so" to ""
-- Installing: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libsockets_posix.so
-- Installing: /home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libtransport_mbedtls_pkcs11_posix.so
-- Set runtime path of "/home/ubuntu/SDKs/aws-iot-device-sdk-embedded-C/project/lib/libtransport_mbedtls_pkcs11_posix.so" to ""
```
今度はうまく行ったようです。fileコマンドで確認すると、ARM版のShared Libraryができていることが分かります。

```
$ file lib/libaws_iot_mqtt.so
lib/libaws_iot_mqtt.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=e817fb93764bd61324572026cdeae0367bcc3af0, not stripped
```


# aws-iot-device-sdk-cpp-v2をARM向けにクロスコンパイル

次に、[C++向けのAWS IoT Device SDK](https://github.com/aws/aws-iot-device-sdk-cpp-v2)をARM向けにクロスコンパイルしてみます。まず、適当なディレクトリにCloneします。

```
$ git clone --recursive https://github.com/aws/aws-iot-device-sdk-cpp-v2.git
```

Readmeに記載の方法で通常のビルドを行います。

```
$ mkdir build
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/home/ubuntu/SDKs -DCMAKE_BUILD_TYPE=Debug ../aws-iot-device-sdk-cpp-v2
$ cmake --build . --target install
.
```
INSTALL_PREFIXで指定した場所にライブラリが作成されます。Shared Libraryを作成したい場合には ```-DSHARED_LIBS```を指定します。

```
$ cmake -DCMAKE_INSTALL_PREFIX=/home/ubuntu/SDKs -DCMAKE_BUILD_TYPE=Debug ../aws-iot-device-sdk-cpp-v2 -DSHARED_LIBS=ON
$ cmake --build . --target install
```
x86_64のライブラリが作成されます。

```
$ file lib/libIotJobs-cpp.so
lib/libIotJobs-cpp.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=eebf46c941b1c5d594dc5e4b3bf4d4ffb26fe243, with debug_info, not stripped
```

次にARMむけのクロスコンパイルを行います。以下のようにarm-toolchain.cmakeファイルを用意します。

```
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabi-gcc)
SET(CMAKE_SYSTEM_PROCESSOR arm)
```

cross-buildディレクトリを作成し、以下のようにビルドします。

```
$ mkdir cross-build
$ cd cross-build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=../arm-toolchain.cmake -DCMAKE_INSTALL_PREFIX=/home/ubuntu/SDKs/cross-build -DCMAKE_BUILD_TYPE=Debug ../aws-iot-device-sdk-cpp-v2
$ cmake --build . --target install
...
[ 33%] Building C object crt/aws-crt-cpp/crt/aws-lc/crypto/CMakeFiles/crypto.dir/x509v3/v3_utl.c.o
[ 33%] Building ASM object crt/aws-crt-cpp/crt/aws-lc/crypto/CMakeFiles/crypto.dir/chacha/chacha-armv4.S.o
[ 33%] Building ASM object crt/aws-crt-cpp/crt/aws-lc/crypto/CMakeFiles/crypto.dir/curve25519/asm/x25519-asm-arm.S.o
/home/ubuntu/SDKs/aws-iot-device-sdk-cpp-v2/crt/aws-crt-cpp/crt/aws-lc/crypto/curve25519/asm/x25519-asm-arm.S: Assembler messages:
/home/ubuntu/SDKs/aws-iot-device-sdk-cpp-v2/crt/aws-crt-cpp/crt/aws-lc/crypto/curve25519/asm/x25519-asm-arm.S:44: Error: selected processor does not support `strd r4,[sp,#0]' in ARM mode
```
x25519-asm-arm.Sでエラーとなってしまいます。[こちら](https://github.com/briansmith/ring/issues/516)によるとx25519-asm-arm.SはNEONでのコードしか含まれていないということなので、aws-iot-device-sdk-cpp-v2/crt/aws-crt-cpp/crt/aws-lc/crypto/CMakeLists.txt から外し、再度ビルド。

```
[ 85%] Building CXX object crt/aws-crt-cpp/crt/aws-lc/tool/CMakeFiles/bssl.dir/sign.cc.o
[ 85%] Building CXX object crt/aws-crt-cpp/crt/aws-lc/tool/CMakeFiles/bssl.dir/speed.cc.o
[ 86%] Building CXX object crt/aws-crt-cpp/crt/aws-lc/tool/CMakeFiles/bssl.dir/tool.cc.o
[ 86%] Building CXX object crt/aws-crt-cpp/crt/aws-lc/tool/CMakeFiles/bssl.dir/transport_common.cc.o
[ 86%] Linking CXX executable bssl
../crypto/libcrypto.a(curve25519.c.o): In function `x25519_scalar_mult':
/home/ubuntu/SDKs/aws-iot-device-sdk-cpp-v2/crt/aws-crt-cpp/crt/aws-lc/crypto/curve25519/curve25519.c:2092: undefined reference to `x25519_NEON'
../crypto/libcrypto.a(curve25519.c.o): In function `X25519_public_from_private':
/home/ubuntu/SDKs/aws-iot-device-sdk-cpp-v2/crt/aws-crt-cpp/crt/aws-lc/crypto/curve25519/curve25519.c:2136: undefined reference to `x25519_NEON'
collect2: error: ld returned 1 exit status
```

x25519_NEONのシンボルが見つからないと言っているので、aws-iot-device-sdk-cpp-v2/crt/aws-crt-cpp/crt/aws-lc/crypto/curve25519/internal.h から#define BORINGSSL_X25519_NEON をはずす。

これで、-DCMAKE_INSTALL_PREFIXに指定した場所にライブラリが作成されます。Shared Libraryを作成する場合は、```-DBUILD_SHARED_LIBS```を指定します。

```
$cmake .. -DCMAKE_TOOLCHAIN_FILE=../arm-toolchain.cmake -DCMAKE_INSTALL_PREFIX=/home/ubuntu/SDKs/cross-shared-build -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON ../aws-iot-device-sdk-cpp-v2
...
-- Installing: /home/ubuntu/SDKs/cross-build/lib/libIotSecureTunneling-cpp.so
-- Set runtime path of "/home/ubuntu/SDKs/cross-build/lib/libIotSecureTunneling-cpp.so" to ""
-- Up-to-date: /home/ubuntu/SDKs/cross-build/include/aws/iotsecuretunneling/Exports.h
-- Up-to-date: /home/ubuntu/SDKs/cross-build/include/aws/iotsecuretunneling/IotSecureTunnelingClient.h
-- Up-to-date: /home/ubuntu/SDKs/cross-build/include/aws/iotsecuretunneling/SecureTunnel.h
-- Up-to-date: /home/ubuntu/SDKs/cross-build/include/aws/iotsecuretunneling/SecureTunnelingNotifyResponse.h
-- Up-to-date: /home/ubuntu/SDKs/cross-build/include/aws/iotsecuretunneling/SubscribeToTunnelsNotifyRequest.h
-- Installing: /home/ubuntu/SDKs/cross-build/lib/IotSecureTunneling-cpp/cmake/shared/IotSecureTunneling-cpp-targets.cmake
-- Installing: /home/ubuntu/SDKs/cross-build/lib/IotSecureTunneling-cpp/cmake/shared/IotSecureTunneling-cpp-targets-debug.cmake
-- Installing: /home/ubuntu/SDKs/cross-build/lib/IotSecureTunneling-cpp/cmake/iotsecuretunneling-cpp-config.cmake

$ file lib/libaws-c-mqtt.so.1.0.0
lib/libaws-c-mqtt.so.1.0.0: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=f5d0d9a425432a1e94f68ae5da3422edc4571d9f, with debug_info, not stripped
```
ARM用の共有ライブラリが作成されていることが確認できます。

# まとめ
AWS IoT Device SDK for embedded C or C++をARM向けにクロスコンパイルしました。デバイスへの導入時に参考になれば幸いです。