I have a simple C++ function compiled into a dylib file that I'm trying to run on an Android phone. The function is super simple, it just adds to numbers and returns the result. However, I keep getting this error:
Another exception was thrown: Invalid argument(s): Failed to load dynamic library 'libadd.dylib': dlopen failed: library "libadd.dylib" not found .
I'm really not sure what I'm doing wrong. I've done the following steps:
My Dart implementation:
import 'dart:ffi' as ffi;
import 'dart:io' show Platform, Directory;
import 'package:path/path.dart' as path;
typedef C_ADD = ffi.Int Function(
ffi.Int a, ffi.Int b); // FFI signature of C function
typedef ADD = int Function(int a, int b);
void linkAndCallFunction() {
var libraryPath = path.join(Directory.current.path, "libadd.dylib");
final dylib = ffi.DynamicLibrary.open(libraryPath);
final ADD add = dylib.lookup<ffi.NativeFunction<C_ADD>>("add").asFunction();
final result = add(40, 2);
print(result);
}
I've added these to the build.gradle files:
build.gradle:
buildscript{
ext{
ndkVersion = "25.1.8937393"
}
...
and app/build.gradle:
android {
ndkVersion rootProject.ext.ndkVersion
externalNativeBuild {
cmake {
path "../../lib/CMakeLists.txt"
}
}
This is my CMakeLists.txt file:
cmake_minimum_required(VERSION 3.10.2)
project(add LANGUAGES CXX C)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
add_library(add SHARED ./add.cpp)
and my file structure of the project looks like this:
lib/
- add.cpp
- add.o
- CMakeLists.txt
- libadd.dylib
- main.dart
it also may be worth mentioning that in order to compile add.cpp into a dylib I ran the following commands:
g++ -c add.cpp
ar rvs libadd.dylib add.o
and if you're wondering, add.cpp looks like this:
#define EXPORT extern "C" __attribute__((visibility("default")))
__attribute__((used))
EXPORT
int add(int a, int b){
return a + b;
}
Where is this error coming from? am I compiling to a dylib incorrectly?
The answer to this problem was relatively simple, it just stemmed from my lack of knowledge on how Android actually compiles to a static library. Hopefully it helps someone else who is trying to understand how to setup external C++ code in a flutter program.
The static library is generated automatically and is set up in CMakeLists.txt.
Firstly, I moved all the C++ files into the android folder. Then, I set up CMakeLists.txt like this:
cmake_minimum_required(VERSION 3.10.2)
add_library( add // library name, will create libadd.so
SHARED
add.cpp
)
The problem before was that I was trying to manually compile the file myself, when I should have been letting CMakeLists do it instead. According to the android documentation:
"The convention CMake uses to name the file of your library is as follows:
liblibrary-name.so
For example, if you specify "native-lib" as the name of your shared library in the build script, CMake creates a file named libnative-lib.so. "
https://developer.android.com/studio/projects/configure-cmake
So when I run the program, the static library is created automatically and placed in the correct place. Then, the Dart FFI can find it with the DynamicLibrary.open() function. In this case, CMakeLists will generate a file called libadd.so, and I can callDynamicLibrary.open('libadd.so') and the program works.
Is there a way to pass a constant at compile time into the externalNativeBuild gradle enclosure, and to be able to use it in code?
For example:
externalNativeBuild {
cmake {
arguments "-DTEST=" + rootProject.ext.value
}
}
And the to be able to use it somehow like next:
void foo() {
int bla = TEST;
....
}
Note that the above code is not working, as TEST is not recognized by the c++ compiler, which is what I am trying to solve.
The easiest solution will be to use add_definitions:
add_definitions(-DTEST=${TEST})
to your CMakeLists.txt file.
Another one would be to use configuration file
Basing on Native sample app from Android Studio:
Create file: config.h.in in the same folder as default native-lib.cpp with following contents:
#ifndef NATIVEAPP1_CONFIG_H_IN
#define NATIVEAPP1_CONFIG_H_IN
#cmakedefine TEST ${TEST}
#endif //NATIVEAPP1_CONFIG_H_IN
In CMakeLists.txt add:
# This sets cmake variable to the one passed from gradle.
# This TEST variable is available only in cmake but can be read in
# config.h.in which is used by cmake to create config.h
set(TEST ${TEST})
configure_file( config.h.in ${CMAKE_BINARY_DIR}/generated/config.h )
include_directories( ${CMAKE_BINARY_DIR}/generated/ )
(add it for example, above add_library call)
Finally, add #include "config.h" in you cod (ex. in native-lib.cpp) to use TEST macro.
Rebuild
I have problem when I try to use C++ library in Android studio project.
I am using statically built Qt 5.14.2 for Android.
I have Qt library project which has only 1 class with this 2 functions:
testclass.h
#include <QDebug>
#include <jni.h>
extern "C"
{
JNIEXPORT void JNICALL Java_com_example_MyTestAndroidApp_LibraryClass_log();
void logNormal();
}
testclass.cpp
JNIEXPORT void JNICALL Java_com_example_MyTestAndroidApp_LibraryClass_log()
{
qDebug() << "---> log from Qt library";
}
void logNormal()
{
qDebug() << "---> log from Qt library";
}
An in the .pro file I am using shared or staticlib to switch from shared(.so) and static(.a) library.
TEMPLATE = lib
CONFIG += shared
#CONFIG += staticlib
CONFIG += c++11
After successfully building the lib (shared and static) I got this files(for ABI x86 but others ABIS have similar sizes too):
libTestLibrary.so ---> 3,359 KB
libTestLibrary.a ---> 7 KB
For the shared (.so) library I was able to successfully call the function from android app.
In Android Studio I am creating new Native C++ project. I have Java JNI LibraryClas which is used to load the library and call the log() function from the library. I placed the library in jniLibs folder.
But for the static(.a) library there are a lot of problems. I tried to call the logNormal() function from the C++ part of the android app(from native-lib.cpp). I am not using JNI like in the shared library case. Here is what I tried:
Create new Native C++ project
Created libs(for the static library) and include(for testclass.h) folders inside app\src\main\cpp
Inside CMakeList.txt I have added this:
add_library(TestLibrary STATIC IMPORTED)
set_target_properties(TestLibrary PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libTestLibrary.a)
target_link_libraries( native-lib TestLibrary ${log-lib} )
And I called logNormal(); from native-lib.cpp.
First I got Error about missing Qt5Core library. After adding it the same way like my library I got errors for others missing libraries and files.
I have this questions:
Why static library is so much smaller then the shared library? (7KB vs 3,359KB) I found this which is opposite from my case:
Can I somehow build my static lib with all libraries and files that it need, so I don't get errors for missing libraries?
Can I use JNI to call C++ functions from static lib? Or when calling C++ functions from static lib it must be done from the C++ part of the android project?
Sorry for the long post and more then 1 question. Any hint or help is welcome. Thank you so much in advance.
It's the first time I try to create a library c++ for Android/iOS.
I'm using Visual Studio 2015 - Xamarin.
First I created a project : Visual C++ -> Cross Platform -> Shared Library. In the hared library, I created 2 files.
SayHello.h :
#pragma once
#include <string.h>
class SayHello {
public:
SayHello();
~SayHello();
static char* Hello();
};
SayHello.cpp :
#include "SayHello.h"
extern "C"
{
SayHello::SayHello(){}
SayHello::~SayHello(){}
char * SayHello::Hello()
{
return "Hello !";
}
}
Then I generated a file libSayHello.so and created a project Android with xamarin to try to call the function hello.
There is my MainActivity.cs :
[DllImport("libSayHello.so")]
static extern String Hello();
protected override void OnCreate(Bundle bundle)
{
// I paste only my added code :
String hello = Hello();
Toast.MakeText(this.ApplicationContext, hello, ToastLength.Long);
}
I did all steps in this tutorial, but I have an exception :
System.DllNotFoundException: libSayHello.so
I searched for this, but I must be so noob cause I did not find anything. How should I use my libSayHello.so ?
EDIT:
There is my libSayHello.so seen with 7zip:
And my project :
I think this will be the best sample for you.
This all works according to the following scheme:
Android supports 7 CPU architectures.
But Xamarin supports 5 of them. So in the settings of your Xamarin.Android project check which architectures you will support:
[Xamarin.Droid.project]->[Properties]->[Android Options]->[Advanced]->[Supported architectures]
Check which archs are necessary for your project. According to this your shared library should be compiled for these archs. And you should put your shared libraries in Xamarin.Droid.project's folder lib:
To see them in Solution Explorer you should mention them in your Xamarin.Android project's .CSPROJ.
Add there next item groups:
<ItemGroup>
<AndroidNativeLibrary Include="lib\{ARCH}\libCLib.so">
<Abi>{ARCH}</Abi>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</AndroidNativeLibrary>
</ItemGroup>
{ARCH} could be next: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64.
Now you can put DllImport in your code:
[DllImport("libCLib", EntryPoint = "clib_add")]
public static extern int Add(int left, int right);
I think you have to tell about entry point, because i had runtime errors without this statement System.EntryPointNotFoundException.
And don't forget to add next in your code:
using System.Runtime.InteropServices;
Confirm the library is put in "/lib/{arch}/" folder.
Try [DllImport("SayHello")]. the engine may add "lib" and ".so" automatically.
I'm developing the native application that works with Android via the NDK.
I need to call the backtrace() function when there is a crash. The problem is that there is no <execinfo.h> for the NDK.
Is there any other way to get that back trace?
Android have no backtrace(), but unwind.h is here to serve. Symbolization is possible via dladdr().
The following code is my simple implementation of backtrace (with no demangling):
#include <iostream>
#include <iomanip>
#include <unwind.h>
#include <dlfcn.h>
namespace {
struct BacktraceState
{
void** current;
void** end;
};
static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
BacktraceState* state = static_cast<BacktraceState*>(arg);
uintptr_t pc = _Unwind_GetIP(context);
if (pc) {
if (state->current == state->end) {
return _URC_END_OF_STACK;
} else {
*state->current++ = reinterpret_cast<void*>(pc);
}
}
return _URC_NO_REASON;
}
}
size_t captureBacktrace(void** buffer, size_t max)
{
BacktraceState state = {buffer, buffer + max};
_Unwind_Backtrace(unwindCallback, &state);
return state.current - buffer;
}
void dumpBacktrace(std::ostream& os, void** buffer, size_t count)
{
for (size_t idx = 0; idx < count; ++idx) {
const void* addr = buffer[idx];
const char* symbol = "";
Dl_info info;
if (dladdr(addr, &info) && info.dli_sname) {
symbol = info.dli_sname;
}
os << " #" << std::setw(2) << idx << ": " << addr << " " << symbol << "\n";
}
}
It may be used for backtracing into LogCat like
#include <sstream>
#include <android/log.h>
void backtraceToLogcat()
{
const size_t max = 30;
void* buffer[max];
std::ostringstream oss;
dumpBacktrace(oss, buffer, captureBacktrace(buffer, max));
__android_log_print(ANDROID_LOG_INFO, "app_name", "%s", oss.str().c_str());
}
Here is some working and complete code that implements dump_stack() by starting with Eugene Shapovalov's answer and does symbol lookups and C++ name demangling right on the device. This solution:
works with the NDK r10e (you don't need the complete Android AOSP source tree)
does NOT require any extra third-party libraries (no libunwind, libbacktrace, corkscrew, CallStack)
does NOT depend on any shared libraries being installed on the device (e.g. corkscrew, which got axed in Android 5)
does NOT force you to map addresses to symbols on your development machine; all symbol names are revealed on the Android device in your code
It uses these facilities, which are built into the NDK:
<unwind.h> header that is in the NDK toolchain/ dirs (NOT libunwind)
dladdr()
__cxxabiv1::__cxa_demangle() from <cxxabi.h> (see STLport note below)
So far, I tested this only with an arm-based Android 5.1 device and I called it only from my main program (not from a signal handler). I was using the default ndk-build which chooses gcc for the arm platform.
Please comment if you are able to make this work
on other Android OSes
from a SIGSEGV handler on crash (my goal was simply to print a stack trace on assertion failure)
using clang toolsets instead of gcc
Note the r10e NDK has <unwind.h> code for many architectures in both gcc and clang toolsets so the support looks broad.
The C++ symbol name demangling support depends on an __cxxabiv1::__cxa_demangle() function that comes from the C++ STL that is included with the NDK. This should work as-is if you are doing your Android build with the GNU STL (APP_STL := gnustl_static or gnustl_shared in Application.mk; see this page for more info). If you are currrently using no STL at all, simply add APP_STL := gnustl_static or gnustl_shared to Application.mk. If you are using STLport, you have to enjoy a special kind of fun (more below).
IMPORTANT: for this code to work, you must not use the -fvisibility=hidden gcc compiler option (at least in your debug builds). That option is commonly used to hide symbols from prying eyes in release builds.
Many people have noted that the ndk-build script strips symbols from your NDK .so whilst copying it to the libs/ directory of your project. That is true (using nm on the two copies of the .so gives very different results) HOWEVER this particular layer of stripping amazingly does not prevent the code below from working. Somehow even after stripping there are still symbols (as long as you remembered not to compile with -fvisibility=hidden). They show up with nm -D.
Other posts on this topic have discussed other compiler options like -funwind-tables. I didn't find that I needed to set any such option. The default ndk-build options worked.
To use this code, replace _my_log() with your favorite logging or string function.
STLport users see special notes below.
#include <unwind.h>
#include <dlfcn.h>
#include <cxxabi.h>
struct android_backtrace_state
{
void **current;
void **end;
};
_Unwind_Reason_Code android_unwind_callback(struct _Unwind_Context* context,
void* arg)
{
android_backtrace_state* state = (android_backtrace_state *)arg;
uintptr_t pc = _Unwind_GetIP(context);
if (pc)
{
if (state->current == state->end)
{
return _URC_END_OF_STACK;
}
else
{
*state->current++ = reinterpret_cast<void*>(pc);
}
}
return _URC_NO_REASON;
}
void dump_stack(void)
{
_my_log("android stack dump");
const int max = 100;
void* buffer[max];
android_backtrace_state state;
state.current = buffer;
state.end = buffer + max;
_Unwind_Backtrace(android_unwind_callback, &state);
int count = (int)(state.current - buffer);
for (int idx = 0; idx < count; idx++)
{
const void* addr = buffer[idx];
const char* symbol = "";
Dl_info info;
if (dladdr(addr, &info) && info.dli_sname)
{
symbol = info.dli_sname;
}
int status = 0;
char *demangled = __cxxabiv1::__cxa_demangle(symbol, 0, 0, &status);
_my_log("%03d: 0x%p %s",
idx,
addr,
(NULL != demangled && 0 == status) ?
demangled : symbol);
if (NULL != demangled)
free(demangled);
}
_my_log("android stack dump done");
}
What if you are using STLport STL instead of GNU STL?
Sucks to be you (and me). There are two problems:
The first problem is that STLport lacks the __cxxabiv1::__cxa_demangle() call from <cxxabi.h>. You will need to download two source files cp-demangle.c and cp-demangle.h from this repository and place them in a demangle/ subdirectory under your source, then do this instead of #include <cxxabi.h>:
#define IN_LIBGCC2 1 // means we want to define __cxxabiv1::__cxa_demangle
namespace __cxxabiv1
{
extern "C"
{
#include "demangle/cp-demangle.c"
}
}
The second problem is more nasty. It turns out there's not one, not two, but THREE different, incompatible types of <unwind.h> in the NDK. And you guessed it, the <unwind.h> in STLport (actually it's in the gabi++ library that comes along for a ride when you choose STLport) is incompatible. The fact that the STLport/gabi++ includes come before the toolchain includes (see your ndk-build output's -I options) means that STLport is preventing you from using the real <unwind.h>. I could not find any better solution than to go in and hack the filenames inside my installed NDK:
sources/cxx-stl/gabi++/include/unwind.h to sources/cxx-stl/gabi++/include/unwind.h.NOT
sources/cxx-stl/gabi++/include/unwind-arm.h to sources/cxx-stl/gabi++/include/unwind-arm.h.NOT
sources/cxx-stl/gabi++/include/unwind-itanium.h to sources/cxx-stl/gabi++/include/unwind-itanium.h.NOT
I'm sure there's some more elegant solution, however I suspect switching the order of the -I compiler options will probably create other problems, since STLs generally want to override toolchain include files.
Enjoy!
backtrace() is a non-standard Glibc extension, and even then somewhat shaky on ARM (you need to have built everything with -funwind-tables, I think, and then have a somewhat new Glibc?)
As far as I know, this function is not included in the Bionic C library used by Android.
You could try pulling the source for Glibc backtrace into your project, and then rebuilding the interesting things with the unwind table, but it sounds like hard work to me.
If you have debug info, you could try launching GDB with a script that attaches to your process, and prints a backtrace that way, but I have no idea if GDB works on Android (although Android is basically Linux, so that much id fine, the installation details may be problematic?) You may get further by dumping core somehow (does Bionic support that?) and analysing it after-the-fact.
Here is a crazy one-line method for getting a fantastically detailed stack trace that includes both C/C++ (native) and Java: abuse JNI
env->FindClass(NULL);
As long as your app is compiled debug, or otherwise uses Android's CheckJNI, this erroneous call will trigger Android's built-in JNI checker which will produce a gorgeous stack trace on the console (from the "art" log source). This stack trace is done inside Android's libart.so using all the latest technologies and bells and whistles that are not easily available to lowly NDK users like us.
You can enable CheckJNI even for apps that are not compiled debug. See this google FAQ for details.
I do not know if this trick works from a SIGSEGV handler (from SIGSEGV you might get a stack trace of the wrong stack, or maybe art will not be triggered at all) but it is worth a try.
If you need a solution that makes the stack trace available in your code (e.g. so you can send it over the net or log it), see my other answer in this same question.
You can use the CallStack:
#include <utils/CallStack.h>
void log_backtrace()
{
CallStack cs;
cs.update(2);
cs.dump();
}
Results will need de-mangling by c++filt or something similar:
D/CallStack( 2277): #08 0x0x40b09ac8: <_ZN7android15TimedEventQueue11threadEntryEv>+0x0x40b09961
D/CallStack( 2277): #09 0x0x40b09b0c: <_ZN7android15TimedEventQueue13ThreadWrapperEPv>+0x0x40b09af9
you#work>$ c++filt _ZN7android15TimedEventQueue11threadEntryEv _ZN7android15TimedEventQueue13ThreadWrapperEPv
android::TimedEventQueue::threadEntry()
android::TimedEventQueue::ThreadWrapper(void*)
Here is how you capture backtrace on 32-bit ARM, using libunwind, that is bundled with modern Android NDKs (such as NDK r16b).
// Android NDK r16b contains "libunwind.a" for armeabi-v7a ABI.
// This library is even silently linked in by the ndk-build,
// so we don't have to add it manually in "Android.mk".
// We can use this library, but we need matching headers,
// namely "libunwind.h" and "__libunwind_config.h".
// For NDK r16b, the headers can be fetched here:
// https://android.googlesource.com/platform/external/libunwind_llvm/+/ndk-r16/include/
#include "libunwind.h"
struct BacktraceState {
const ucontext_t* signal_ucontext;
size_t address_count = 0;
static const size_t address_count_max = 30;
uintptr_t addresses[address_count_max] = {};
BacktraceState(const ucontext_t* ucontext) : signal_ucontext(ucontext) {}
bool AddAddress(uintptr_t ip) {
// No more space in the storage. Fail.
if (address_count >= address_count_max)
return false;
// Add the address to the storage.
addresses[address_count++] = ip;
return true;
}
};
void CaptureBacktraceUsingLibUnwind(BacktraceState* state) {
assert(state);
// Initialize unw_context and unw_cursor.
unw_context_t unw_context = {};
unw_getcontext(&unw_context);
unw_cursor_t unw_cursor = {};
unw_init_local(&unw_cursor, &unw_context);
// Get more contexts.
const ucontext_t* signal_ucontext = state->signal_ucontext;
assert(signal_ucontext);
const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext);
assert(signal_mcontext);
// Set registers.
unw_set_reg(&unw_cursor, UNW_ARM_R0, signal_mcontext->arm_r0);
unw_set_reg(&unw_cursor, UNW_ARM_R1, signal_mcontext->arm_r1);
unw_set_reg(&unw_cursor, UNW_ARM_R2, signal_mcontext->arm_r2);
unw_set_reg(&unw_cursor, UNW_ARM_R3, signal_mcontext->arm_r3);
unw_set_reg(&unw_cursor, UNW_ARM_R4, signal_mcontext->arm_r4);
unw_set_reg(&unw_cursor, UNW_ARM_R5, signal_mcontext->arm_r5);
unw_set_reg(&unw_cursor, UNW_ARM_R6, signal_mcontext->arm_r6);
unw_set_reg(&unw_cursor, UNW_ARM_R7, signal_mcontext->arm_r7);
unw_set_reg(&unw_cursor, UNW_ARM_R8, signal_mcontext->arm_r8);
unw_set_reg(&unw_cursor, UNW_ARM_R9, signal_mcontext->arm_r9);
unw_set_reg(&unw_cursor, UNW_ARM_R10, signal_mcontext->arm_r10);
unw_set_reg(&unw_cursor, UNW_ARM_R11, signal_mcontext->arm_fp);
unw_set_reg(&unw_cursor, UNW_ARM_R12, signal_mcontext->arm_ip);
unw_set_reg(&unw_cursor, UNW_ARM_R13, signal_mcontext->arm_sp);
unw_set_reg(&unw_cursor, UNW_ARM_R14, signal_mcontext->arm_lr);
unw_set_reg(&unw_cursor, UNW_ARM_R15, signal_mcontext->arm_pc);
unw_set_reg(&unw_cursor, UNW_REG_IP, signal_mcontext->arm_pc);
unw_set_reg(&unw_cursor, UNW_REG_SP, signal_mcontext->arm_sp);
// unw_step() does not return the first IP,
// the address of the instruction which caused the crash.
// Thus let's add this address manually.
state->AddAddress(signal_mcontext->arm_pc);
// Unwind frames one by one, going up the frame stack.
while (unw_step(&unw_cursor) > 0) {
unw_word_t ip = 0;
unw_get_reg(&unw_cursor, UNW_REG_IP, &ip);
bool ok = state->AddAddress(ip);
if (!ok)
break;
}
}
void SigActionHandler(int sig, siginfo_t* info, void* ucontext) {
const ucontext_t* signal_ucontext = (const ucontext_t*)ucontext;
assert(signal_ucontext);
BacktraceState backtrace_state(signal_ucontext);
CaptureBacktraceUsingLibUnwind(&backtrace_state);
exit(0);
}
Here is a sample backtrace testing app with 3 implemented backtracing methods, including the method shown above.
https://github.com/alexeikh/android-ndk-backtrace-test
If you just want a few (eg 2 - 5) topmost call frames and if your GCC is recent enough, you might consider using some return address or frame address builtins.
(But I don't know much about Android, so I could be wrong)