Запуск через valgrind, похоже, увеличивает (но не гарантирует) шансы на успех. Он также генерирует массу предупреждений (среди прочего, неверные записи под указателем стека).
Я пробовал это только в Linux, но с разными версиями Java, в основном с openjdk-25. Связывание осуществляется с помощью мезона (
Код: Выделить всё
dependency('jni')Плагин загружается с помощью dlopen(plugin, RTLD_NOW|RTLD_GLOBAL). На самом деле не имеет значения, для чего это плагин. Я попытался создать крошечную программу, которая просто загружала бы и мой плагин. (Считайте это концепцией написания плагинов на Java для приложений, написанных на C.)
Конечно, я позаботился о том, чтобы JavaVMInitArgs не содержал случайных байтов. И что jvm и env имеют значение NULL перед вызовом JNI_CreateJavaVM().
Я также пытался выполнить вызов в потоке с большим стеком. Я попытался увеличить стек с помощью setrlimit(). И я попробовал несколько разных вариантов JavaVMINitArgs. Любое такое изменение может привести к временной работе кода. Но тогда любое несвязанное изменение может снова перестать работать.
Есть предложения? Или JNI не должен работать в таких условиях. В таком случае, почему?
Изменить:
Это крошечная программа, которая загружает плагин:
Код: Выделить всё
#include
#include
int main(int argc, char **argv){
void *plugin;
int(*entry_point)();
plugin = dlopen("./plugin.so", RTLD_NOW|RTLD_GLOBAL);
if(!plugin){
fprintf(stderr, "dlopen() failed: %s\n", dlerror());
return 1;
}
entry_point = dlsym(plugin, "entry_point");
if(!entry_point){
fprintf(stderr, "dlsym() failed\n");
return 1;
}
entry_point();
entry_point();
printf("Back without crashing!\n");
return 0;
}
Код: Выделить всё
#include
#include
#include
static JavaVM *jvm;
int entry_point(){
JNIEnv *env;
if(!jvm){
JavaVMInitArgs args;
env = NULL;
memset(&args, 0, sizeof(JavaVMInitArgs));
args.version = JNI_VERSION_1_6;
args.nOptions = 0;
int result;
result = JNI_CreateJavaVM(&jvm, (void**)&env, &args);
if(result < 0){
fprintf(stderr, "JNI_CreateJavaVM() failed\n");
return result;
}
}else{
fprintf(stderr, "JVM already started by this plugin\n");
}
/* Other code that works if it gets this far. */
return 0;
}
Мне сказали, что JVM требует много места в стеке для работы (кто-то предложил не менее 16 МБ), поэтому я попробовал различные способы увеличения стека.
Мне также сказали, что JVM «очень чувствительна» к пользовательским обработчикам сигналов. Отсутствие возможности устанавливать обработчики сигналов неоптимально. Но отказ от обработки сигналов также не решает проблему.
И, наконец, мне сказали, что мне не следует даже пытаться это сделать, поскольку мне явно не хватает более глубокого понимания внутренней работы JVM. Это говорит о том, что здесь замешана какая-то черная магия. Но что вам действительно нужно знать, чтобы выполнить, казалось бы, простой вызов функции?
Редактировать 2:
Это файл сборки мезона для создания кода:
Код: Выделить всё
project('test', ['c', 'java'])
jni_dep = dependency('jni', modules: 'jvm', required: true, method: 'cmake')
x = run_command('java', '-XshowSettings:properties', '-version', check: false)
foreach line : x.stderr().split('\n')
tok = line.split('=')
if tok[0].strip() == 'java.home'
java_home = tok[1].strip()
x = run_command('find', java_home, '-name', 'libjvm.so', check: false).stdout().strip()
java_runpath = import('fs').parent(x)
break
endif
endforeach
executable('loader',
'loader.c')
shared_library('plugin',
'plugin.c',
name_prefix: '',
build_rpath: java_runpath,
dependencies: jni_dep)
Код: Выделить всё
cc -Iloader.p -I. -I.. -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -O0 -g -MD -MQ loader.p/loader.c.o -MF loader.p/loader.c.o.d -o loader.p/loader.c.o -c ../loader.c
cc -o loader loader.p/loader.c.o -Wl,--as-needed -Wl,--no-undefined
cc -Iplugin.so.p -I. -I.. -I/etc/java-config-2/current-system-vm/include -I/etc/java-config-2/current-system-vm/include/linux -fdiagnostics-color=always -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -O0 -g -fPIC -MD -MQ plugin.so.p/plugin.c.o -MF plugin.so.p/plugin.c.o.d -o plugin.so.p/plugin.c.o -c ../plugin.c
cc -o plugin.so plugin.so.p/plugin.c.o -Wl,--as-needed -Wl,--no-undefined -shared -fPIC -Wl,-soname,plugin.so -Wl,-rpath,/usr/lib64/openjdk-25/lib/server /etc/java-config-2/current-system-vm/lib/server/libjvm.so
Компилятор C Я использую GCC 15.2.1 (я также пробовал несколько старых версий GCC с тем же результатом). Точная версия JDK, которую я использую, — OpenJDK 25.0.2 (я также пробовал OpenJDK 8.462 и Icedtea 3.21 и, по крайней мере, еще одну, точно не помню, с тем же результатом).
Мобильная версия