java - 用g++编译jvmti代理失败,但cc工作正常

标签 java gcc g++ jvmti

我有一个非常简单的代理,基本上只是所需的 Agent_OnLoad 方法签名。

如果我用g++编译它。

 g++ -g -fno-strict-aliasing -fPIC -fno-omit-frame-pointer -W -Wall  -Wno-unused -Wno-parentheses  -I. -I../agent_util -I/home/user/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//include -I/home/user/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//include/linux  -c -o ../src/testagent.o -DMAX_THREADS=1000 -DJVMTI_TYPE=1 ../src/testagent.c

并创建一个共享库并在代理上运行测试

LD_LIBRARY_PATH=`pwd` /home/mnc/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//bin/java -agentlib:testagent -version

我收到错误

Error occurred during initialization of VM
Could not find agent library on the library path or in the local directory: testagent
make: *** [test] Error 1

如果我使用以下命令进行编译,即编译为 C,则可以正常工作。

gcc -Wl,-soname=calltracer.so -g -fno-strict-aliasing -fPIC -fno-omit-frame-pointer -W -Wall  -Wno-unused -Wno-parentheses  -I. -I../agent_util -I/home/user/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//include -I/home/user/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//include/linux  -c -o ../src/testagent.o -DMAX_THREADS=1000 -DJVMTI_TYPE=1 ../src/testagent.c

然后创建一个shred lib并测试它

LD_LIBRARY_PATH=`pwd` /home/user/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//bin/java -agentlib:testagent -version
java version "1.6.0_13"
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Java HotSpot(TM) Server VM (build 11.3-b02, mixed mode)

工作正常。

问题是我的代码是实际方法的 cpp 代码,而不是 c。可以使用 C++ 代码创建代理吗?我怀疑是这样,但我不知道我做错了什么。

这是我的测试代理的来源。没有比这更简单的了。

/*testagent.c*/
#include "jni.h"
#include "jvmti.h"


JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
return 0;
}

/* Agent_OnUnload() is called last */
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm)
{
}

编译为 c 文件时可以正常工作

here's the output from the nm command
0000046c T Agent_OnLoad
00000476 T Agent_OnUnload
00001f18 a _DYNAMIC
00001ff4 a _GLOBAL_OFFSET_TABLE_
         w _Jv_RegisterClasses
00001f08 d __CTOR_END__
00001f04 d __CTOR_LIST__
00001f10 d __DTOR_END__
00001f0c d __DTOR_LIST__
000004d4 r __FRAME_END__
00001f14 d __JCR_END__
00001f14 d __JCR_LIST__
0000200c A __bss_start
         w __cxa_finalize@@GLIBC_2.1.3
00000480 t __do_global_ctors_aux
000003b0 t __do_global_dtors_aux
00002008 d __dso_handle
         w __gmon_start__
00000467 t __i686.get_pc_thunk.bx
0000200c A _edata
00002014 A _end
000004b8 T _fini
00000348 T _init
0000200c b completed.7021
00002010 b dtor_idx.7023
00000430 t frame_dummy

这是另一个版本,我添加了您对 extern "C"的建议,但结果与以前相同,找不到库。

/*testagent.c*/
#include "jni.h"
#include "jvmti.h"

extern "C" {
    JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
    {
        return 0;
    }
}

/* Agent_OnUnload() is called last */
JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm)
{
}

这是 nm 命令的输出

000004bc T Agent_OnLoad
000004c6 T Agent_OnUnload
0000200c d DW.ref.__gxx_personality_v0
00001f18 a _DYNAMIC
00001ff4 a _GLOBAL_OFFSET_TABLE_
         w _Jv_RegisterClasses
00001f08 d __CTOR_END__
00001f04 d __CTOR_LIST__
00001f10 d __DTOR_END__
00001f0c d __DTOR_LIST__
00000594 r __FRAME_END__
00001f14 d __JCR_END__
00001f14 d __JCR_LIST__
00002010 A __bss_start
         w __cxa_finalize@@GLIBC_2.1.3
000004d0 t __do_global_ctors_aux
00000400 t __do_global_dtors_aux
00002008 d __dso_handle
         w __gmon_start__
         U __gxx_personality_v0
000004b7 t __i686.get_pc_thunk.bx
00002010 A _edata
00002018 A _end
00000508 T _fini
0000039c T _init
00002010 b completed.7021
00002014 b dtor_idx.7023
00000480 t frame_dummy

nm 命令的跟踪略有不同,但它们都包含 Agent_OnLoad。

