Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android NDK - make two native shared libraries calling each other

Wasted half a day trying to build two shared libraries, e.g. mod1 and mod2 (which Android NDK compiles to libmod1.so and libmod2.so), from sources in a jni folder and sub-folders, then have mod1 call a function from mod2. Plenty of answers on how to make the build work, but then runtime dynamic linking was not working, the app crashed on startup.

Decided to post this question and immediately answer it, so that Q and A to the whole process are together, and hopefully someone else won't waste a day researching it again.

like image 565
gregko Avatar asked Jul 16 '13 22:07

gregko


1 Answers

The correct build procedure was relatively easy, my problem was that making libmod1.so dependent on libmod2.so caused unsatisfied links upon startup - mod1 code could not find mod2 shared library, even though both were present within the same folder in the final APK, under libs/armeabi, libs/x86 etc. However, to make my answer complete:

  • Put your C or C++ sources and header files under sub-directories of jni dir in your Android project, e.g. folders mod1/ and mod2/

  • Per NDK instructions, create Application.mk file, e.g. mine is:

NDK_TOOLCHAIN_VERSION=4.7
APP_PLATFORM := android-8
APP_ABI := armeabi armeabi-v7a x86

  • Create Android.mk following this template:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := mod2    # this makes libmod1.so dependent on libmod2.so
LOCAL_MODULE := mod1
LOCAL_SRC_FILES := mod1/file1.c
LOCAL_SRC_FILES += mod1/file2.cpp
...
include $(BUILD_SHARED_LIBRARY)    # this actually builds libmod1.so

include $(CLEAR_VARS)
LOCAL_MODULE := mod2
LOCAL_SRC_FILES := mod2/file1.cc
LOCAL_SRC_FILES += mod2/file2.cc
...
include $(BUILD_SHARED_LIBRARY)    # this builds libmod2.so

That's about it, all builds without complains with ndkbuild script. You only need a C wrapper to call some functions from Java. And here was my problem. Since I had functions callable from Java only in libmod1.so, my C wrapper class in Java was like:

public class CWrapper {
    static {
        System.loadLibrary("mod1");
    }
    public static native int func1(String aParam);
    ...
}

This seemed perfectly logical to me - I call to libmod1.so from Java, so I used System.loadLibrary("mod1"), and since libmod1.so knows it depends on libmod2.so, and both files are in the same folder, libmod1 will know how to find and load libmod2, right? Wrong! It was crashing upon app startup with "unsatisfied link". The exact error message was:

java.lang.UnsatisfiedLinkError: Cannot load library: soinfo_link_image(linker.cpp:1635): could not load library "libmod2.so" needed by "libmod1.so"; caused by load_library(linker.cpp:745): library "libmod2.so" not found

I was searching everywhere for some more code to add to Android.mk to resolve this in vain. Finally Eureka! I modified my CWrapper class as follows:

public class CWrapper {
    static {
        System.loadLibrary("mod2"); // must be first, as mod1 depends on mod2!
        System.loadLibrary("mod1");
    }
    public static native int func1(String aParam);
    ...
}

and things started working like a charm...

Greg

like image 55
gregko Avatar answered Nov 17 '22 19:11

gregko