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

4

8

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) -->
Nixie answered 21/9, 2012 at 5:20 Comment(2)
Could you let me know how you "manually call ANativeActivity_onCreate"? Did you do that in C code?Chancroid
How do you remove the version numbers from the soname? Did you have to rebuild all your dependencies removing the version suffixes? (I realize this is really old, but I'm trying to do the same thing and am wondering about this).Barncard
N
13

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);
}
Nixie answered 22/9, 2012 at 14:53 Comment(2)
Thanks da_petcu21. Loading libraries in the JVM with native code it's a precious contribution ;) ... +1Redness
Are you saying that the only way to make a native activity load .so libraries is to make a second native activity load the libraries manually and launch the first native activity? If not, could you elaborate on how your solution works? Been working on this issue for quite some time now without very little luck.Burkley
R
5

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.

Redness answered 21/9, 2012 at 12:29 Comment(1)
yes, that was the problem, but I managed to solve it without Java. I'm gonna post my solutionNixie
A
1

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.

Attlee answered 15/11, 2017 at 20:15 Comment(0)
A
0

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

Alumnus answered 21/9, 2012 at 9:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.