Android C++: uncaught exception from loaded shared library

Viewed 1104

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

1 Answers

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