我可以阻止 JLI_Launch 呼叫来电者的 "int main (int argc, char *argv[])" 吗?
Can I prevent JLI_Launch from calling the caller's "int main (int argc, char *argv[])"?
在调用 JLI_Launch
之前我可以做一些简单的事情来防止它的 macOS 引导行为(在 linux 上不会发生)吗?还是我必须继续阅读有关 invocation api 的更多信息并使用 JLI_Launch
以外的内容(例如 JNI_CreateJavaVM
)?如果你想知道为什么这么复杂的启动器,如果我不使用它的架构(不创建子进程),然后将文件拖放到应用程序的图标上(停靠或未停靠)打开应用程序,但应用程序确实 没有看到文件放置事件。
JLI_Launch
的行为就像代码看起来像这样:
static int callCount = 0;
int JLI_Launch (/* parameters */) {
if (callCount == 0) {
callCount = 1;
/* figure out what to tell the caller by creating argc,argv */
main(argc, argv);
} else {
callCount = 0;
// launch the VM
}
return 0;
}
调用dlopen
和dlsym
后调用JLI_Launch
的appbundler project has code。 JLI_Launch
函数然后调用 appbundler 的 main
重复所有内容,使用相同的参数再次调用 JLI_Launch
。第二次调用JLI_Launch
,虚拟机运行java代码。
除了第二次执行准备代码时可能发生的一些奇怪的灾难(例如,选择 Java 的版本而不是第一次执行时选择的版本),执行的代码量准备调用 JLI_Launch
很重要。为相同的结果执行两次是浪费。
这是 MCV 示例。我知道它是 C 而不是 Objective-C,但这部分无关紧要。至少它似乎并不重要,因为它重现了我认为不受欢迎的“自举”行为。
#include <stdio.h>
#include <dlfcn.h>
#include "jni.h"
int main ( int argc, char *argv[] ) {
// The code required to identify jargv is somewhat significant.
char * jargv[] = { "test.exe", "Test", "Hello, world." };
int jargc = sizeof(jargv) / sizeof(jargv[0]);
// The code required to identify lib is significant.
char * lib = "/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/lib/jli/libjli.dylib";
void * h = dlopen(lib, RTLD_LAZY);
void (*launcher)() = dlsym(h, "JLI_Launch");
puts("calling JLI_Launch");
launcher(jargc, jargv,
0, NULL, 0, NULL,
"", "", "java", "java",
JNI_FALSE, JNI_FALSE, JNI_FALSE,
(jint) 0);
return 0;
}
这是它将要执行的 java 代码:
public class Test {
public static void main (String[] args) {
System.out.println(args[0]);
}
}
CLG:
$ gcc -o test.exe -g -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin test.c && ./test.exe
calling JLI_Launch
calling JLI_Launch
Hello, world.
这里有更多使用调试器的证据:
$ lldb test.exe
(lldb) target create "test.exe"
Current executable set to '.../test.exe' (x86_64).
(lldb) b main
Breakpoint 1: where = test.exe`main + 40 at test.c:6:12, address = 0x0000000100003da8
(lldb) r
Process 27252 launched: '.../test.exe' (x86_64)
Process 27252 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003da8 test.exe`main(argc=1, argv=0x00007ffeefbfeb00) at test.c:6:12
3 #include "jni.h"
4
5 int main ( int argc, char *argv[] ) {
-> 6 char * jargv[] = { "test.exe", "Test", "Hello, world." };
^
7 int jargc = sizeof(jargv) / sizeof(jargv[0]);
8 char * lib = "/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/lib/jli/libjli.dylib";
9 void * h = dlopen(lib, RTLD_LAZY);
Target 0: (test.exe) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000100003da8 test.exe`main(argc=1, argv=0x00007ffeefbfeb00) at test.c:6:12
frame #1: 0x00007fff20659621 libdyld.dylib`start + 1
(lldb) c
Process 27252 resuming
22 locations added to breakpoint 1
calling JLI_Launch
Process 27252 stopped
* thread #2, stop reason = breakpoint 1.1
frame #0: 0x0000000100003da8 test.exe`main(argc=3, argv=0x0000000100307a70) at test.c:6:12
3 #include "jni.h"
4
5 int main ( int argc, char *argv[] ) {
-> 6 char * jargv[] = { "test.exe", "Test", "Hello, world." };
^
7 int jargc = sizeof(jargv) / sizeof(jargv[0]);
8 char * lib = "/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/lib/jli/libjli.dylib";
9 void * h = dlopen(lib, RTLD_LAZY);
Target 0: (test.exe) stopped.
(lldb) bt
* thread #2, stop reason = breakpoint 1.1
* frame #0: 0x0000000100003da8 test.exe`main(argc=3, argv=0x0000000100307a70) at test.c:6:12
frame #1: 0x000000010015e31f libjli.dylib`apple_main + 84
frame #2: 0x00007fff2063e950 libsystem_pthread.dylib`_pthread_start + 224
frame #3: 0x00007fff2063a47b libsystem_pthread.dylib`thread_start + 15
(lldb)
后记
启动器代码不正常的一个线索是没有任何包含字符串 JLI_
的头文件。我只能找到 JNI_
函数定义,并且只能在 jni.h
.
中找到
“自举”的另一个线索是 JLI_Launch
使用 _start
使用的不同 argc
和 argv
调用 main
。但我不明白为什么我会关心,因为代码两次向 JLI_Launch
发送完全相同的值。如果我认为反馈是必要的,那么我会接受它,但我认为没有。
另一个线索是,在 linux 上,至少在 JDK 1.8.0_121 中,这种行为不存在。我不得不做一些意想不到的事情来让它工作:
1. call the executable bin/java instead of test.exe
2. soft link the JDK lib folder
显然,在 linux 上,java 调用 dlopen 到 link 它的库,基于其对主文件夹的了解。
查看我发布的回溯后,我意识到两件事:
1. I see the name of the calling function, `apple_main`
2. I might be able to find its definition if I download Java SE 16's source code
我在src/java.base/macosx/native/libjli/java_md_macosx.m
中找到了apple_main
的定义:
/*
* Unwrap the arguments and re-run main()
*/
static void *apple_main (void *arg)
{
if (main_fptr == NULL) {
#ifdef STATIC_BUILD
extern int main(int argc, char **argv);
main_fptr = &main;
#else
main_fptr = (int (*)())dlsym(RTLD_DEFAULT, "main");
#endif
if (main_fptr == NULL) {
JLI_ReportErrorMessageSys("error locating main entrypoint\n");
exit(1);
}
}
struct NSAppArgs *args = (struct NSAppArgs *) arg;
exit(main_fptr(args->argc, args->argv));
}
对 pthread_start
的调用隐藏(根据定义)一些调用序列(以堆栈形式显示),即:
test.c: main (new main thread)
src/java.base/macosx/native/libjli/java_md_macosx.m: apple_main (new main thread)
src/java.base/macosx/native/libjli/java_md_macosx.m: MacOSXStartup (main thread)
src/java.base/macosx/native/libjli/java_md_macosx.m: CreateExecutionEnvironment (main thread)
src/java.base/share/native/libjli/java.c: JLI_Launch (main thread)
test.c: main (main thread)
我现在可以继续了。
在调用 JLI_Launch
之前我可以做一些简单的事情来防止它的 macOS 引导行为(在 linux 上不会发生)吗?还是我必须继续阅读有关 invocation api 的更多信息并使用 JLI_Launch
以外的内容(例如 JNI_CreateJavaVM
)?如果你想知道为什么这么复杂的启动器,如果我不使用它的架构(不创建子进程),然后将文件拖放到应用程序的图标上(停靠或未停靠)打开应用程序,但应用程序确实 没有看到文件放置事件。
JLI_Launch
的行为就像代码看起来像这样:
static int callCount = 0;
int JLI_Launch (/* parameters */) {
if (callCount == 0) {
callCount = 1;
/* figure out what to tell the caller by creating argc,argv */
main(argc, argv);
} else {
callCount = 0;
// launch the VM
}
return 0;
}
调用dlopen
和dlsym
后调用JLI_Launch
的appbundler project has code。 JLI_Launch
函数然后调用 appbundler 的 main
重复所有内容,使用相同的参数再次调用 JLI_Launch
。第二次调用JLI_Launch
,虚拟机运行java代码。
除了第二次执行准备代码时可能发生的一些奇怪的灾难(例如,选择 Java 的版本而不是第一次执行时选择的版本),执行的代码量准备调用 JLI_Launch
很重要。为相同的结果执行两次是浪费。
这是 MCV 示例。我知道它是 C 而不是 Objective-C,但这部分无关紧要。至少它似乎并不重要,因为它重现了我认为不受欢迎的“自举”行为。
#include <stdio.h>
#include <dlfcn.h>
#include "jni.h"
int main ( int argc, char *argv[] ) {
// The code required to identify jargv is somewhat significant.
char * jargv[] = { "test.exe", "Test", "Hello, world." };
int jargc = sizeof(jargv) / sizeof(jargv[0]);
// The code required to identify lib is significant.
char * lib = "/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/lib/jli/libjli.dylib";
void * h = dlopen(lib, RTLD_LAZY);
void (*launcher)() = dlsym(h, "JLI_Launch");
puts("calling JLI_Launch");
launcher(jargc, jargv,
0, NULL, 0, NULL,
"", "", "java", "java",
JNI_FALSE, JNI_FALSE, JNI_FALSE,
(jint) 0);
return 0;
}
这是它将要执行的 java 代码:
public class Test {
public static void main (String[] args) {
System.out.println(args[0]);
}
}
CLG:
$ gcc -o test.exe -g -I $JAVA_HOME/include -I $JAVA_HOME/include/darwin test.c && ./test.exe
calling JLI_Launch
calling JLI_Launch
Hello, world.
这里有更多使用调试器的证据:
$ lldb test.exe
(lldb) target create "test.exe"
Current executable set to '.../test.exe' (x86_64).
(lldb) b main
Breakpoint 1: where = test.exe`main + 40 at test.c:6:12, address = 0x0000000100003da8
(lldb) r
Process 27252 launched: '.../test.exe' (x86_64)
Process 27252 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003da8 test.exe`main(argc=1, argv=0x00007ffeefbfeb00) at test.c:6:12
3 #include "jni.h"
4
5 int main ( int argc, char *argv[] ) {
-> 6 char * jargv[] = { "test.exe", "Test", "Hello, world." };
^
7 int jargc = sizeof(jargv) / sizeof(jargv[0]);
8 char * lib = "/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/lib/jli/libjli.dylib";
9 void * h = dlopen(lib, RTLD_LAZY);
Target 0: (test.exe) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000100003da8 test.exe`main(argc=1, argv=0x00007ffeefbfeb00) at test.c:6:12
frame #1: 0x00007fff20659621 libdyld.dylib`start + 1
(lldb) c
Process 27252 resuming
22 locations added to breakpoint 1
calling JLI_Launch
Process 27252 stopped
* thread #2, stop reason = breakpoint 1.1
frame #0: 0x0000000100003da8 test.exe`main(argc=3, argv=0x0000000100307a70) at test.c:6:12
3 #include "jni.h"
4
5 int main ( int argc, char *argv[] ) {
-> 6 char * jargv[] = { "test.exe", "Test", "Hello, world." };
^
7 int jargc = sizeof(jargv) / sizeof(jargv[0]);
8 char * lib = "/Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home/jre/lib/jli/libjli.dylib";
9 void * h = dlopen(lib, RTLD_LAZY);
Target 0: (test.exe) stopped.
(lldb) bt
* thread #2, stop reason = breakpoint 1.1
* frame #0: 0x0000000100003da8 test.exe`main(argc=3, argv=0x0000000100307a70) at test.c:6:12
frame #1: 0x000000010015e31f libjli.dylib`apple_main + 84
frame #2: 0x00007fff2063e950 libsystem_pthread.dylib`_pthread_start + 224
frame #3: 0x00007fff2063a47b libsystem_pthread.dylib`thread_start + 15
(lldb)
后记
启动器代码不正常的一个线索是没有任何包含字符串 JLI_
的头文件。我只能找到 JNI_
函数定义,并且只能在 jni.h
.
“自举”的另一个线索是 JLI_Launch
使用 _start
使用的不同 argc
和 argv
调用 main
。但我不明白为什么我会关心,因为代码两次向 JLI_Launch
发送完全相同的值。如果我认为反馈是必要的,那么我会接受它,但我认为没有。
另一个线索是,在 linux 上,至少在 JDK 1.8.0_121 中,这种行为不存在。我不得不做一些意想不到的事情来让它工作:
1. call the executable bin/java instead of test.exe
2. soft link the JDK lib folder
显然,在 linux 上,java 调用 dlopen 到 link 它的库,基于其对主文件夹的了解。
查看我发布的回溯后,我意识到两件事:
1. I see the name of the calling function, `apple_main`
2. I might be able to find its definition if I download Java SE 16's source code
我在src/java.base/macosx/native/libjli/java_md_macosx.m
中找到了apple_main
的定义:
/*
* Unwrap the arguments and re-run main()
*/
static void *apple_main (void *arg)
{
if (main_fptr == NULL) {
#ifdef STATIC_BUILD
extern int main(int argc, char **argv);
main_fptr = &main;
#else
main_fptr = (int (*)())dlsym(RTLD_DEFAULT, "main");
#endif
if (main_fptr == NULL) {
JLI_ReportErrorMessageSys("error locating main entrypoint\n");
exit(1);
}
}
struct NSAppArgs *args = (struct NSAppArgs *) arg;
exit(main_fptr(args->argc, args->argv));
}
对 pthread_start
的调用隐藏(根据定义)一些调用序列(以堆栈形式显示),即:
test.c: main (new main thread)
src/java.base/macosx/native/libjli/java_md_macosx.m: apple_main (new main thread)
src/java.base/macosx/native/libjli/java_md_macosx.m: MacOSXStartup (main thread)
src/java.base/macosx/native/libjli/java_md_macosx.m: CreateExecutionEnvironment (main thread)
src/java.base/share/native/libjli/java.c: JLI_Launch (main thread)
test.c: main (main thread)
我现在可以继续了。