Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.UnsatisfiedLinkError: dlopen failed: library not found

I am trying to build my Android (native) project using cmake (migrating it from gradle experimental plugin where it used to build and run fine).

I have some native code(will call it 'a') which uses another external prebuilt library code (will call it 'b') and I linked the two like this: (according to https://developer.android.com/studio/projects/configure-cmake)

cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -frtti -fno-common -fexceptions")

include_directories(
#a's include files' paths
#b's include files' paths)

file(GLOB_RECURSE A_SOURCES
#a's source files' paths)

add_library(a_lib SHARED ${A_SOURCES})
add_library(b_lib SHARED IMPORTED)
set_target_properties(b_lib PROPERTIES IMPORTED_LOCATION "b's .so path")
target_link_libraries(a_lib b_lib)

I got past the compilation and linking steps and the android studio goes ahead to install the APK on my device. However, after launch, the app freezes with the following in the logcat:

E/ExceptionHandler: Uncaught Exception java.lang.UnsatisfiedLinkError: dlopen failed: library "libb_lib.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:989)
at java.lang.System.loadLibrary(System.java:1530)
at com.mm.projectname.model.libloadingclassname.<clinit>(libloadingclassname.java:99)

I know this is happening because b_lib's .so is not there in the apk. And I can see b's symbols along with a's symbols in a's shared library.

So my question is how can I

  1. either package my prebuilt b's .so in the apk
  2. or prevent the system from looking for b.so in the libs folder and make it look for b's symbols in a's .so only.

I searched a lot for similar posts and questions (eg one and two ) but i cant get anything to work. I am really looking for the right way to do this - which wont create problems in the future (like changing the targetSDKversion). I also tried building the prebuilt lib with the latest ndk version.

It's possible that I am doing a very small mistake, and would really appreciate if someone could point it out.

Thanks in advance

like image 366
smitt Avatar asked Aug 29 '18 11:08

smitt


2 Answers

  1. Create a jniLibs folder in your app's main directory (e.g.: /app/src/main).
  2. Make another folder named according to the architecture.
  3. Store your .so file in this folder.
like image 124
PEEHU RAJ Avatar answered Nov 20 '22 09:11

PEEHU RAJ


A little late, but someone else might be interested (or even the op).

I had the same situation as the op and I tried many different things. One of those was to try and compile the project with an earlier version of NDK (I think r14b, but I am not enitrely sure). I got a different error and managed to track down the issue which is well described here, in sections "Invalid DT_NEEDED Entries" and "Missing SONAME". Additionally, the problem is described in this particular question and has received an adequate answer.

If you cannot recompile the shared library you are using in order to include the SONAME, like in my case, you can do the following thing I did and managed to work:

  • Include the library in jniLibs folder in your project tree, so it is packed in your APK but do not link against it.
  • Track down the symbols of the functions you want to incorporate using any method described here.
  • Create function pointers of the corresponding functions in your C++ code.
  • Load the shared library at runtime and map to those functions.

Example Code

Header:

private:

typedef uint32_t (*InitX_t)();
typedef uint32_t (*DoX_t)();
typedef uint32_t (*GetX)(uint32_t, char*);

InitX_t InitX;
DoX_t DoX;
GetX_t GetX;

CPP

void *handle = dlopen("libMyLib.so", RTLD_NOW);

if(handle == nullptr)
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Could not load library");
}
else
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Library loaded");
}

InitX = (InitX_t)dlsym(handle, "InitX_SYMBOL");
ScanX = (ScanX_t)dlsym(handle, "ScanX_SYMBOL");
GetX = (GetX_t)dlsym(handle, "GetX_SYMBOL");

if(ScanX == nullptr || GetX == nullptr || InitX == nullptr)
{
    __android_log_print(ANDROID_LOG_INFO, "My Class", "Could not load functions.");
}

If done correctly, you should be able, now, to use the functions as usual. I understand, though, that it's not the most trivial process to go if you are a beginner.

like image 1
Manos Avatar answered Nov 20 '22 09:11

Manos