Search code examples
androidlinkerandroid-ndknative-activity

Can't load native shared library with dependencies in a native activity app


In my Android app I have 4 libraries:

libTemplate.so
   depends on libPorkholt.so
libPorkholt.so
   depends on libpng15.so
   depends on liblua.so
   depends on libopenal.so
libpng15.so
liblua.so
libopenal.so

If I write a small command line executable that links against libTemplate and manually call ANativeActivity_onCreate, it links and runs just fine (if I point LD_LIBRARY_PATH to /data/data/com.mycompany.Template/lib)

If I run my app I get this very useful error message:

E/AndroidRuntime(13214): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mycompany.Template/android.app.NativeActivity}: java.lang.IllegalArgumentException: Unable to load native library: /data/data/com.mycompany.Template/lib/libTemplate.so

It doesn't even enter ANativeActivity_onCreate, so my only guess is that's it has something to do with linking

I should probably mention that I'm using CMake with this script: http://code.google.com/p/android-cmake/ to build the libraries myself (without ndk-build). I managed to compile the native-activity sample with it, so I know it works.

Also, I made sure no library contains a version number in its soname

My manifest:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.mycompany.Template"
        android:versionCode="1"
        android:versionName="1.0">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="9" />

    <!-- This .apk has no Java code itself, so set hasCode to false. -->
    <application android:label="Template Porkholt project" android:hasCode="false">

        <!-- Our activity is the built-in NativeActivity framework class.
             This will take care of integrating with our NDK code. -->
        <activity android:name="android.app.NativeActivity"
                android:label="Template Porkholt project"
                android:configChanges="orientation|keyboardHidden">
            <!-- Tell NativeActivity the name of or .so -->
            <meta-data android:name="android.app.lib_name"
                    android:value="Template" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest> 
<!-- END_INCLUDE(manifest) -->

Solution

  • Since apparently Android isn't smart enough to set a LD_LIBRARY_PATH correctly, I managed to solve my problem by creating a small bootstrapper library that manually loads the actual activity. Here's the code:

    #include <android/native_activity.h>
    #include <android/log.h>
    #include <dlfcn.h>
    #include <errno.h>
    #include <stdlib.h>
    
    #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "Porkholt", __VA_ARGS__))
    #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "Porkholt", __VA_ARGS__))
    
    #define LIB_PATH "/data/data/@PH_BUNDLE_ID@/lib/"
    
    void * load_lib(const char * l)
    {
        void * handle = dlopen(l, RTLD_NOW | RTLD_GLOBAL);
        if (!handle)
        {
            LOGE("dlopen(\"%s\"): %s", l, strerror(errno));
            exit(1);
        }
        return handle;
    }
    
    void ANativeActivity_onCreate(ANativeActivity * app, void * ud, size_t udsize)
    {
        LOGI("Loaded bootstrap");
        load_lib(LIB_PATH "libpng15.so");
        load_lib(LIB_PATH "liblua.so");
        load_lib(LIB_PATH "libopenal.so");
        load_lib(LIB_PATH "libPorkholt.so");
        void (*main)(ANativeActivity*, void*, size_t) = dlsym(load_lib(LIB_PATH "lib@[email protected]"), "ANativeActivity_onCreate");
        if (!main)
        {
            LOGE("undefined symbol ANativeActivity_onCreate");
            exit(1);
        }
        main(app, ud, udsize);
    }