2

I have a problem loading a native library using System.loadLibrary("my_shared_lib"); The problem is that this call never returns.

Here is the context :

In my project I have several static libraries built using the nkd-build script. Building them works well using this Android.mk for each .a lib I need:

**Android.mk used for static libs**

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := my_static_lib_1

# Include paths
LOCAL_C_INCLUDES := \
    $(PATH_TO_INCLUDES1) \
    $(PATH_TO_INCLUDES2)

# Sources
LOCAL_SRC_FILES := \
    my_source_1.cpp\
    my_source_11.cpp


# Target
include $(BUILD_STATIC_LIBRARY)

These static libraries contain of course only native (C++) sources, among with some JNI wrappers (callable from Java sources).

Once all static libraries (.a) are built, I want to build a shared library (.so) containing all the .a libs I need. Here is the Android.mk I'm using to build this shared library:

LOCAL_PATH := $(call my-dir)
#----------
# Static prebuilt libs 
#----------
#-- my_static_lib_1
include $(CLEAR_VARS)
LOCAL_MODULE    := my_static_lib_1
LOCAL_SRC_FILES := $(PATH_TO_LIBS)/my_static_lib_1.a
include $(PREBUILT_STATIC_LIBRARY)

#-- my_static_lib_2
include $(CLEAR_VARS)
LOCAL_MODULE    := my_static_lib_2
LOCAL_SRC_FILES := $(PATH_TO_LIBS)/my_static_lib_2.a
include $(PREBUILT_STATIC_LIBRARY)

#----------
# Building shared lib
#----------
include $(CLEAR_VARS)
LOCAL_MODULE := my_shared_lib

#-- Some of the .a static libs need these libs
LOCAL_LDLIBS := \
    -lz\
    -llog \
    $(PATH_TO_NDK_LIBS)\libstlport_static.a

#-- This variable is used to force libs to be included in the .so
LOCAL_WHOLE_STATIC_LIBRARIES := \
    my_static_lib_1\
    my_static_lib_2

include $(BUILD_SHARED_LIBRARY)

This builds me a perfect .so file, containing all the functions from the .a libs (I could check that using the nm command on the generated .so lib).

And to be complete, here is the .java file dynamically loading the .so lib:

public class MyClass
{
    static {
        Log.d("MYLOGS", "Loading...");
        System.loadLibrary("my_shared_lib");
        Log.d("MYLOGS", "Loaded.");
    }
    ...
}

Problems:

When I'm creating a new instance of MyClass, loadLibrary is called, but it never returns. I can see the Loading... log but never the Loaded. one. The logCat says Trying to load lib [...].so but that's all, the app freezes.

All of this works fine when I have only one static lib .a in my .so file. In that case, I can call my native code perfectly. But my project uses 8 .a files, and I got a freezed app because loadLibrary never ends in that case.

What is wrong with the loadLibrary call ? Do you have any idea ?

Thanks.

Vincent
  • 1,013
  • 14
  • 33
  • Could you have a static initializer or on load type of function that enters an infinite loop? What actually ends up happening when "the app never returns"? Do you get an Application Not Responding dialog? Anything in logcat indicating where the various threads are stuck? You might try replacing the real library with trivially simple ones similarly built with multiple .a files and see if you can narrow down what the problem is - is it having multiple files, or is it one of the files in particular? – Chris Stratton Feb 20 '14 at 18:13
  • You could also try delaying the library loading so that you get a chance to connect then ndk-gdb first, and then watch what happens during the load attempt with the debugger. – Chris Stratton Feb 20 '14 at 18:14
  • I don't have a dialog saying the app is not responding. I got a black screen and logcat says "Launch timeout has expired, giving up wake lock!" just after "Trying to load lib.so". (knowing that the loadLibrary is called at the launch of the app. So it seems it's not a deadlock, but loadLibrary seems to wait for something... – Vincent Feb 21 '14 at 09:56

2 Answers2

3

I've found out what was wrong in my code.

Actually when the loadLibrary loads a .so library, it creates all the global variables/constants declared in the .so, including of course every global of every .a lib contained in the .so.

Some of these global are built through constructors executing some code. I've found out that in my .a libs architecture, this code was deadlocking at some point because it was called too early (some needed stuff didn't exist yet). I didn't expect it to be called at the loadLibrary time.

So if that can help anyone: keep in mind that the loadLibrary involves creating all the global objects contained in the .so lib that you're trying to load.

Not knowing that was my mistake.

Vincent
  • 1,013
  • 14
  • 33
  • There are also **gcc** specific `__attribute__((constructor))` and `__init`. See [GCC docs](http://gcc.gnu.org/onlinedocs/gccint/Initialization.html) if you want to understand how they all can misbehave when you load a shared lib. And `JNI_OnLoad()` that will be called _after_ all constructors complete. – Alex Cohn Feb 25 '14 at 20:34
1

In general, when you build a shared library (.so), some kind of compiler options such as -fPIC are used to generate position-independent code. On the other hand, when you build a static library (.a), such an option is not used. Even if you pack static libraries into a shared library, binary code in the static libraries are not position-independent. It is the reason why loadLibrary() fails, I guess.

If I were you, I would avoid packing static libraries into a shared library and instead would compile all C++ files with -fPIC or something equivalent.

Takahiko Kawasaki
  • 18,118
  • 9
  • 62
  • 105