I'm new to stackoverflow and I wish to ask for some help on Android C++.
I'm trying to implement a very simple Android test program in C++ which calls a function in a loaded shared library implemented in C++ as well.
Here is my main JNI implemenation (native-lib.cpp):
#include <jni.h>
#include <string>
#include <dlfcn.h>
#include "external.hpp"
extern "C" JNIEXPORT jstring JNICALL
Java_com_useless_myapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
void* handle;
void (*funcext)();
handle = dlopen("libexternal.so",RTLD_LAZY);
funcext = (void (*)(void))dlsym(handle, "_Z5func2v");
try {
funcext();
}
catch (MyException &err)
{
std::string hello = "MyException from C++";
return env->NewStringUTF(hello.c_str());
}
catch (GenericException &err)
{
std::string hello = "GenericException from C++";
return env->NewStringUTF(hello.c_str());
}
catch (GenericException* err)
{
std::string hello = "GenericException* from C++";
return env->NewStringUTF(hello.c_str());
}
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
And here is my libexternal.so implementation (external.cpp):
#include <jni.h>
#include <string.h>
#include "external.hpp"
GenericException::GenericException(){};
GenericException::GenericException(int errcode,char* msg)
{
errorcode = errcode;
memset(message,0,256);
strcpy(message,msg);
}
MyException::MyException(int errcode,char* msg)
{
errorcode = errcode;
memset(message,0,256);
strcpy(message,msg);
}
void func()
{
throw MyException(10,"Error1!");
}
bool func3()
{
try {
func();
}
catch (GenericException &err)
{
return false;
}
return true;
}
void func2()
{
if (!func3())
throw MyException(11,"Error2!");
}
The external.hpp file is defined as follows:
void func();
void func2();
bool func3();
class GenericException
{
public:
GenericException();
GenericException(int errcode,char* msg);
protected:
int errorcode;
char message[256];
};
class MyException : public GenericException
{
public:
MyException(int errcode,char* msg);
};
The program compiles and links clean however when I run it my android application crashes whith the following message in the logcat:
2018-11-14 09:57:42.058 6519-6519/com.useless.myapplication A/libc: /usr/local/google/buildbot/src/android/ndk-release-r18/external/libcxx/../../external/libcxxabi/src/abort_message.cpp:73: abort_message: assertion "terminating with uncaught exception of type MyException" failed
The error rises when I try to execute external.cpp line 41:
throw MyException(11,"Error2!");
As suggested by other posts I've found I've tried to enable the -frtti flag in my app build.gradle cppflgs but this doesn't solved the error.
I've tried to run the same code (without the Java top layer honestly) on Linux and MacOS but on these platforms the exception is caught by the native-lib.cpp code.
Is there something I'm not aware of about C++ exceptions in Android?
How can I be able to catche the exception thrown by a library I loaded with dlopen on Android?
Thanks
You exception type does not have a key function, so it's typeinfo is emitted with vague linkage. The means it is a weak symbol in every library in which it is used.
Your JNI library loads and resolves its own typeinfo. Your dlopened library is then loaded and also resolves its own typeinfo because it cannot access the parent scope (System.loadLibrary uses RTLD_LOCAL). Because of this, there are two separate typeinfo objects for your exception type. RTTI equality is checked by comparing the addresses of the typeinfo object (see the C++ ABI spec).
I'm not certain if this can be resolved without directly linking your JNI code to libexternal.so. If you add the key function necessary to make this work (which would be defined in libexternal.so), then I believe you'd need to link to it for your JNI code to link.
Related
I am hooking an existing library that is compiled using gnustl std::string instead of libc++ std::_ndk1::string. If I try to set or access these strings I just get garbage. How do I convert my std::string to std::_ndk1::string and vice versa in my hook application?
I cannot compile my hook with "-DANDROID_STL=gnustl_shared" because it no longer exists and other libraries in use require libc++.
The documentation mentions this https://developer.android.com/ndk/guides/cpp-support "The various STLs are not compatible with one another. As an example, the layout of std::string in libc++ is not the same as it is in gnustl" which is exactly the problem.
To use gnustl, you could compile all your native code with NDK r.17 or older. This is a dangerous path, because many important bugs, including security-related, have been fixed since then.
Another unsupported (and not recommended) way to deal with your problem is to get the gnustl sources from NDK r.17 and compile them with the latest NDK version.
Your best option is to have all your dependencies rebuilt with a recent version of NDK and its c++_shared runtime library.
Here's what I came up with (for now, really not ideal):
StringsProxy.cc
#include "StringsProxy.h"
#include <iostream>
#include <string>
using namespace std;
__attribute__((visibility("default")))
extern "C" StringsProxy::StringsProxy(const char* contents)
{
set_string = std::string(contents);
}
__attribute__((visibility("default")))
extern "C" StringsProxy::StringsProxy(uintptr_t str) {
set_string = *reinterpret_cast<proxy_string*>(str);
}
__attribute__((visibility("default")))
extern "C" const char* StringsProxy::c_str() {
return set_string.c_str();
}
__attribute__((visibility("default")))
extern "C" const uintptr_t* StringsProxy::ptr() {
return reinterpret_cast<uintptr_t *>(&set_string);
}
__attribute__((visibility("default")))
extern "C" StringsProxy::~StringsProxy() {
}
StringsProxy.h
#ifndef __STRINGSPROXY_H__
#define __STRINGSPROXY_H__
#include <string>
typedef std::basic_string<char> proxy_string;
class StringsProxy
{
public:
/* Initialize StringsProxy with a pointer to an existing string */
StringsProxy(uintptr_t str);
/* Initialize StringsProxy with a new string */
StringsProxy(const char* str);
/* Get C string */
virtual const char* c_str();
/* Get pointer to string for injection */
const virtual uintptr_t* ptr();
private:
proxy_string set_string;
};
#endif
Compile this into a shared object using the old NDK with -DCMAKE_ANDROID_STL_TYPE=gnustl_static
Then link this shared object to the hooking program (in CMakeLists):
target_link_libraries(${TARGET_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/abiproxy/build/armeabi-v7a/libabiproxy.so)
Then in the hooking program, can be used like this:
#include "abiproxy/StringsProxy.h"
void *setUriDebug(uintptr_t a1, uintptr_t stri) {
auto y = StringsProxy(stri);
LOGI("URI CALLED %s", y.c_str());
return setUriDebugOld(a1, stri);
}
Or in reverse:
StringsProxy assetNameBaseProxy = StringsProxy("https://example.com/");
void setResourceUrl(uintptr_t* a1, int a2) {
*(a1 + 1) = *assetNameBaseProxy.ptr();
}
This isn't by any means a good solution, but it works for my use-case.
I am following a tutorial on NDK development using C++. The app is a basic fibonacci number printing app. I have the appropriate System.loadLibrary and the JNI_OnLoad call. I can see in the logcat that the library is getting loaded. However, post that, the system still looks for methods based on the package names. Heres the error from logcat .
No implementation found for long com.test.fib.FibLib.fibN(long) (tried Java_com_test_fib_FibLib_fibN and Java_com_test_fib_FibLib_fibN__J)
And here is the class where I have the cpp code and related stuff .
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <android/log.h>
#include <jni.h>
#define LOG_TAG "Fibonacci Stuff"
#define LOG_D(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
namespace com_test_fib {
static long fib(long n) {
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1) + fib(n-2);
}
/* JNI wrapper */
static jlong fibN(JNIEnv* env, jclass clazz, jlong n) {
return fib(n);
}
static JNINativeMethod method_table[] = {
{"fibN", "(J)J", (void *) fibN }
};
}
using namespace com_test_fib;
extern "C" jint JNI_Onload(JavaVM* vm, void* reserved) {
LOG_D ("JNI_OnLoad");
JNIEnv* env;
if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK ) {
LOG_D("Initial Get env error");
return JNI_ERR;
}
else {
jclass clazz = env->FindClass("com/test/fib/FibLib");
LOG_D ("Find the class");
if(clazz) {
LOG_D ("Class not found");
jint ret = env->RegisterNatives(clazz, method_table, sizeof(method_table)/sizeof(method_table[0]) );
env->DeleteLocalRef(clazz);
return ret ==0 ? JNI_VERSION_1_6 : JNI_ERR;
} else {
LOG_D("Some error it seems");
return JNI_ERR;
}
}
}
Here is the loadLibrary call
package com.test.fib;
import android.util.Log;
public class FibLib {
public static long fibJ(long n) {
if(n==0) {
return 0;
}
if(n==1) {
return 1;
}
return fibJ(n-1) + fibJ(n-2);
}
/* Wrapper to call the JNI code. This is called by the activity */
public static native long fibN(long n);
static {
Log.d("Sys library loading", "This should call the onLoad function");
System.loadLibrary("Fib");
}
}
I can see the above message in Logcat. After that i directly see the exceptions and no messages from the onLoad method. I am using eclipse, min version is 14 and compile version is 5.1.1. Device is nexus 7. The package names are matching com.test.fib) in the app and the namespace.
Can anyone please let me know whats wrong here. THis is driving me mad..
Thanks
I have a.so which defines void a() and b.so which defines void b(). They are both put in the .apk so they are available to the Android application.
Now suppose that I'm calling a() through JNI. Is it possible to call b() from a() while completely bypassing JNI?
Can I do it this way in Android (the code is only for illustration, so it might have some errors)?
void a() {
void *handle = dlopen("b.so", RTLD_LAZY);
void (*b)() = dlsym(handle, "b");
b();
}
Would I need to add the fully qualified path, or is b.so already in LD_LIBRARY_PATH for the app?
You can do it this way on Android, though take care of where the shared library has been put in Android folders. It can change from a version to another.
On api 17 for example, it remains in /data/app-lib/. You can hardwrite it, but the best is to make calls to Java (through JNI) to know where the libraries should be.
We're doing something like this in our project :
JNIEnv* env;
const char* temp;
jobject oActivity = state->activity->clazz;
jclass cActivity = env->GetObjectClass(oActivity);
// get the path to where android extracts native libraries to
jmethodID midActivityGetApplicationInfo = env->GetMethodID(cActivity, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
jobject oApplicationInfo = env->CallObjectMethod(oActivity, midActivityGetApplicationInfo);
jclass cApplicationInfo = env->GetObjectClass(oApplicationInfo);
jfieldID fidApplicationInfoNativeLibraryDir = env->GetFieldID(cApplicationInfo, "nativeLibraryDir", "Ljava/lang/String;");
jstring sNativeLibraryDir = (jstring)env->GetObjectField(oApplicationInfo, fidApplicationInfoNativeLibraryDir);
temp = env->GetStringUTFChars(sNativeLibraryDir, NULL);
strcpy(libpath, temp);
strcat(libpath, "/");
Then you push your dlopen + dlsym combo in the fight and it should work.
As mentioned here : How do I load a shared object in C++?
There are two ways of loading shared objects in C++
For either of these methods you would always need the header file for the object you want to use. The header will contain the definitions of the classes or objects you want to use in your code.
#include "blah.h"
int main()
{
ClassFromBlah a;
a.DoSomething();
}
gcc yourfile.cpp -lblah
Dynamically (In Linux):
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen ("libm.so", RTLD_LAZY);
if (!handle) {
fprintf (stderr, "%s\n", dlerror());
exit(1);
}
dlerror(); /* Clear any existing error */
cosine = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fprintf (stderr, "%s\n", error);
exit(1);
}
printf ("%f\n", (*cosine)(2.0));
dlclose(handle);
return 0;
}
PS : for the dynamic approach, it depends on platform : on Linux, you use dlopen, on windows, you use LoadLibrary.
EDIT: I've solved this problem (and found the next issue in the long line of JNI tribulations!) by changing the following:
Delete "static" declaration of the native method in both Java and C++, add a Java method to get an instance of my SimpleGame class, and calling
public static void callCppApiResponse (String result, String token, long context) {
**getInstance()**.cppAndroidApiResponse(token, 200, result, result.length(), context);
}
Now the information successfully makes it from Java to C++. Hopefully this may help someone else with a similar issue.
ORIGINAL POST:
I'm using the Cocos2d-x platform and writing C++ code for a cross-platform iOS/Android app, and there are certain methods that I need to be handled by the respective native iOs/Android systems, so I need to be able to call Java from C++ then call C++ from Java.
I am able to load the shared library for the app and trigger a Java response from C++, but when I try to call C++ from Java I get the following logcat output and the app crashes:
dalvik vm No implementation found for native Lorg/cocos2dx/simplegame/SimpleGame; cppSideAndroidApiResponse:(Ljava/lang/String;ILJava/lang/String;IJ)V
AndroidRuntime java.lang.UnsatisfiedLinkError: Native method not found: org.cocos2dx.simplegame.SimpleGame.cppAndroidApiResponse:(Ljava/lang/String;ILJava/lang/String;IJ)V
Here is the relevant Java code:
package org.cocos2dx.simplegame;
private static native void cppAndroidApiResponse(String token, int response, String data, int dataLen, long context);
static {
System.loadLibrary("sb");
}
public static void callCppApiResponse (String result, String token, long context) {
//This is where the error is triggered
cppAndroidApiResponse(token, 200, result, result.length(), context);
}
and in the C++ file cppSide.h:
# if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
# if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JNIEXPORT static void JNICALL
Java_org_cocos2dx_simplegame_SimpleGame_cppAndroidApiResponse(JNIEnv *, jobject, jstring, jint, jstring, jint, jlong);
#endif
#ifdef __cplusplus
}
#endif
Then finally the cppSide.cpp file
#include "cppSide.h"
extern "C" {
JNIEXPORT static void JNICALL
Java_org_cocos2dx_simplegame_SimpleGame_cppAndroidApiResponse(JNIEnv *env, jobject obj, jstring token, jint response, jstring data, jint length, jlong context)
{ ...do stuff...}
}
I know that my shared library is loading because I get my C++ based loading screen, and I know that JNI is working at least partially because I am able to call Java from that same cppSide.cpp file in another method. I have been researching the JNI and Cocos2d-X documentation, but to no avail.
Thanks in advance!
I have some problems when using the dynamic loading API (<dlfcn.h>: dlopen(), dlclose(), etc) on Android.
I'm using NDK standalone toolchain (version 8) to compile the applications and libraries.
The Android version is 2.2.1 Froyo.
Here is the source code of the simple shared library.
#include <stdio.h>
int iii = 0;
int *ptr = NULL;
__attribute__((constructor))
static void init()
{
iii = 653;
}
__attribute__((destructor))
static void cleanup()
{
}
int aaa(int i)
{
printf("aaa %d\n", iii);
}
Here is the program source code which uses the mentioned library.
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
void *handle;
typedef int (*func)(int);
func bbb;
printf("start...\n");
handle = dlopen("/data/testt/test.so", RTLD_LAZY);
if (!handle)
{
return 0;
}
bbb = (func)dlsym(handle, "aaa");
if (bbb == NULL)
{
return 0;
}
bbb(1);
dlclose(handle);
printf("exit...\n");
return 0;
}
With these sources everything is working fine, but when I try to use some STL functions or classes, the program crashes with a segmentation fault, when the main() function exits, for example when using this source code for the shared library.
#include <iostream>
using namespace std;
int iii = 0;
int *ptr = NULL;
__attribute__((constructor))
static void init()
{
iii = 653;
}
__attribute__((destructor))
static void cleanup()
{
}
int aaa(int i)
{
cout << iii << endl;
}
With this code, the program crashes with segmentation fault after or the during main() function exit.
I have tried couple of tests and found the following results.
Without using of STL everything is working fine.
When use STL and do not call dlclose() at the end, everything is working fine.
I tried to compile with various compilation flags like -fno-use-cxa-atexit or -fuse-cxa-atexit, the result is the same.
What is wrong in my code that uses the STL?
Looks like I found the reason of the bug. I have tried another example with the following source files:
Here is the source code of the simple class:
myclass.h
class MyClass
{
public:
MyClass();
~MyClass();
void Set();
void Show();
private:
int *pArray;
};
myclass.cpp
#include <stdio.h>
#include <stdlib.h>
#include "myclass.h"
MyClass::MyClass()
{
pArray = (int *)malloc(sizeof(int) * 5);
}
MyClass::~MyClass()
{
free(pArray);
pArray = NULL;
}
void MyClass::Set()
{
if (pArray != NULL)
{
pArray[0] = 0;
pArray[1] = 1;
pArray[2] = 2;
pArray[3] = 3;
pArray[4] = 4;
}
}
void MyClass::Show()
{
if (pArray != NULL)
{
for (int i = 0; i < 5; i++)
{
printf("pArray[%d] = %d\n", i, pArray[i]);
}
}
}
As you can see from the code I did not used any STL related stuff.
Here is the source files of the functions library exports.
func.h
#ifdef __cplusplus
extern "C" {
#endif
int SetBabe(int);
int ShowBabe(int);
#ifdef __cplusplus
}
#endif
func.cpp
#include <stdio.h>
#include "myclass.h"
#include "func.h"
MyClass cls;
__attribute__((constructor))
static void init()
{
}
__attribute__((destructor))
static void cleanup()
{
}
int SetBabe(int i)
{
cls.Set();
return i;
}
int ShowBabe(int i)
{
cls.Show();
return i;
}
And finally this is the source code of the programm that uses the library.
main.cpp
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include "../simple_lib/func.h"
int main()
{
void *handle;
typedef int (*func)(int);
func bbb;
printf("start...\n");
handle = dlopen("/data/testt/test.so", RTLD_LAZY);
if (!handle)
{
printf("%s\n", dlerror());
return 0;
}
bbb = (func)dlsym(handle, "SetBabe");
if (bbb == NULL)
{
printf("%s\n", dlerror());
return 0;
}
bbb(1);
bbb = (func)dlsym(handle, "ShowBabe");
if (bbb == NULL)
{
printf("%s\n", dlerror());
return 0;
}
bbb(1);
dlclose(handle);
printf("exit...\n");
return 0;
}
Again as you can see the program using the library also does not using any STL related stuff, but after run of the program I got the same segmentation fault during main(...) function exit. So the issue is not connected to STL itself, and it is hidden in some other place. Then after some long research I found the bug.
Normally the destructors of static C++ variables are called immediately before main(...) function exit, if they are defined in main program, or if they are defined in some library and you are using it, then the destructors should be called immediately before dlclose(...).
On Android OS all destructors(defined in main program or in some library you are using) of static C++ variables are called during main(...) function exit. So what happens in our case? We have cls static C++ variable defined in library we are using. Then immediately before main(...) function exit we call dlclose(...) function, as a result library closed and cls becomes non valid. But the pointer of cls is stored somewhere and it's destructor should be called during main(...) function exit, and because at the time of call it is already invalid, we get segmentation fault. So the solution is to not call dlclose(...) and everything should be fine. Unfortunately with this solution we cannot use attribute((destructor)) for deinitializing of something we want to deinitialize, because it is called as a result of dlclose(...) call.
I have a general aversion to calling dlclose(). The problem is that you must ensure that nothing will try to execute code in the shared library after it has been unmapped, or you will get a segmentation fault.
The most common way to fail is to create an object whose destructor is defined in or calls code defined in the shared library. If the object still exists after dlclose(), your app will crash when the object is deleted.
If you look at logcat you should see a debuggerd stack trace. If you can decode that with the arm-eabi-addr2line tool you should be able to determine if it's in a destructor, and if so, for what class. Alternatively, take the crash address, strip off the high 12 bits, and use that as an offset into the library that was dlclose()d and try to figure out what code lives at that address.
I encountered the same headache on Linux. A work-around that fixes my segfault is to put these lines in the same file as main(), so that dlclose() is called after main returns:
static void* handle = 0;
void myDLClose(void) __attribute__ ((destructor));
void myDLClose(void)
{
dlclose(handle);
}
int main()
{
handle = dlopen(...);
/* ... real work ... */
return 0;
}
The root cause of dlclose-induced segfault may be that a particular implementation of dlclose() does not clean up the global variables inside the shared object.
You need to compile with -fpic as a compiler flag for the application that is using dlopen() and dlclose(). You should also try error handling via dlerror() and perhaps checking if the assignment of your function pointer is valid, even if it's not NULL the function pointer could be pointing to something invalid from the initialization, dlsym() is not guaranteed to return NULL on android if it cannot find a symbol. Refer to the android documentation opposed to the posix compliant stuff, not everything is posix compliant on android.
You should use extern "C" to declare you function aaa()