JNI Linux segmentation fault

倾然丶 夕夏残阳落幕 提交于 2019-12-11 16:19:32

问题


My JNI library works flawlessly on Windows, however, on Linux I always get a strange segmentation fault.

siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000000

The stack crace from the crash file is this:

C  [libfmodjavaL.so+0xfb8c]  JNIEnv_::GetStaticObjectField(_jclass*, _jfieldID*)+0x18
C  [libfmodjavaL.so+0xf72b]  Logger::sendToSystemOut(bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)+0x75
C  [libfmodjavaL.so+0xf7c2]  Logger::log(char const*)+0x4c
C  [libfmodjavaL.so+0xd70d]  fmodDebugCallback(unsigned int, char const*, int, char const*, char const*)+0x127

So it appears that it crashed when calling GetStaticObject field in the Logger class. This is that method:

void Logger::sendToSystemOut(bool error, std::string message) {
    JNIEnv* jni = FMODWrapper::utils->getJNI();

    jobject printStream;
    if (error) {
        printStream = jni->GetStaticObjectField(this->systemClass, this->errFieldID);
    } else {
        printStream = jni->GetStaticObjectField(this->systemClass, this->outFieldID);
    }

    jobject messageString = jni->NewStringUTF(message.c_str());
    jni->CallObjectMethod(printStream, this->printlnMethodID, messageString);
}

So I'm guessing something's not right about storing the class and field IDs of these fields. But the weird thing is, I get logging output when my library starts up, even from FMOD, which the fmodDebugCallback gets called by.

Logger::Logger(const char* name) {
    this->name = name;

    JNIEnv* jni = FMODWrapper::utils->getJNI();

    this->systemClass = FMODWrapper::utils->findClass("java/lang/System");
    this->outFieldID = jni->GetStaticFieldID(this->systemClass, "out", "Ljava/io/PrintStream;");
    this->errFieldID = jni->GetStaticFieldID(this->systemClass, "err", "Ljava/io/PrintStream;");

    jclass printStreamClass = FMODWrapper::utils->findClass("java/io/PrintStream");
    this->printlnMethodID = jni->GetMethodID(printStreamClass, "println", "(Ljava/lang/String;)V");
}

So, logging works flawlessly on Windows, but after some time crashes on Linux. Compiled with g++ on Fedora 29 64-bit.

Update: my method for getting a JNIEnv*

JNIEnv* Utils::getJNI() {
    JNIEnv* jni;

    int getEnvResult = FMODWrapper::jvm->GetEnv((void**) &jni, JNI_VERSION_1_6);

    if (getEnvResult == JNI_EDETACHED) {
        FMODWrapper::jvm->AttachCurrentThread(ANDROID_VOIDPP_CAST &jni, nullptr);
    }

    return jni;
}

Update 2: the code itself works up to a certain point since I'm getting log messages. Might be something to do with threads? https://hastebin.com/kuzefuwawu.txt


回答1:


systemClass, errFieldId, and outFieldID are all obtained from a different JNIEnv.

The JNIEnv cannot be cached: Keeping a global reference to the JNIEnv environment

Just as it cannot be cached, you cannot store ids that were obtained from the other JNIEnv that you should no longer be using, nor should you be using anything that came from it. You need to get them all from the current valid JNIEnv.




回答2:


The problem is not with thread affinity of class references or field IDs. The problem is with using a local class reference out of its scope. This is an implementation detail of some JVMs, that local references do not actually expire.

The fix would be to use

Logger::Logger(const char* name) {
    this->name = name;
    JNIEnv* jni = FMODWrapper::utils->getJNI();
    this->systemClass = jni->NewGlobalRef(jni->findClass("java/lang/System"));
    …


来源:https://stackoverflow.com/questions/55768112/jni-linux-segmentation-fault

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!