Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android NDK Linker wrong path

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?

like image 412
Lucas Teske Avatar asked Sep 01 '26 01:09

Lucas Teske


1 Answers

Short version

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.

Long version

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.

like image 189
sinisterchipmunk Avatar answered Sep 03 '26 15:09

sinisterchipmunk