Попытка вызвать JNI_CreateJavaVM из libart.so не удалась - PullRequest
0 голосов
/ 24 мая 2019

Я работаю над приложением Xamarin.Android с частью C ++. Теперь мне нужно напрямую вызывать Java-интерфейсы Android из библиотеки C ++.

Я скопировал код из Подробного и очень полезного сообщения в блоге Калеба Фентона , в котором используется JNI для вызова из C ++ в Java . Но я не могу получить указатель на JVM так же, как он это делает.

(Кстати, я в основном программист на C #, поэтому вполне возможно, что я допустил элементарную ошибку в C ++).

В заголовочном файле:

 #pragma once
class MyJniClass
{
    //Create this once and cache it.
    JavaVM *m_jvm;                      // Pointer to the JVM (Java Virtual Machine)
    JNIEnv *m_env;                      // Pointer to native interface
    bool init_jvm();
}

В файле .cpp:

    #include <jni.h>
#include <dlfcn.h>
#include "MyJniClass.h"

typedef int(*JNI_CreateJavaVM_t)(void *, void *, void *);


/**Code is based on https://github.com/rednaga/native-shim/blob/master/vm.c  
*/
bool MyJniClass::init_jvm() 
{
    // https://android.googlesource.com/platform/frameworks/native/+/ce3a0a5/services/surfaceflinger/DdmConnection.cpp
    JavaVMOption opt[1];
    opt[0].optionString = "-Djava.class.path=."; // I added a small java class to the dll to which this C++ class is linked, 
                                                 //so that there would be a java class in the current directory.  

    //opt/*[1]*/.optionString = "-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y";


    JavaVMInitArgs args;
    args.version = JNI_VERSION_1_6;
    args.options = opt;
    args.nOptions = 1;
    args.ignoreUnrecognized = JNI_FALSE;

    void *libart_dso = dlopen("libart.so", RTLD_NOW); //libdvm.so is outdated,  libnativehelper.so doesn't work

    if (!libart_dso ) 
    {
        //Execution doesn't pass through here 
        return false;
    }

    //Try to get the JNI_CreateJavaVM function pointer
    JNI_CreateJavaVM_t JNI_CreateJavaVM;
    JNI_CreateJavaVM = (JNI_CreateJavaVM_t)dlsym(libart_dso, "JNI_CreateJavaVM");
    if (!JNI_CreateJavaVM) 
    {
        //Execution doesn't pass through here 
        return false;
    }

    signed int result = JNI_CreateJavaVM(&(m_jvm), &(m_env), &args);

    if ( result != 0)
    {
        ostringstream os;
        os << "Call to JNI_CreateJavaVM returned ";
        os << result;
        m_logger->writeEntry(Loglevel::debug, os.str()); // ===> Here, I can see that result is always -1
        return false;
    }

    return true;
}

Я попытался найти функцию JNI_CreateJavaVM в исходном коде ART здесь , но не смог ее найти. Но, конечно, это должно быть там, чтобы dlsym мог найти функцию? Я думаю, что мне нужно искать дальше, чтобы найти исходный код для libart.so.

Что я делаю не так, что я не могу получить действительный вызов JNI_CreateJavaVM?

Ответы [ 2 ]

0 голосов
/ 25 мая 2019

Похоже, проблема связана с библиотекой, которая предоставляет JNI_CreateJavaVM. Я имею в виду эту строку

void *libart_dso = dlopen("libart.so", RTLD_NOW); 

Если я удаляю ваш код из всего, что не связано с JVM, и если я использую JDK на основе macOS, он работает просто отлично.

--- 8< CUT HERE 8< ----
#include <jni.h>
#include <dlfcn.h>
#include <iostream>
#include "MyJniClass.h"

using namespace std;

bool MyJniClass::init_jvm()
{
    JavaVM *jvm;
    JNIEnv *env = NULL;
    JavaVMOption opt[1];
    opt[0].optionString = "-Djava.class.path=.";

    JavaVMInitArgs args;
    args.version = JNI_VERSION_1_6;
    args.options = opt;
    args.nOptions = 1;
    args.ignoreUnrecognized = JNI_FALSE;

    int status = JNI_CreateJavaVM (&jvm, (void **) &env, &args);

    cout << status << endl;

    return true;
}

int main(int argc, char **argv) {
  MyJniClass jni;
  jni.init_jvm();
}
--- 8< CUT HERE 8< ----
--- 8< CUT HERE 8< ----
#pragma once
class MyJniClass
{
    //Create this once and cache it.
    JavaVM *m_jvm;                      // Pointer to the JVM (Java Virtual Machine)
    JNIEnv *m_env;                      // Pointer to native interface
    public: bool init_jvm();
};
--- 8< CUT HERE 8< ----

и затем я компилирую код:

g++ -o lib/recipeNo027_main c/recipeNo027_main.c \
        -I/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/include \
        -I/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/include/darwin/ \
    -rpath -L/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/lib/server -ljvm

(это основано на recipeNo027 отсюда: http://jnicookbook.owsiak.org/recipe-no-027/)

Я могу запустить его без проблем

> lib/recipeNo027_main
0

Похоже, что-то подозрительное происходит внутри вашей реализации JNI_CreateJavaVM.

0 голосов
/ 24 мая 2019

первой модификацией здесь будет добавление опции диагностики Xcheck:jni. Это предоставит детали в случае ошибок. Добавьте еще один параметр, изменив JavaVMOption opt[1]; на JavaVMOption opt[2]; а затем добавьте следующую опцию: opt[1].optionString = "-Xcheck:jni".

Кроме того, dll должна быть загружена из своего исходного местоположения (так как задействованы другие библиотеки DLL), а не из каталога вашего проекта. Более подробная информация представлена ​​в следующем посте: JNI_CreateJavaVM () завершается с кодом выхода 1

Наконец, вы должны привести указатель к собственному интерфейсу JNIEnv, изменив:

signed int result = JNI_CreateJavaVM(&(m_jvm), &(m_env), &args);

до

signed int result = JNI_CreateJavaVM(&(m_jvm), (void**)&(m_env), &args);

Это должно решить проблему.

...