Reputation: 48
I have a very simple agent, basically just the required Agent_OnLoad method signature.
If I compile it with 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
and create a shared lib and run a test on the agent
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
I get an error
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
If I compile as using the following command i.e. compile as C, it works ok.
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
and then create a shred lib and test it
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)
it works ok.
The problem is the code I have, is cpp code for the actual method and not c. Can an agent be created using c++ code? I suspect it is but I don't know what I'm doing wrong.
Here's the source for my test agent. Cant get much more simple then this.
/*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)
{
}
This works ok when compiled as a c file
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
Here's the another version, I added your suggestion of the extern "C" but I had the same result as before, the library could not be found.
/*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)
{
}
Here's the output from the nm command
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
The trace from the nm commands are slightly different but they both include the Agent_OnLoad.
This is the command line used to create the shared lib in both cases.
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
out put from ldd, NOT working case (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)
output from ldd, working case (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)
Been using linux for about 15 years never knew you could do LD_DEBUG=all, very useful. Here's the interesting output
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
I did a search on stackoverflow regarding this, a post suggested adding a global for this symbol so I've added __gxx_personality_v0 as a void *__gxx_personality_v0;
and now the JVM found the library, when compiling with g++.
Upvotes: 1
Views: 1009
Reputation: 213646
You didn't show us contents of testagent.c
.
My guess is that you are being bitten by C++ name mangling, either because you didn't use JNIEXPORT
when defining Agent_OnLoad()
, or because JNIEXPORT
doesn't include extern "C"
on your platform.
Surrounding your definition of Agent_OnLoad()
with extern "C" {
and }
should be all you need.
You can verify whether name mangling is in fact your problem by executing
nm libtestagent.so | grep Agent_OnLoad
and comparing the result for working (gcc
) and broken (g++
) versions.
Update:
Ok, so C++ name mangling wasn't it. You next step should be finding out why dynamic linker is failing to dlopen("libtestagent.so")
. You can do that by prefixing your command with LD_DEBUG=all
, collecting the working and non-working output (output will be huge), and looking for the differences.
Update 2:
I've added __gxx_personality_v0 as a void *__gxx_personality_v0;
That's not quite the correct way to fix the problem, and will likely cause you problems later when you start using actual C++ in the agent.
The correct way to fix the problem is to link the library with g++
, and not gcc
. That will add a dependency on libstdc++.so.6
, which defines __gxx_personality_v0
and a bunch of other stuff your C++ code will need.
Upvotes: 2