Home » Android » android – Can't load native shared library with dependencies in a native activity app

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

Posted by: admin June 15, 2020 Leave a comment

Questions:

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) -->
How to&Answers:

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/@[email protected]/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 "[email protected][email protected]"), "ANativeActivity_onCreate");
    if (!main)
    {
        LOGE("undefined symbol ANativeActivity_onCreate");
        exit(1);
    }
    main(app, ud, udsize);
}

Answer:

I don’t think Android will automatically load libraries other than the ones specified in the manifest, so you should create a “dummy” Java class to load external dependencies, it should contain:

static {
    System.loadLibrary("openal");
    System.loadLibrary("lua");
    System.loadLibrary("png15");
    System.loadLibrary("Porkholt");
    System.loadLibrary("Template");
}

Since this section is static, it will be executed when the class is loaded, even if its methods aren’t called.

Answer:

This no longer affects API 24+ (see the framework fix here). However, if you need to support older versions, extend NativeActivity, refer to your extension in the manifest file instead, and add the static block mentioned in Sdra’s answer as workaround.

Answer:

Your Activity probably has a static constructor that calls System.Load(“libTemplate.so”). It should load the other libraries according to the dependency order.