Why do I get UnsatisfiedLinkError when calling a MinGW-compiled function (not loading the library) on Windows?

余生长醉 提交于 2021-01-27 09:03:41

问题


I'm making a simple JNI test app using Eclipse on Windows. My C++ compiler is MinGW 4.6.2. Java is throwing an UnsatisfiedLinkError when I try to invoke a function in my test DLL (the DLL itself loads without problem). I have verified that my DLL exports a "C" function with the same name as the function generated by the javah utility.

How could trying to invoke the function possibly generate a link error? (Also, is there any way to get more detail as to what symbol isn't being found? A bald statement that there's an UnsatisfiedLinkError is next to useless.)

Here's the Java which defines the native function:

package com.xyz.jsdi_test;

import java.io.File;

public class JSDI
{
    public static native void func(
        String str,
        int i,
        Integer ii,
        long j /* 64 bits */,
        Long jj,
        byte[] b
    );
    public static void dummy()
    {
        System.out.println("JSDI.dummy()");
    }
    static
    {
        File f = new File("..\\jsdi\\bin\\jsdi.dll");
        System.out.println("Preparing to load: " + f);
        System.load(f.getAbsolutePath());
        System.out.println("Successfully loaded: " + f);
    }

Here is the corresponding output from javah:

...
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_xyz_jsdi_test_JSDI
 * Method:    func
 * Signature: (Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
 */
JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func
  (JNIEnv *, jclass, jstring, jint, jobject, jlong, jobject, jbyteArray);

#ifdef __cplusplus
}
#endif

...And how I implemented the function...:

extern "C"
{

JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func(
    JNIEnv * env,
    jclass _class,
    jstring str,
    jint i,
    jobject ii,
    jlong j,
    jobject jj,
    jbyteArray b
)
{
    // don't do anything...let's just try to get called successfully...
}

} // extern "C"

Here is how I attempt to invoke it.

...

public static void main(String[] args)
{
    JSDI.dummy(); // cause class to load, which should cause System.load() to run.
    JSDI.func("hello", 0, 0, 0L, 0L, (byte[])null);
}

Finally, this is the output:

Preparing to load: ..\jsdi\bin\jsdi.dll
Successfully loaded: ..\jsdi\bin\jsdi.dll
JSDI.dummy()
java.lang.UnsatisfiedLinkError: com.xyz.jsdi_test.JSDI.func(Ljava/lang/String;ILjava/lang/Integer;JLjava/lang/Long;[B)V
   at com.xyz.jsdi_test.JSDI.func(Native Method)
   at com.xyz.jsdi_test.SimpleTest.main(SimpleTest.java:24)

回答1:


SOLVED IT -- WOOO!

It turns out that MSVC prepends an underscore to the name of __stdcall functions. MinGW does not. The Windows JVM apparently expects the '_' prefix. As soon as I prepended '_' to the function name and rebuilt with MinGW, everything worked just dandy.

eg:

JNIEXPORT void JNICALL Java_com_xyz_jsdi_1test_JSDI_func ==> _Java_com_xyz_jsdi_1test_JSDI_func

EDIT: The --add-stdcall-underscore feature of the dlltool utility included with MinGW can solve this problem for you transparently. Set it up in your Makefile and you don't need to worry about having different versions of the actual source code for different compilers. See at this link.




回答2:


Posting a working example, copy the content in the three files in the same directory (modify the path to your JDK) then invoke build.cmd

/* File: HelloWorld.java */
public class HelloWorld {
    private static native void writeHelloWorldToStdout();

    public static void main(String[] args) {
        System.loadLibrary("HelloWorld");
        writeHelloWorldToStdout();
    }
}

/* File: HelloWorld.c */
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL Java_HelloWorld_writeHelloWorldToStdout(JNIEnv *env, jclass c)
{
    printf("Hello World!");
}

rem File: build.cmd 

%echo off
echo delete generated binaries
del HelloWorld.class
del HelloWorld.dll
del HelloWorld.h
del HelloWorld.def

echo Compile the Java Class
javac HelloWorld.java

echo Generate the Header file
javah -classpath . -o HelloWorld.h HelloWorld

echo Build the DLL 
gcc -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include" -I"C:\Program Files (x86)\Java\jdk1.7.0_25\include\win32" -Wl,--add-stdcall-alias -Wl,--output-def,HelloWorld.def -shared  -o HelloWorld.dll HelloWorld.c

echo run the program 
java HelloWorld



回答3:


The signature in the exception doesn't have the 'int' parameter. So your Java code disagrees with your native code.



来源:https://stackoverflow.com/questions/17199904/why-do-i-get-unsatisfiedlinkerror-when-calling-a-mingw-compiled-function-not-lo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!