Switching gears for a moment and dumping some vital Java knowledge for later.
Using Eclipse 3.2 and JDK 1.6.10 update 11 and jre6 I have finally managed to call a freaking hello world function in a freaking native dll. This only after three days of flailing around with directories and dll files. The final trick however should have been apparent from day one.
Here's what I'm working with currently. First some Java code that declares a native function:
package org.cainan.jnitest;
public class HelloWorld {
public static native void printTest();
public static void main(String[] args) {
// TODO Auto-generated method stub
HelloWorld.printTest();
}
static {
System.loadLibrary("HelloWorld");
}
}
Second, the C implementation of the declared native function:
#include "org_cainan_jnitest_HelloWorld.h"
#include
JNIEXPORT void JNICALL Java_org_cainan_jnitest_HelloWorld_printTest(JNIEnv* env, jclass ptrThis)
{
printf("Hello bitches!\n");
}
Note the "org_cainan_jnitest_HelloWorld.h" is the header file that was created with this command:
This was done that the top level bin folder because javah walked down the folders org, cainan, and jnitest to find HelloWorld.class.
Now, onto the problem.
After getting all this set up and loading the correct library into the Java program, I still got an error saying that the printTest method is not implemented. After MANY attempts at figuring this out I finally loaded up this nifty little app : Anywhere PE Viewer. This little app dumps the export table for a library like the "Dumpbin.exe /EXPORTS" of Visual Studio fame.
This is the only function that was originally exported in my library:
Java_org_cainan_jnitest_HelloWorld_printTest@8
Notice the @8 appended to the end. I believe this is the bytes that are to be passed to this function. I'm not entirely sure, but, I do know that the export name is wrong. It should look like this:
Java_org_cainan_jnitest_HelloWorld_printTest
Minus any other appended crap. Ok. Now that we know that how do we get the name fixed. In Eclipse I have a "Managed" C project that compiles the native code into a shared library (dll) but the export of the function is messed up. In order to fix this I need an additional linker option. This option can be added in the project properties under "C/C++ Build" branch, the "Tool Settings" tab, and under "GCC C Linker"->"Miscellaneous"->"Other options".
The option that worked for me was "--add-stdcall-alias". This option is specefic to the Portable Executable (PE) format i386 linker of the GCC. According to the GNU Linker manual, the command:
If given, symbols with a stdcall suffix (@nn) will be exported as-is and also with the suffix stripped. [This option is specific to the i386 PE targeted port of the linker]
This means that when using the option then the dll will export both version of the function (mangled with the stdcall's suffix and clean). After doing this and getting my exported functions named correctly my little JNI test finally worked.
Fortunately for me, I found this guys post that had the linker switch and some other insights.
He is offering a $50 for an answer to his question. I wish I could help.
No comments:
Post a Comment