DRY

Web関連の技術の事、食事/お酒の事、旅行の事など

android-ndk-r6でopensslを動かす

これは本当に苦労しました。。。泣きました。。。

とりあえず改めて環境の確認ですが

# 環境
Windows7
Eclipse 3.6 Helios
Cygwin

となっています。


# 環境変数の設定


export NDK_DIR=/cygdrive/c/bin/android-ndk-r6
export ANDROID_NDK_ROOT=/cygdrive/c/bin/android-ndk-r6
export PATH=$PATH:$ANDROID_NDK_ROOT
export STLPORT_DIR=$ANDROID_NDK_ROOT/sources/cxx-stl/stlport

こんな感じで設定しています


# まずはgitにてopensslのソースの取得

NDK用のopensslはココで公開されています。


$ cd $NDK_DIR
$ git clone git://android.git.kernel.org/platform/external/openssl.git

で早速ndk-buildしてみるのですが、どんな環境変数、.mkを設定しても絶対に存在しない「jni」の下のAndroid.mkを見に行くので、もう諦めて作成します。


$ cd $NDK_DIR/source/openssl
$ mkdir jni
$ cp -a Android.mk jni
$ ndk-build NDK_LOG=1

で改めてndk-buildするのですが、さっぱりわからずWebを探し回っていたら、この記事にぶつかりました。
まあとりあえずやってみるしかないので、この通りの手順+α(後述)でコンパイルが若干進みました。


1.
# Give the NDK build system the Application.mk it needs. I couldn't
find a way to specify this manually, and it seems the jni bit is
hardcoded.
mkdir jni

と言っているので、ああやっぱり必要なんだね。みたいな




2.
echo "APP_PROJECT_PATH := $(pwd)" >> jni/Application.mk
echo "APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk" >> jni/
Application.mk


との事でApplication.mkをjniの下に作成しました。


APP_PROJECT_PATH := $(call my-dir)
APP_BUILD_SCRIPT := $(APP_PROJECT_PATH)/Android.mk



3.
# Edit crypto/Android to fix the include paths.
nano crypto/Android.mk

# Change the last bits to:
#################
LOCAL_C_INCLUDES += \
$(NDK_PROJECT_PATH) \
$(NDK_PROJECT_PATH)/include
# external/zlib

# LOCAL_SHARED_LIBRARIES += libz

#ifneq ($(TARGET_SIMULATOR),true)
# LOCAL_SHARED_LIBRARIES += libdl
#endif

LOCAL_LDLIBS += -lz
# LOCAL_LDLIBS += -ldl
#################

Then it actually starts successfully compiling things, but gets stuck
at some assembly stuff.


と言っております。
確かにさっきndk-buildした際に、多くのヘッダーファイルがNo such or directoryだったので
修正が必要なんだな。
「external/」とか今の状況でみれば意味がわからないし。。。

ただ、LOCAL_C_INCLUDESに関しては上記だと不足だと思います。
なので、私は付け足しをしました。
修正した「crypt/Android.mk」を載せます。
変更箇所を赤でハイライトします。


LOCAL_PATH:= $(call my-dir)

arm_cflags := -DOPENSSL_BN_ASM_MONT -DAES_ASM -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM
arm_src_files := \
    aes/asm/aes-armv4.S \
    bn/asm/armv4-mont.S \
    sha/asm/sha1-armv4-large.S \
    sha/asm/sha256-armv4.S \
    sha/asm/sha512-armv4.S

non_arm_src_files := aes/aes_core.c

local_src_files := \
cryptlib.c \
<-- ここは変更なし -->

local_c_includes := \
external/openssl \
external/openssl/crypto/asn1 \
external/openssl/crypto/evp \
external/openssl/include \
external/openssl/include/openssl \
external/zlib

local_c_flags := -DNO_WINDOWS_BRAINDEATH

#######################################

