We provide Android libraries for use with the Dalvik virtual machine as a part of our ASN1C and XBinder products, but we also build our C/C++ runtimes using the Android NDK as well. These libraries can be used through the Android native interface.
We recently received a report that there were missing symbols in our runtimes when using JNI. It took a bit of effort to track down the failure, but thanks to a dedicated reseller and some internal testing, we were able to isolate and resolve the issue. This blog post helps to summarize some of the issues and suggests some workarounds for those considering using our software with JNI, but it should be more broadly applicable to anyone who is using the Android NDK for their own purposes. It's a fairly lengthy take on the issue, but we hope it illuminates some of the pitfalls that can come with building native Android applications.
The reported error looked a bit like this:
How Does This Happen?
Those of you who have used dynamically-linked applications can recognize the basic problem: the library in question doesn't provide the requested function at run time. It's common with applications whose dependencies are satisfied by the build host but not the target deployment system. (This is why we provide our own versions of the Qt libraries when distributing our GUIs: the target systems often have a different version of Qt whose symbols differ from the ones we link against.)
Usually this problem arises when an application is successfully built on a host system and deployed to a target whose libraries don't match. But it can also happen when attempting to dynamically load a library from within an application. In Java, this is done via System.loadLibrary. Unfortunately, these libraries can be incorrectly linked very easily—even by automated build tools. (In this case, it appears to be an issue with Eclipse, which successfully resolved the functions in our libraries when compiling the application and failed to export them into a custom library that our customer created.)
We'll demonstrate the issue by showing progressive attempts to compile an application against an improperly linked library here, but your experience with JNI will probably look rather more like the error above.
Dynamically linked libraries will happily ignore missing symbols (tools like nm use the U to mark them as undefined), so it's easy to accidentally create broken ones if you're not careful. This becomes especially problematic with optimizing linkers, since they remove functions that aren't directly referenced. Library link order is very significant in this scenario.
Clearly the solution is to make sure that the library link order is correct whenever you create a dynamic library. Your IDE may need to be configured differently to ensure that it doesn't accidentally misorder the libraries.
Alternatively, you can also use the -Wl,-whole-archive switch when creating the library to tell ld to include all of the objects in the relevant libraries. (Obviously this won't work if your backend linker isn't ld or one of its drop-in replacements.) If you use this switch when linking an application, you'll need to make sure that you add `-Wl,-no-whole-archive`` after the libraries, too <http://stackoverflow.com/a/2657390>`__.
We generally wouldn't recommend this latter option because it produces larger libraries than may be needed—it doesn't optimize, after all—and space is at a premium on embedded devices. Various problems using it have also been reported.