I'd like to compile a library (static but also could be shared) written in C++ so that is statically contains all the C++ runtime functionality that I use in the library, i.e. another project can simply include my .a or .so file without having to worry about further dependencies. How can I do this using the NDK/Android build system?
So far, I have
APP_STL := c++_static
in my Application.mk and build my library using
include $(BUILD_STATIC_LIBRARY)
in my Android.mk after setting up the project. However, if I then pull in the resulting library into a second project (say, a dynamic library) that does not specify an STL (e.g. pure C) I get a load of symbol not found linker errors. Only when I specify to use the c++_static STL in the second project do these errors go away indicating that c++_static was not pulled into the first library. (I know this isn't surprising but it's not the behaviour I want).
Using good ol' make files and specifying build flags myself allows me to combine static libraries. My problem is coaxing the NDK build system to do it :)
if you compile a C++ runtime statically to your lib, unfortunately another project have to worry about this dependency:
If several .so files are using static C++ runtimes, this can lead to corruption and crashes because of the sharing of global variables used by the runtimes.
If you compile everything (your lib and the final project) into a single .so file, you can use one static C++ runtime, and it's better then if it's declared inside the end project. Because forcing the static inclusion of the C++ runtime in your lib would prevent the end project of using a C++ runtime.
Related
I have an application where all components are first assembled as static libraries and then those libraries are repacked to shared libraries. So the module(s) doing include $(BUILD_SHARED_LIBRARY) have empty LOCAL_SRC_FILES. With the way the components are laid out and composed, it makes sense and must remain that way.
However, the library for APP_STL only gets added if there is a C++ source (recognized by extension, which can be overridden with LOCAL_CPP_EXTENSION) in LOCAL_SRC_FILES. So the standard library does not get added and the final object does not link, because the component libraries do need it.
Is there a way to force NDK to include it?
There is an undocumented variable LOCAL_HAS_CPP that does it:
LOCAL_HAS_CPP := true
It is relatively new though. It is in the most recent NDK r16b (as of Mar 2018), but not in the r12b version we used until now.
I have been working with a project that needs to link against two shared libraries from other applications, without actually including them in the final package. I was using Android Studio with a common way of executing an external ndk-build but I am now using the experimental gradle NDK support.
From what I understand from the information here, using the jniLibs source set will package the binary with the final application, which is not what I want. I would like to link against the libraries during compile time, and expect that my own module is loaded in an environment where they are available.
My old workaround was to add the line: TARGET_NO_UNDEFINED_LDFLAGS := within the Android.mk, but I'm not aware of any way to something equivalent with the new NDK support.
Currently, I am including the jni directory which contains the libraries alongside my source by adding -L"<directory>" to ldFlags, and linking against them as I would with a system library, as below (currently, I am only building for ARMv7):
android.ndk {
moduleName = "ropecraftpe"
ldLibs.addAll(['log', 'name of one library', 'name of other library'])
ldFlags.add('-L"' + file('src/main/jni').absolutePath + '"')
cppFlags.add('-std=c++11')
stl = 'gnustl_shared'
abiFilters.addAll(['armeabi-v7a'])
}
This works for the most part - oddly enough, the linker does not complain about calls to static functions or globals, but does complain when attempting to use a constructor defined in one of the libraries: Error:(12) undefined reference to 'ClassName::ClassName(std::string const&, etc.)'. This class is defined in a header file included from my own C++ code, and the constructor is definitely present in the linked .so.
How does one go about linking this project successfully?
I have made a workaround; my solution was to add the libraries that I wished to link against to the NDK's system library directory.
This is located under ANDROID_NDK/platforms/android-XX/arch-XX/usr/lib/. This appears to work fine, as I wanted the libraries to be treated in the same way as system libraries (not included as prebuilts, but still linked against).
As an addition, one can place the header files under the include directory there.
I am building an Android library that contains a native part written using STL that is hidden under Java wrapper. In NDK documentation in a section Static runtimes it is stated that I should use shared runtime to avoid problems with multiple static runtimes, but I want to try using gnustl_static with a flag -fvisibility=hidden set.
The question is, will it solve possible problems or a shared library is the only way out?
No, building with -fvisibility=hidden will not help with the problems created by linking multiple libraries to a static C++ runtime.
Unless you are building gnustl_static yourself, the -fvisibility=hidden flag only applies to your native code which depends on gnustl_static. It won't change how the symbols are exported from gnustl_static.
In addition, if you link two shared libraries–or a shared library and
an executable– against the same static runtime, the final binary image
of each shared library includes a copy of the runtime's code. Having
multiple instances of runtime code is problematic because of
duplication of certain global variables that the runtime uses or
provides internally.
However, linking against a static C++ runtime should work perfectly fine as long as you are only creating one shared library.
This problem does not apply to a project comprising a single shared
library. For example, you can link against stlport_static, and expect
your app to behave correctly. If your project requires several shared
library modules, we recommend that you use the shared library variant
of your C++ runtime.
I am using a number of static pre-built static libraries in my native android application and everything works fine. Now I want to switch one of my static libraries to be .so. I was successfully able to build .so library by replacing BUILD_STATIC_LIBRARY with BUILD_SHARED_LIBRARY in its android.mk and adding required dependencies.
I was also able to build my application by replacing corresponding PREBUILT_STATIC_LIBRARY with PREBUILT_SHARED_LIBRARY in its android.mk. The resulting application now fails to start. I cannot even get to point where debugger attaches to the application.
Besides that what I do not understand is how the build system knows that the function should be imported from the library. My so library should export one function, but I did not declare it as dllexport/import or something. Still there are no unresolved symbols in my application (when I remove my prebuilt library from the list, the unresolved symbol appears as expected).
The other question is that I see there are two .so files generated. One big file in obj/local/$(TARGET_ARCH_ABI) folder and another small one in libs/$(TARGET_ARCH_ABI). When declaring my prebuilt library I reference the second one in libs folder.
I did try to search stackoverflow for answers and found quite a few related posts:
loading library (.so file) in android
NDK - How to use a generated .so library in another project
How to use .so file in Android code to use the native methods
How to use libffmpeg.so in Android project?
but I do not see how these posts related to my problem since I can successfully build and even link my application.
You need to load the libraries in reverse dependency order in the java code. You previously probably have something like this:
System.loadLibrary("mylib");
Now if your prebuilt library (that was previously a static library, now a shared library) is named dependencylib, you need to change the code for loading the libraries into this:
System.loadLibrary("dependencylib");
System.loadLibrary("mylib");
As for your question how the linker can figure it out; when linking libmylib.so, it looks for all undefined symbols in all the other libraries you specified (i.e. in libdependencylib.so, and in libc.so and other system libraries). As long as all undefined symbols are found somewhere, the linker is ok. Then at runtime, when libmylib.so is loaded, it does the same routine again; all undefined symbols are looked up in the list of symbols loaded in the current process. On linux, you normally don't need to manually mark symbols as dllexport as you do on windows - all non-static symbols are exported by default.
There may be two reasons why the app fails to start after the change of STATIC -> SHARED.
The prebuilt library is not installed. With your device connected, run adb ls -l /data/your.package.name/lib/. Do you see the library there?
The prebuilt library is not loaded. In your main Java class, try
static {
System.loadLibrary("prebuiltname");
System.loadLibrary("yourlib");
}
This is a static constructor, the safest place to load JNI library.
If you are on linux you will see exported symbols using nm -D. example nm -D libzip.so:
...
0000000000009dc0 T zip_unchange
0000000000009dd0 T zip_unchange_all
0000000000009e30 T zip_unchange_archive
0000000000009e60 T _zip_unchange_data
If you want to control visibility of your functions use __attribute__ ((visibility ("default"))) and command line -fvisibility=hidden. More information here.
Now I want to switch one of my static libraries to be .so. I was successfully able to build .so library by replacing BUILD_STATIC_LIBRARY with BUILD_SHARED_LIBRARY in its android.mk and adding required dependencies.
I don't think you can do it if its a C++ library. From <doc>/CPLUSPLUS-SUPPORT.html:
Please keep in mind that the static library variant of a given C++
runtime SHALL ONLY BE LINKED INTO A SINGLE BINARY for optimal
conditions.
What this means is that if your project consists of a
single shared library, you can link against, e.g., stlport_static, and
everything will work correctly.
On the other hand, if you have two
shared libraries in your project (e.g. libfoo.so and libbar.so) which
both link against the same static runtime, each one of them will
include a copy of the runtime's code in its final binary image. This
is problematic because certain global variables used/provided
internally by the runtime are duplicated.
This is likely to result in code that doesn't work correctly, for example:
* memory allocated in one library, and freed in the other would leak
or even corrupt the heap.
* exceptions raised in libfoo.so cannot be caught in libbar.so (and may
simply crash the program).
* the buffering of cout not working properly
This problem also happens if you want to link an executable and a shared
library to the same static library.
In other words, if your project requires several shared library modules,
then use the shared library variant of your C++ runtime.
From above, it means everything needs to link against the same C++ standard runtime shared object.
I develop an Android application, which needs to be a shared library. I have already made the same lib in Linux (using gcc), so I want to compile the same shared lib in Android (ARM processor). But NDK only suports JNI. Now I want to build a separate shared library without direct JNI interaction. I have a lot of C files and headers, so i cannot write JNI for everything. I want build my library for ARM processors. How can i do this?
my stuture is
----->JNI
---->myfile.c(jni c code)
----->android.mk(here i call my two shared lib)
folder1
--->include
----src
---->lib(here i will get my shared lib)
folder 2
----->include
----->src
----->lib(here i will get my 2nd shared lib)
Here I want to build my shared lib separately and then I will call it. Is it possible to build a shred lib without JNI in Android?
Android NDK makes no requirements for JNI compliance of the binaries built with it. But usually Android applications are written in Java, and when they use native components, the natural path is to use JNI.
Android NDK provides a rich and easy to use ndk-build command, which requires you to prepare at least one Android.mk file that is used by their make system. In many cases it is worth the effort to prepare such file based on your makefile(s). But if the project that you port from another platform uses advanced configure or Cmake features, you can skip the Android.mk completely, and use the standalone toolchain which is part of the NDK, as described in NDK official documentation. Note that Android does not support full Linux glibc, but rather a subset called bionic. The STL support is also not obvious, you have to choose one of the 7 options!
You can stop right here, get the shared library or an executable file and use them in traditional Linux manner. You can build your Android application all in C++.
But the easiest approach is to build a Java Android application using Android SDK, and call some native methods via JDK. You will need a wrapper .so for that, a library that exports JNI functions to Java and calls the non-JNI libraries in turn. In Android.mk you can refer to these libraries with LOCAL_SHARED_LIBRARIES. You may find the following question useful for this part: ndk build library outside main project source tree.
You will need to load the native libraries "manually" in your Java code. The system loader only searches for unresolved references in /system/lib folder.
The conventional place is the static constructor of the Java class that uses the native methods. The order of loading is important. If libone.so depends on libtwo.so, and libjni.so depends on libone and on libtwo, you will have something like
{
System.Load("two");
System.Load("one");
System.Load("jni");
}
You should also make sure that the Android SDK builder finds the files in libs/armeabi or libs-armeabi-v7a directory when it creates the APK file.
It is possible to build native library without writing any JNI specific C-code. You need to create Android.mk and Application.mk files for your library. You have a valid makefile ready, so you can use it to make an Android.mk.
Application.mk can be as follows:
APP_OPTIM := release
APP_PLATFORM := android-7
APP_STL := gnustl_static
APP_CPPFLAGS += -frtti
APP_CPPFLAGS += -fexceptions
APP_CPPFLAGS += -DANDROID
APP_ABI := armeabi-v7a
APP_MODULES := <put-the-name-of-your-library-here>
Use ndk-build to compile the library.