Jack
Jack

Reputation: 133619

Android NDK CMake linking issues

I'm quite new with NDK + Gradle + CMake integration and I'm trying to understand why linking doesn't export symbols as intended.

I have a static library built by a CMakeLists.txt which is not the main CMakeLists.txt.

The scripts does something like:

# main CMakeLists.txt
add_subdirectory(${LIBS}/foo libs}

add_library(native SHARED native.cpp)
# omitting standard android libraries
target_link_libraries(native foo ${android-lib} ${log-lib})

while CMakeLists.txt inside ${libs}/foo is the following:

# misc configuration of ${SRC}
add_library(foo STATIC ${SRC})

The script works fine, it's able to link libnative.so and I'm able to find the generated libfoo.a. Everything seems fine.

I then try to define a native method in foo.cpp contained in foo library:

extern "C" JNIEXPORT void JNICALL Java_com_mypackage_Controls_onTap(JNIEnv*, jobject, int x, int y) {
  // log something
}

But I'm not able to call the native method defined in foo library. I get an UnsatisfiedLinkError at runtime. If, instead, I move (directly by copying and pasting) the method to native.cpp then everything goes fine.

So basically:

I tried to inspect the exported functions with nm and it looks like that foo.a correctly exports the native function as I can see

00011060 T Java_com_mypackage_Controls_onTap

But this entry disappears from libnative.so. If, instead, I define the method directly in native.cpp then I can see it correctly with nm also on libnative.so.

In addition calling any method in foo library from native.cpp works as intended so the library is effectively statically linked.

I am not able to understand the reason behind this, the approach should be fine, visibility should be correct as specified by JNIEXPORT macro so I'm really groping in the dark (and Gradle doesn't provide any output of compilation phase so I can't understand what's happening, but the build.ninja file seems correct)

Upvotes: 2

Views: 2218

Answers (1)

Alex Cohn
Alex Cohn

Reputation: 57203

This behavior, even if unpleasant, is correct. The linker drops any object "files" (in your case, foo.o) from used static libraries, unless they are "pinned" by one of the objects in the shared lib (in your case, native.o). There are three ways to solve the problem:

  1. compile foo.cpp as part of libnative.so instead of a static lib.

  2. reference Java_com_mypackage_Controls_onTap or any other external symbol from foo.cpp in native.cpp

  3. use SET(native -Wl,--whole-archive foo -Wl,--no-whole-archive) (see https://stackoverflow.com/a/17477559/192373)

Upvotes: 4

Related Questions