UnsatisfiedLinkError: The specified procedure could not be found - java

I'm writing some JNI code in C++ to be called from an applet on Windows XP. I've been able to successfully run the applet and have the JNI library loaded and called, even going so far as having it call functions in other DLLs. I got this working by setting up the PATH system environment variable to include the directory all of my DLLs are in.
So, the problem, is that I add another call that uses a new external DLL, and suddenly when loading the library, an UnsatisfiedLinkError is thrown. The message is: 'The specified procedure could not be found'. This doesn't seem to be a problem with a missing dependent DLL, because I can remove a dependent DLL and get a different message about dependent DLL missing. From what I've been able to find online, it appears that this message means that a native Java function implementation is missing from the DLL, but it's odd that it works fine without this extra bit of code.
Does anyone know what might be causing this? What kinds of things can give a 'The specified procedure could not be found' messages for an UnsatisifedLinkError?

I figured out the problem. This was a doozy. The message "The specified procedure could not be found" for UnsatisfiedLinkError indicates that a function in the root dll or in a dependent dll could not be found. The most likely cause of this in a JNI situation is that the native JNI function is not exported correctly. But this can apparently happen if a dependent DLL is loaded and that DLL is missing a function required by its parent.
By way of example, we have a library named input.dll. The DLL search order is to always look in the application directory first and the PATH directories last. In the past, we always ran executables from the same directory as input.dll. However, there is another input.dll in the windows system directory (which is in the middle of the DLL search order). So when running this from a java applet, if I include the code described above in the applet, which causes input.dll to be loaded, it loads the input.dll from the system directory. Because our code is expecting certain functions in input.dll which aren't there (because it's a different DLL) the load fails with an error message about missing procedures. Not because the JNI functions are exported wrong, but because the wrong dependent DLL was loaded and it didn't have the expected functions in it.

There is a chance that the DLL was built using C++(as opposed to C). unless you took care to do an extern on the procedure,this is one possible reason.
Try exporting all the functions from the DLL. If the list includes your function, then you're good.

Usually, when linking to other libraries, you need to link to the relevant .lib file. It sounds like you aren't referencing all the lib files you need. Check what isn't linking and make sure you add it's lib to the list for the linker.

Did you create the new external DLL using the standard JNI procedure? I.e., using javah and so forth? If so, then I am not sure what is wrong.
If not, then the procedure you're trying to call hasn't been exported (as mentioned by anjanb). I am aware of two way of exporting functions: a separate export list and marking specific functions with __declspec(dllexport).
Can't access variable in C++ DLL from a C app has a little more information the topic of DLLs.

Compile your c++ code in debug mode. Then insert the DebugBreak(); statement where you would like to start debugging. Run the java code. When the DebugBreak() statement is encountered you will get a popup with a Debug button on it. Click on it. Dev Studio will open with your program in machine code. Step over with the debugger twice and you should be able to step over your source code.

If you have done all programming issue at JNI manuals and examples but still you are getting same missing procedure error, problem can be at your path variable probably. Do below steps and run again:
Be sure about you set JAVA_HOME variable to your JDK folder(not JRE because JRE doesnt contain jni header)
Example:
At environment variable settings panel define var:JAVA_HOME val:C:\Program Files\Java\jdk1.7.0_11
add %JAVA_HOME%\bin to your path variable
After doing those steps, your application can find jni procedure name and links to JNI.dll in right way. So, i hope you dont get this missing procedure error again.

Related

Java JNI, multiple versions of a dll: How to specify which dll is being used for native calls (matlab jvm)

