I'm currently working with a NDK Project that uses shared libraries. And I have two shared libraries to integrate: libsatprotocol.so and libsat-tanca.so.
So I added to my Android.mk these libraries so I could make a wrapper. For libsatprotocol everything is working fine. But for libsat-tanca, I get a crash on android:
java.lang.UnsatisfiedLinkError: dlopen failed: could not load library "/home/lucas/Rockspoon/satlib/Android/app/src/main/obj/local/armeabi/libsat-tanca.so" needed by "libsat-jni.so"; caused by library "/home/lucas/Rockspoon/satlib/Android/app/src/main/obj/local/armeabi/libsat-tanca.so" not found
at java.lang.Runtime.loadLibrary(Runtime.java:371)
at java.lang.System.loadLibrary(System.java:989)
So the weird thing is that this path in my computer path for the library, and I have no clue from where it is getting it. If I remove the libsat-tanca of the dependencies, it works fine (in libsatprotocol).
Here are my Android.mk:
LOCAL_PATH := $(call my-dir)
#LOCAL_ALLOW_UNDEFINED_SYMBOLS=true
include $(CLEAR_VARS)
LOCAL_MODULE := sat-tanca
LOCAL_SRC_FILES := tanca/$(TARGET_ARCH_ABI)/libsat-tanca.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := sat-dimep
LOCAL_SRC_FILES := dimep/$(TARGET_ARCH_ABI)/libsatprotocol.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := sat-jni
LOCAL_SRC_FILES := satlib.c
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -lz -llog
LOCAL_SHARED_LIBRARIES := sat-tanca sat-dimep
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := armeabi #armeabi-v7a mips x86 x86_64
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libsatprotocol.so $(TARGET_ARCH_ABI)/libsat-tanca.so
SATControl.java
static {
System.loadLibrary("sat-jni");
}
build.gradle (app)
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.rockspoon.libraries.satlib"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
ndk {
moduleName "sat-jni"
}
}
sourceSets.main {
jni.srcDirs = [] // This prevents the auto generation of Android.mk
jniLibs.srcDir 'src/main/libs' // This is not necessary unless you have precompiled libraries in your project.
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
task buildNative(type: Exec, description: 'Compile JNI source via NDK') {
def ndkDir = android.ndkDirectory
commandLine "$ndkDir/ndk-build",
'-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
'-j', Runtime.runtime.availableProcessors(),
'all',
'NDK_DEBUG=1'
}
task cleanNative(type: Exec, description: 'Clean JNI object files') {
def ndkDir = android.ndkDirectory
commandLine "$ndkDir/ndk-build",
'-C', file('src/main/jni').absolutePath, // Change src/main/jni the relative path to your jni source
'clean'
}
clean.dependsOn 'cleanNative'
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn buildNative
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
}
Any idea why it is linking just the libsat-tanca.so with my PC path?
For me this problem occurred when the shared library that was being linked against lacked a SONAME entry. It took hours to debug but was trivially fixed by making sure to pass the -Wl,-soname,libmylibrary.so option when compiling the library.
I started with a standalone library, libmylibrary.so. This file was originally compiled outside of the main project.
The NDK was configured to generate a second shared object, libmylibrary-wrapper.so, which depends on the library above.
So my architecture is a bit like: Activity depends on libmylibrary-wrapper.so depends on libmylibrary.so. It was libmylibrary.so that lacked a SONAME, but it was libmylibrary-wrapper.so that became damaged.
My guess is the linker looked inside of libmylibrary.so, didn't find a SONAME, and chose to use the filename of this library instead. This is a sane choice I think, since the SONAME entry usually matches the filename. However, instead of using just the filename, it uses the full absolute path as it appears on the host machine. (It may have to do with the how the linker is invoked, since typically you'd link with -lmylibrary instead of an absolute /path/to/libmylibrary.so. This is speculation on my part, though.)
I verified this issue by running arm-linux-androideabi/bin/readelf -d intermediates/merged_native_libs/debug/out/lib/arm64-v8a/libmylibrary-wrapper.so. Checking the output for NEEDED, I saw some normal entries, and one very obvious erroneous entry:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [/Users/me/AndroidStudioProjects/TestNDKApp/app/src/main/cpp/../../../libs/arm64-v8a/libmylibrary.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x000000000000000e (SONAME) Library soname: [libmylibrary-wrapper.so]
So now we know how the PC path is appearing on an Android, and this itself implies a problem with the linking process, but nothing explicitly points at the other library as the source of the issue. It took a bit of a guess. Perhaps knowing that the NEEDED entry is typically generated by the other library's SONAME could have been a hint.
Going back to the build process for libmylibrary.so and just adding the flag -Wl,-soname,libmylibrary.so to the compiler resolved the issue. This instructs the compiler to instruct the linker (-Wl) to set the SONAME (-soname) of the shared object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With