卧槽。痛苦的一天。
因为需要读取 jar 文件,还用 C++ 语言QAQAQAQ,没有现成的库……我又不可能直接去解析……QAQ,于是就想到直接调用 Hotspot 写好的现成的 jar 文件库(我好聪明,逃。
然后就开始了日狗的一天。啥都没干,就一直在干动态库(。mac 的动态库 dylib 实在是气死了。但是这个设计也真是让人眼前一亮啊。学到了不少东西。
一开始只想要调用 Java 的 Native 方法
想写一个小型 jvm 的话,(虽然是玩具,不过我是不可能仅仅局限于只是跑一个 hello world 这种的。自然,想要打造一个运行时环境,那么就必须能够和原生java挂钩。这是目标,同样也是底线。不能让步,如果写完了之后连 java 的类库都不能调,可以狗带了。比如说日后我要是想要实现一个 C 语言的玩具编译器的话,那么不可能仅仅调用我自己底层写的 printf,一定要调用 stdio.h 中的 printf 才好啊。但是这样的话,就肯定涉及解释执行 C 宏了。因为 stdio.h 全都是宏,大家也不是不知道。当然……就我这辣鸡水平估计是要弃坑了……不过这是一年以后的话题了。我们先放着。和这一样,如果要写一个小型 jvm,那么就必须要支持 java 类库的调用才行。要不然就只能跑自己 XJB 写的不带任何库的 java 文件,一点意义也没有。因而,必须要调用 Native 方法。但是,这时就产生了一个问题啊。这个 Native 方法,每个 Native 是不是都是纯函数呢?这是个很重要的话题呢。如果不纯,我即使强行从动态库中把他们扒出来,调用也一定会出错。因为可能这个 Native 方法还涉及到他周边的上下文环境,而我只调用了这个方法,那么肯定会出错的。这个 blog 就是一个栗子。。因为要拆包 jar 文件,我模仿 hotspot 的调用方式调用了一波 jre lib,于是代码如下:
emmm。然后 mac 的动态库给我报错
|
|
这一段非常恶心。本质上原因是:由于你这个调用的动态库引用了别的动态库,如图:
我们能够发现这个动态库 libzip.dylib 引用了一堆动态库,有两个 libz.1.dylib 和 libSystem.B.dylib 是绝对路径,那么系统绝对不可能找不着。关键在于另三个:前边都有个 @rpath。
这个 @rpath 是执行的路径。貌似没法直接输出,只能通过强行指定才行。有的大神用 otool -l libzip.dylib
(注意 l 小写) 然后得到了如下:
大概能看出来后边的 LC_RPATH 的指定位置,是 @loader_path。也就是,应该是你的可执行文件调用这个库所在的位置。比如说,我在 /usr/tmp/haha
这个可执行文件中调用 <YourOpenJdk>/build/macosx-x86_64-normal-server-slowdebug/images/j2sdk-bundle/jdk1.8.0.jdk/Contents/Home/jre/lib/libzip.dylib
,那么八成这个 @loader_path
只是 /usr/tmp/haha
。然后由于 @rpath
是 @loader_path/.
,那么就一样。于是 libzip.dylib 引用的 libjvm.dylib 等共三个的目录变成了 /usr/tmp/libjvm.dylib
……应该是这样。为啥说是应该,因为我也没有验证过,存在错误的可能。不过八成是真的(逃。但是总之你是肯定索引不到真正的 libjvm.dylib 就是了,哈哈。
于是我们要引入 mac 的一个工具:install_name_tool
。这个工具可以在可执行文件/动态库中加入 @rpath。由于我们正在写程序,所以不可能通过程序把自己设置 rpath。。。所以,我们只能在程序内去修改 libzip.dylib 的 rpath 了。那么我们在刚才的 [1] 处添加几行代码:
这样就可以成功了 QAQ。我说的比较容易,其实花了我将近半天才弄完QAQ。
调用不纯的 Native 方法是一个巨坑。
到这一步能够解析动态库了,那么我们就开始搜刮里边的 sun 公司写好的函数了~于是加入下边的代码来 get 那个 ZIP_Open 函数~
然后就开始了:
|
|
Oh my god。
试了无数遍都这样,我是崩溃的!!
卧槽,后来仔细想了想……这一段错误提示是不是在 jvm 中的啊???
然后我在 hotspot/src/share/vm/utilities/debug.cpp
下……
然而!然而!!
我调用的 ZIP_Open 函数分明是 jdk 中的 Native 函数啊!!jdk 和 hotspot 是两个模块互不干扰,而且我就是写了一段 cpp 代码,根本没用到 jvm 啊!!
emmmm。细心的小伙伴肯定发现了。刚才的 libzip.dylib 引用了 libjvm.dylib。
那只能说明一点!!
(双眼一亮)
ZIPOpen 函数内部肯定检测了 JVM 的启动!!
是的就是这样。查阅源码之后我们能看到内部的各种检测自家的 JVM 启没启动的代码……
所以我们必须启动一个虚拟机才行。调用 JNI:
在整个代码的前部加上:
然后就可以了!输出就是正常的了。不过我们在编译的时候也要用 install_name_tools:
这说明,如果要自己写的话,Native 方法也要自己实现,不能调用人家的啊……(