# target
include $(CLEAR_VARS)
include $(LOCAL_PATH)/../android-config.mk
LOCAL_SRC_FILES += $(local_src_files)
LOCAL_CFLAGS += $(local_c_flags)
#LOCAL_C_INCLUDES += $(local_c_includes)
LOCAL_C_INCLUDES +=  $(NDK_PROJECT_PATH) \
                     $(NDK_PROJECT_PATH)/include \
                     $(NDK_PROJECT_PATH)/include/openssl \
                     $(NDK_PROJECT_PATH)/crypto \
                     $(NDK_PROJECT_PATH)/crypto/asn1 \
                     $(NDK_PROJECT_PATH)/crypto/evp
#LOCAL_SHARED_LIBRARIES += libz
ifeq ($(TARGET_ARCH),arm)
LOCAL_SRC_FILES += $(arm_src_files)
LOCAL_CFLAGS += $(arm_cflags)
else
LOCAL_SRC_FILES += $(non_arm_src_files)
endif
#ifeq ($(TARGET_SIMULATOR),true)
# # Make valgrind happy.
# LOCAL_CFLAGS += -DPURIFY
#    LOCAL_LDLIBS += -ldl
#else
# LOCAL_SHARED_LIBRARIES += libdl
#endif
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= libcrypto
include $(BUILD_SHARED_LIBRARY)

#######################################
# host shared library
ifeq ($(WITH_HOST_DALVIK),true)
    include $(CLEAR_VARS)
    include $(LOCAL_PATH)/../android-config.mk
    LOCAL_SRC_FILES += $(local_src_files)
    LOCAL_CFLAGS += $(local_c_flags) -DPURIFY
    LOCAL_C_INCLUDES += $(local_c_includes)
    LOCAL_SRC_FILES += $(non_arm_src_files)
    LOCAL_STATIC_LIBRARIES += libz
    #LOCAL_LDLIBS += -ldl
    LOCAL_LDLIBS += -lz
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE:= libcrypto
    include $(BUILD_HOST_SHARED_LIBRARY)
endif

########################################
# host static library, which is used by some SDK tools.

include $(CLEAR_VARS)
include $(LOCAL_PATH)/../android-config.mk
LOCAL_SRC_FILES += $(local_src_files)
LOCAL_CFLAGS += $(local_c_flags) -DPURIFY
LOCAL_C_INCLUDES += $(local_c_includes)
LOCAL_SRC_FILES += $(non_arm_src_files)
LOCAL_STATIC_LIBRARIES += libz
#LOCAL_LDLIBS += -ldl
LOCAL_LDLIBS += -lz
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= libcrypto_static
#include $(BUILD_HOST_STATIC_LIBRARY)
include $(BUILD_STATIC_LIBRARY)

それで、ここからはndk-build NDK_LOG=1との戦いなのですが、初めて全うにコンパイルが始まりました。


ですが暫くすると下記のエラーが


dso_dlfcn.c:445: error: 'Dl_info' undeclared (first use in this function)

これはもうわからないので、こちらのエントリを勝手に信じて(笑)関数を以下のようにすべてコメントアウトしました。


static int dlfcn_pathbyaddr(void *addr,char *path,int sz)
{
/*
#ifdef HAVE_DLINFO
Dl_info dli;
int len;

if (addr == NULL)
{
union { int(*f)(void*,char*,int); void *p; } t =
{ dlfcn_pathbyaddr };
addr = t.p;
}

if (dladdr(addr,&dli))
{
len = (int)strlen(dli.dli_fname);
if (sz <= 0) return len+1;
if (len >= sz) len=sz-1;
memcpy(path,dli.dli_fname,len);
path[len++]=0;
return len;
}

ERR_add_error_data(4, "dlfcn_pathbyaddr(): ", dlerror());
#endif
*/
return -1;
}


次に出たエラーは


make: execvp: /cygdrive/c/bin/android-ndk-r6/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/bin/arm-linux-androideabi-g++: Argument list too long

このサイトによると


1/ Try installing the NDK and your project in top-level directories to make
the final build commands smaller. What I mean would be:

  • install the ndk as /cygdrive/c/ndkr5/
  • put your project under /cydrive/c/myproj/

2/ Try generating static libraries that contain fewer object files. I.e.
instead of generating libfoo.a, try to make libfoo1.a + libfoo2.a +
libfoo3.a, where each one only contains a third of the object files. Not
ideal, but would help to reduce the size of the static archive command.