I am facing a special situation. I am trying to run a java application within matlab. My application uses an external dll which depends on another dll (xerces-c_3_2.dll). My problem is that matlab also contains a xerces-c_3_2.dll in its root folder.
Unfortunately, these two dll files are different! It is not possible to change the library path of the jvm within matlab in a way, that the 'matlab'-version of the dll is not shadowing my dll version (it's automatically loaded on matlab startup). Due to this, my application is always throwing exceptions that a procedure could not be found because its using the wrong version.
Since matlab won't start with my version of the dll, my idea is now to rename the dll to 'xerces-c_3_2_myVersion.dll' and load it redundantly.
How can I tell the jvm for a specific jni call which native interface should be used?
In my jni interface the known
public final static native lines are defined, but I never faced the question how to specify the dll in case of redundant native functions?
Any ideas? Thank you!
Sven
I have resolved my problem:
I renamed my version of the dll file from 'xerces-c_3_2.dll' to 'xerces-c_3_s.dll'
I modified my compiled personal dll in a hex editor and changed the dependency acc. to the naming in (1)
I loaded the modified dll with the changed dependency using java within matlab. Now it's working without any problems!
So technically it was more a windows library thing rather than a java question.

How can I add to the system 'Path' variable and have the new path used by a DLL immediately?

Here's the situation. I am attempting to load the JRI for R integration in Java.
Here's what I know to make it work:
-Make sure that the path to the JRI DLL is in the java.library.path (so java can load the native library).
-Make sure that the path to the R DLL is in the system path (so the system can resolve a DLL dependency).
If both of these are true before my application loads, the library will load with no problem.
However I am having problems with loading it if I try to do it automatically, without use involvement.
Here's how I am trying to make it work:
-If the JRI DLL is not in the java.library.path, add it at runtime using reflection and try to load the library again immediately - this works great.
-If the R DLL is not in the system 'Path' add it using the 'setx command.
The second step is where I am hitting huge problems - I still see an error with finding the R DLL (which is a dependency of the JRI DLL).
My code sets the system 'Path' as it should immediately, as I check this in the control panel, however loading the JRI library fails on a dependency.
I know setting the path correctly fixes the dependency issue if I set it before running my application, so why it does not work in this case I do not understand.If I kill my application and start it from scratch, it works fine as the 'Path' was set from the last time it was run.
When a dependency failure occurs on loading a DLL, where is the error actually happening?Does Java look for the dependency of the JRI DLL? I had assumed Java played no part in the resolving a dependency of a DLL, was I wrong?If the operating system is responsible for resolving the dependency of a DLL, then why is my 'Path' change not recognised?Do environment variables get passed to a DLL when Java tries to load it?
I should add that I am developing this in Netbeans, however I see the same behaviour if I build a JAR and run that directly in the terminal. I often see that a 'Path' update is not seen until a restart of Netbeans, however I had thought that a DLL resolving its dependencies would use the 'Path' directly.
It's pretty frustrating so it'd be great to hear your thoughts on this.

Cross-platform Java with native libraries

I am trying to implement a wrapper for native libraries that will be consumed via JNI in a Java application. I want to make installation of the program be as simple as "Unzip this file and run".
I've worked through most of the wrapper so far, getting it to unzip the native libraries from inside the platform-specific JAR file and setting the -Djava.native.lib variable at runtime. I've hit one last roadblock due to unsatisfied link errors.
On Windows, the DLLs that I'm calling link against msvcr100.dll. While the DLLs that I'm calling into are getting picked up by java.native.lib, msvcr100.dll isn't. I don't want to have to require the user to install the C++ runtime before using our app, so we are packaging the DLL file right along side the other DLLs.
As far as I understand, to get things to work we need to put the folder that contains msvcr100.dll on the PATH. I tried using reflection to change the environment inside the application to add the unpack directory to the PATH environmental variable. After this was unsuccessful, I recalled that I was only modifying a copy of the environment.
Is there some way that I can fix the unsatisfied link error without requiring any additional system setup (installing the runtime, hand modification of PATH, etc)? I would think it is possible since I've encountered other pieces of software (SWT, JOGL) that package native libraries that don't exhibit this issue.

Program uses outdated (not current) env variable value

I've got a C++ program that internally uses java (via my C++ dll that wrapps the WebLogic jsmc.dll that internally uses jvm.dll).
When I set CLASSPATH before running my program, all JAR libraries are found and the program works properly.
When I do not set the CLASSPATH before running my program, the JARs are not found, which of course is expected.
Now, when I set the CLASSPATH before running my program, but clear this CLASSPATH environment variable inside the program code before loading my dll that uses java, a strange thing happens: all JARs are still found and the program works as if everything was OK. I have verified by several ways that the CLASSPATH is really deleted from the env variables (e.g. by using ProcessExplorer or by printing its value).
QUESTION:
Can you explain this behaviour to me? I'm not wondering why java ignores the CLASSPATH I set, but how is it possible that java sees the old CLASSPATH value, not the current one? I emphasize that it isn't possible for java to store the old CLASSPATH value somehow because java was not loaded at the the time the old value was available.
How can I make the java to respect the changes in the process env variables?
DETAILS:
The problem above is just a simplification I've made to explore my real problem. I'm trying to set the CLASSPATH from within the program and avoid to have it set externally. But the java uses the externally set CLASSPATH, not the one I set inside the program.
I read and set the env variables values using the Windows API (GetEnvironmentVariableA, SetEnvironmentVariableA). I have verified that the program process environment variables really change after setting them this way. I even printed the CLASSPATH value from the dll that uses java, before calling any java method. I checked using the ProcessMonitor that jvm.dll is really loaded after the CLASSPATH is deleted. I also tried to exclude the possibility that the CLASSPATH is read from the parent process. Now I'm pretty sure that at the time jvm.dll is being loaded, the CLASSPATH is already deleted from the process environment.
I have tried both a Visual C++ 2010 testing program and the HP LoadRunner C-compiler (mmdrv.exe) vuser script, with the same result. LoadRunner is the main reason why I need to solve this problem.
The problem was caused by that the C-runtime somehow caches the environment variables. While I was trying to modify the CLASSPATH using the system function SetEnvironmentVariableA(), jmsc.dll read CLASSPATH from the C-runtime cache. The C-runtime tries to synchronize its cache with the real values in the process environment, but evidently not very successfully. It was necessary for me to replace the system call to SetEnvironmentVariableA() with the call to _putenv() from the C-runtime in order to change the CLASSPATH.
But there was another problem. There were various versions of C-runtime used by my code, each having its own environment cache. My VC code was linked against msvcr100.dll, while jmsc.dll (that instantiates the Java VM) uses msvcrt.dll. The solution was to link my code to msvcrt.dll too, so that my code sets CLASSPATH using _putenv() from the same C-runtime that jmsc.dll reads.
Thanks to Harry Johnston for the crucial hint, and Peter Cetinski for valuable information.
You should not rely upon the CLASSPATH environment variable when invoking a new JVM process from C++. The JNI interface provides a mechanism to specify the classpath of the JVM upon startup.
See http://java.sun.com/docs/books/jni/html/invoke.html#28719

Calling dlopen from a shared library loaded via JNI makes previously loaded symbols invisible

I have a Java application which interacts with native code using JNI. The native code compiles a file at runtime and attempts to load this file using a dlopen call. This call fails and I get a warning stating
Could not load library (x):(x) undefined symbol: y
However when I have a native application starting a JVM via JNI run the same code, this error does not occur and it runs as expected. I am suspecting Java is doing something fancy which causes the already loaded libraries to be invisible for the library that is loaded with dlopen.
This I did to diagnose the problem:
Confirmed in which shared library the symbol that was supposedly undefined is located using objdump
Confirmed this library is loaded by using gdb (via eclipse - the library was listed in the modules pane)
Printed LD_LIBRARY_PATH just before the dlopen and confirmed Java passes it through (It did add Java's lib dirs but the original dirs were still there)
I have been trying to solve this for a while now, but I can't figure out what is going on. Especially because it does work when the JVM is loaded from a native application.
Thank you in advance!
I finally found the answer. The solution was to recompile the shared library that contained the symbol that was not found with the -Wl,--export-dynamic linker flag.
Interestingly I did not program that shared library myself and I would expect the default compilation to add the flag since it was needed for software using it to function properly.
Either way it is a open source project so I could compile it with the proper flags set.

Categories

Resources