微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

我可以阻止 JLI_Launch 调用调用者的“int main (int argc, char *argv[])”吗? 后记

如何解决我可以阻止 JLI_Launch 调用调用者的“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;
}

appbundler projectcode,它在调用 JLI_Launchdlopen 之后调用 dlsymJLI_Launch 函数然后调用 appbundler 的 main,它重复所有内容,使用相同的参数再次调用 JLI_Launch。第二次调用 JLI_Launch 时,虚拟机运行 java 代码

除了在第二次执行准备代码时可能发生的一些奇怪的灾难(例如,选择了不同于第一次执行时选择的 Java 版本),为调用 {{ 1}} 很重要。为相同的结果执行两次是浪费。

这是 MCV 示例。我知道它是 C 而不是 Objective-C,但这部分无关紧要。至少它似乎并不重要,因为它重现了我认为不受欢迎的“引导”行为。

JLI_Launch

这是它将执行的java代码

#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,NULL,"","java",JNI_FALSE,(jint) 0);
    return 0;
}

CLG:

public class Test {
    public static void main (String[] args) {
        System.out.println(args[0]);
    }
}

以下是使用调试器的更多证明:

$ 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",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,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,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 不同的 mainargc 调用 argv。但是我不明白为什么我会关心,因为代码两次都向 _start 发送了完全相同的值。如果我认为反馈是必要的,那么我会接受它,但我认为没有。

一个线索是在 linux 上,至少在 JDK 1.8.0_121 上,这种行为不存在。我不得不做一些意想不到的事情才能让它发挥作用:

JLI_Launch

显然,在 linux 上,java 调用 dlopen 以根据其主文件夹的知识链接其库。

解决方法

在查看我发布的回溯后,我意识到两件事:

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

我在 apple_main 中找到了 src/java.base/macosx/native/libjli/java_md_macosx.m 的定义:

/*
 * 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)

我现在可以继续了。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。