との事なので、とりあえず$NDK_DIRを"/cygdrive/c/ndk"に変更して、環境変数も変更しました


#export NDK_DIR=/cygdrive/c/bin/android-ndk-r6
export NDK_DIR=/cygdrive/c/ndk
#export ANDROID_NDK_ROOT=/cygdrive/c/bin/android-ndk-r6
export ANDROID_NDK_ROOT=/cygdrive/c/ndk


で改めて、「 /cygdrive/c/ndk/sources/openssl」でndk-buile NDK_LOG=1します。
いよいよ行けるか!と思ったのですがやっぱりまたエラー。。。


SharedLibrary : libcrypto.so
arm-linux-androideabi-g++.exe: CreateProcess: No such file or directory

で、別にSharedLibraryじゃなくて良いので(むしろstaticの方が良いかも)なので
※どうしても共有ライブラリにしたい方、すみません。

crypto/Android.mkの513行目あたりを「include $(BUILD_STATIC_LIBRARY)」に書き換えて再度ndk-buile NDK_LOG=1すると


StaticLibrary : libcrypto.a
の記載が!!!

ただし、libssl.soでまた落ちましたがこれは一旦無視で
(とりあえず、ssl/Android.mkを「include $(BUILD_STATIC_LIBRARY)」に書き換えておきました)



$ cd /cygdrive/c/ndk/sources/openssl/obj/local/armeabi
で見てみると、libcrypto.aがきちんと生成されていました。


それで、コイツを自分のプロジェクトに組み込みたいので


/cygdrive/d/Develop/Eclipse//jni/libs/
の下にコピーします
※私の自作のEclipse Workspaceは「/cygdrive/d/Develop/Eclipse/」以下になっています。


# openssl以下のフォルダをコピーします。
includeファイルが当然必要なので、コピーしますがとりあえずメンドクサイから全部「/cygdrive/d/Develop/Eclipse//jni」以下にコピーします。


# 自分の該当プロジェクトのAndroid.mkを変更します。


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := モジュール名
LOCAL_SRC_FILES := ファイル名
LOCAL_LDLIBS := -llog -L$(LOCAL_PATH)/libs -ldebug
LOCAL_C_INCLUDES += $(LOCAL_PATH)/openssl \
$(LOCAL_PATH)/openssl/include \
$(LOCAL_PATH)/openssl/include/openssl \
$(LOCAL_PATH)/openssl/crypto \
$(LOCAL_PATH)/openssl/crypto/asn1 \
$(LOCAL_PATH)/openssl/crypto/evp
LOCAL_CFLAGS += $(LOCAL_C_INCLUDES:%=-I%)

include $(BUILD_SHARED_LIBRARY)


でJNI側で適当に「#include "aes.h"」とかして関数を呼んで、ndk-buildでOK!
長かった。。。



■2011/08/01 追記
関数を呼んで「C:/ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows/bin/../lib/gcc/arm-linux-androideabi/4.4.3/../../../../arm-linux-androideabi/bin/ld.exe: cannot find -l./libcrypto: ld returned 1 exit statusmake: *** [/cygdrive/d/Develop/Eclipse//obj/local/armeabi/xxxxx.so] Error 1

とかエラーが出たら、LOCAL_LDLIBSの設定を以下のように変えてみて下さい。
※ミソはhost-pathみたいです。

LOCAL_LDLIBS := -ldl -llog $(call host-path, $(LOCAL_PATH)/libs/libcrypto.a)


ちなみにディレクトリ構成はこんな感じです

[\\D:\Develop\Eclipse]
└ MyProject(+)
  └ jni(+)
    ├ openssl # includeファイルなど
    ├ Application.mk
    ├ Android.mk
    ├ xxxx.h
    ├ xxxx.cpp
    └ libs
      └ libcrypto.a
  └ res(+)
  └ src(+)
  └ AndroidManifest.xml
(など。他のDir/Fileは省略)



多分正しくは「LOCAL_STATIC_LIBRARIES」なんでしょうけど、良くわからない&「LOCAL_LDLIBS」で出来るっぽいので(これ導きだすのに6時間も掛かりました。。。)とりあえず良しとしました。