这是在两种情况下用于创建共享库的命令行。

cc -g -fno-strict-aliasing -fPIC -fno-omit-frame-pointer -W -Wall  -Wno-unused -Wno-parentheses  -I. -I../agent_util -I/home/user/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//include -I/home/user/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//include/linux  -Wl,-soname=libtestagent.so -static-libgcc -mimpure-text  -shared -o libtestagent.so ../src/testagent.o -lc

从 ldd 输出,不工作的情况 (g++)

ldd libtestagent.so 
linux-gate.so.1 =>  (0x00d96000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x0019a000)
/lib/ld-linux.so.2 (0x005ee000)

ldd 的输出,工作案例 (gcc)

ldd libtestagent.so 
linux-gate.so.1 =>  (0x00544000)
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0x00908000)
/lib/ld-linux.so.2 (0x003a2000)

使用 Linux 大约 15 年了,从来不知道你可以做 LD_DEBUG=all,非常有用。 这是有趣的输出

2689:   symbol=__gxx_personality_v0;  lookup in file=/home/mnc/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013//bin/java [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/lib/tls/i686/cmov/libpthread.so.0 [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/home/mnc/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013/bin/../jre/lib/i386/jli/libjli.so [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/lib/tls/i686/cmov/libdl.so.2 [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/lib/ld-linux.so.2 [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/home/mnc/apps/Genuitec/Common/binary/com.sun.java.jdk.linux.x86_1.6.0.013/jre/lib/i386/server/libjvm.so [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/lib/tls/i686/cmov/libm.so.6 [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/home/mnc/apps/javacalltracer/Calltracer/jvmti/libtestagent.so [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/lib/tls/i686/cmov/libc.so.6 [0]
      2689: symbol=__gxx_personality_v0;  lookup in file=/lib/ld-linux.so.2 [0]
      2689: /home/mnc/apps/javacalltracer/Calltracer/jvmti/libtestagent.so: error: symbol lookup error: undefined symbol: __gxx_personality_v0 (fatal)
      2689: 
      2689: file=/home/mnc/apps/javacalltracer/Calltracer/jvmti/libtestagent.so [0];  destroying link map
Error occurred during initialization of VM

我在 stackoverflow 上对此进行了搜索,有一篇文章建议为此符号添加一个全局变量 所以我添加了 __gxx_personality_v0 作为 无效*__gxx_personality_v0;

现在,当使用 g++ 进行编译时,JVM 找到了该库。

java,#gcc

最佳答案

您没有向我们展示 testagent.c 的内容。

我的猜测是你被C++ name mangling咬了,要么是因为您在定义 Agent_OnLoad() 时没有使用 JNIEXPORT,要么是因为 JNIEXPORT 不包含 extern "C" 在您的平台上。

extern "C"{} 包围 Agent_OnLoad() 的定义应该就是您所需要的。

您可以通过执行来验证名称修改是否确实是您的问题

nm libtestagent.so | grep Agent_OnLoad

并比较工作版本 (gcc) 和损坏版本 (g++) 的结果。

更新:

好吧,所以 C++ 名称修改不是这样的。您下一步应该找出动态链接器无法dlopen("libtestagent.so")的原因。您可以通过在命令前加上 LD_DEBUG=all 前缀、收集工作和非工作输出(输出将会很大)并查找差异来做到这一点。

更新2:

I've added __gxx_personality_v0 as a void *__gxx_personality_v0;

这并不是解决问题的正确方法,并且当您开始在代理中使用实际的 C++ 时,可能会导致出现问题。

解决该问题的正确方法是将库链接到 g++,而不是 gcc。这将添加对 libstdc++.so.6 的依赖,它定义了 __gxx_personality_v0 以及您的 C++ 代码需要的一堆其他内容。

关于java - 用g++编译jvmti代理失败,但cc工作正常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8087971/

相关文章:

java - 使用过滤器在登录尝试失败后重定向

java - Android Studio 中的版本控制

gcc - #include <alsa/asoundlib.h> 和 <sys/time.h> 导致多个定义冲突

iphone - 如何为 iPhone 构建静态库?

java - JNI下C程序编译报错

c++ - 错误: 'unordered_set' is not a member of 'std'

java - java中没有分配的字符串连接

java - JAXB 解码问题

c++ - 是否可以在基于 Linux 的系统上使用 gcc 或 g++ 进行静态编译?

c++ - OpenMP 二维拉普拉斯方程代码 : G++ blows up while Intel C converges perfectly