Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28988 Discussions

Trouble generating DLL in Frotran and using it in Java

Bruno_Repetto
Beginner
1,957 Views
Can anybody help by pointing out what the problem may be, and suggesting a way to correct it?

I have been following the examples in "FORTRAN 90/95 and Java interoperability using JNA", successfully inserting and testing the Fortran subroutines shown in the webpage one by one, each time generating a DLL using it in NetBeans Java. The Java code has no problem loading and using the DLL. The source code for the DLL contains the following 4 subroutines, as per the examples:

FUNCTION mult(a, b)
!DEC$ ATTRIBUTES ALIAS:'foomult', DLLEXPORT :: mult
INTEGER,VALUE :: a,b
INTEGER :: mult
mult = a * b
END FUNCTION

SUBROUTINE ffunc(a, b)
!DEC$ ATTRIBUTES ALIAS:'reffunc', DLLEXPORT :: ffunc
INTEGER :: a,b
a = 3
b = 5
END SUBROUTINE

SUBROUTINE inc(arr, len)
!DEC$ ATTRIBUTES ALIAS:'fooinc', DLLEXPORT :: inc
INTEGER,DIMENSION(len) :: arr
INTEGER,VALUE :: len
INTEGER :: i
DO i = 1, len
arr(i) = arr(i) + 30
END DO
END SUBROUTINE

SUBROUTINE arr2d(arr, m, n)
!DEC$ ATTRIBUTES ALIAS:'arr2d', DLLEXPORT :: arr2d
INTEGER,DIMENSION(m,n) :: arr
INTEGER,VALUE :: m
INTEGER,VALUE :: n
INTEGER :: i,j
DO i = 1, m
DO j = 1, n
arr(i,j) = arr(i,j) + 1
END DO
END DO
END SUBROUTINE

When I add the piece of code below, suddenly Java cannot load the DLL at all, even though the compile/link steps in Fortran show no errors or warnings, and dumpbin /exports correctly indicates that the appropriate modules have been exported.

FUNCTION strpass(line, b)
!DEC$ ATTRIBUTES ALIAS:'foostr', DLLEXPORT :: strpass
INTEGER :: b
CHARACTER(len=b) :: line
LOGICAL :: strpass
strpass = (line == 'str_test') .AND. (b == 8)
END FUNCTION

The text of the error message in Java is as follows:

run:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'libf90str': The specified module could not be found.
at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:163)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:236)
at com.sun.jna.Library$Handler.(Library.java:140)
at com.sun.jna.Native.loadLibrary(Native.java:379)
at com.sun.jna.Native.loadLibrary(Native.java:364)
at jna_test.Main$F95Test.(Main.java:28)
at jna_test.Main.main(Main.java:84)
Java Result: 1
BUILD SUCCESSFUL (total time: 1 second)
FUNCTION strpass(line, b)
!DEC$ ATTRIBUTES ALIAS:'foostr', DLLEXPORT :: strpass
INTEGER :: b
CHARACTER(len=b) :: line
LOGICAL :: strpass
strpass = (line == 'str_test') .AND. (b == 8)
END FUNCTION

0 Kudos
9 Replies
netphilou31
New Contributor III
1,957 Views
Bruno,

The problem could be caused by the default character string passing convention in fortran. In fact when you declare a character string as argument, the fortran adds an additional hidden argument for the string length (before the string itself as far as I remember). So I suggest that you add the string length in the declaration of the routine in your Java code.

Moreover, it seems that you forget to add :BIND(C, name='foostr')to the declaration of the function as mentioned in the article you refer to.
[fortran]FUNCTION strpass(line, b) BIND(C, name='foostr')
    CHARACTER(len=b) :: line
    INTEGER, VALUE :: b
    LOGICAL :: strpass

    strpass = (line == 'str_test') .AND. (b == 8)
END FUNCTION[/fortran]
Regards,

Phil.
0 Kudos
Bruno_Repetto
Beginner
1,957 Views
Phil:

Thanks for the suggestion. I will make sure the declaration for the string length is as you indicate and as it appears in the article. I purposefully removed the BIND declaration, as I am using the !DEC$ in all the functions (not just this one), where the ALIAS declaration is used in it's stead. However, I now think that the "C" portion of the BIND declaration may have something to do with the problem.

I have had to make minor allowances and changes from what's posted in the article, as there are minor typos and reversals of instructions; problems occur if we use the examples verbatim.

Also, it is a little baffling that the addition of the 'foostr' subroutine apparently causes the entire DLL to be "not found" by Java, intead of giving a less definitive error. It apparently loses the definitions for the other subroutines.

Bruno
0 Kudos
IanH
Honored Contributor III
1,957 Views
I wonder if the additional code includes calls to the fortran runtime (_for_cpstr perhaps) that weren't there before. As a result, when java loads your DLL the OS loader also now needs to be able to load the fortran runtime DLL's. Perhaps those DLL's are not in a directory on the PATH?

You can use the depends utility that comes with visual studio (and is available as a free download) to see whether all the DLLs needed to load your DLL can be found. Note that you need to run it in the same environment (same PATH) as Java uses to see the same results that Java would see.

As an aside (more of a response to a previous post of yours) - my personal preference is to avoid compiler directives whenever I can - because they are inherently compiler specific. In this case (Fortran to another language) nstead of DLLEXPORT I would supply a separate DEF file to the linker to nominate the DLL's exported entry points. Your code then just looks like...
[fortran]MODULE MyJavaUtils
  IMPLICIT NONE
CONTAINS    
  FUNCTION mult(a, b) BIND(C, NAME='foomult')
    USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT
    INTEGER(C_INT), VALUE :: a,b
    INTEGER(C_INT) :: mult
    mult = a * b
  END FUNCTION
END MODULE MyJavaUtils
[/fortran]
(C_INT kind corresponds to C's int type)

and then you would have a DEF file that contained...
[plain]EXPORTS
  foomult[/plain]
Within Visual Studio you supply the name of the DEF file via the property Linker > Input > Module Definition File, if you are using the command line you can just supply the filename directly as an argument to the ifort driver. See the help for the linker for more information on DEF files.

Note with a separate DEF file I think you lose magic DLLEXPORT to DLLIMPORT handling with module procedures that are USE'd elsewhere, plus some people find that having a separate file is opaque/inconvenient and/or a maintenance problem, so they are not everyone's cup of tea.
0 Kudos
Bruno_Repetto
Beginner
1,957 Views
As it turns out, two redistributable DLLs were needed that Java wasn't explicitly naming. The DLLs in question were libcoremd.dll and libmmd.dll. Once they were put in the path, the problem was solved. Thanks for the Dependency Walker suggestion!

I hate cryptic error messaging.

Thanks to all who helped!

Bruno Repetto, PhD
0 Kudos
Mohammad_K_
Beginner
1,957 Views

Hello,

I am having the same problem you were experiencing. I used Intel's Fortran compiler. What is the compiler you used? Also can you provide the compiler settings if they are different than the default ones. I used the dependency tool and it is showing that the following dll libraries are missing.

msvcr110d.dll

kernel32.dll

libifcoremdd.dll

Although I copied these libraries into the path where the library is called this did not resolve the error message. The message is:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'echoOutput_': The specified procedure could not be found.

    at com.sun.jna.Function.<init>(Function.java:179)
    at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:345)
    at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:325)
    at com.sun.jna.Library$Handler.invoke(Library.java:203)
    at NB3Eqs.$Proxy0.echoOutput_(Unknown Source)
    at NB3Eqs.NB3Eqs_Solution.unitTest(NB3Eqs_Solution.java:110)
    at NB3Eqs.NB3Eqs_Solution.main(NB3Eqs_Solution.java:148)

Thanks for your help,

Mohammad

0 Kudos
Bruno_Repetto
Beginner
1,957 Views

Mohammed,

I managed to solve this problem a while back, so much so that I don't remember all the details... but we can collaborate here and make sure things will work with you, so let me give it a shot:

  • The compiler I used back then identified itself as "Intel(R) Visual Fortran Composer XE 2011 Update 9 Integration for Microsoft Visual Studio* 2008, 12.1.3526.2008".
  • Make sure that your Java package knows where the support DLLs are. I used Eclipse.  In Eclipse, you point to the necessary folders by entering the paths to anything you want to include in Java Build Path / Libraries entry of your Java project properties.
  • I put the JNA.jar file in the folder where I have the DLL.
  • Point to your DLL using a VM argument. In Eclipse, I followed this path of menus and tabs:

               Run / Run Configurations... /  "Main" entry under "Java Application" on the left-side panel / "(x)= Arguments" tab

            In the VM arguments sub-window, enter these:  -Xmx768m -Djna.library.path="<full path to the folder where your DLL is>".

            You may have to tweak the 768 number.

  • These are the DLLs that I had to include, in addition to mine (myDLL.dll) to support Fortran access by Java:

                  libifcoremd.dll libifportmd.dll libmmd.dll

I defined my Fortran project in two ways: One, where the subroutine would be tested using a fortran "driver" routine, so I could debug it, and another where the DLL would be created and tested using the Java code.  The only difference between the project modes was that the DLL-generating one would include the compiler-directive in the subroutine that read:

                  !DEC$ ATTRIBUTES ALIAS:'myDLL', DLLEXPORT :: myDLL

The presence or absence of this compiler directive was controlled by the presence or absence of "DLL", a compiler symbol that I defined.

Hope this helps!!

Bruno W. Repetto, PhD

0 Kudos
Mohammad_K_
Beginner
1,957 Views

Bruno,

Thanks for your quick and detailed reply. After generating the DLL from Intel Fortran compiler is there a way to check that the functions are accessible to JAVA. For gfortran I can write nm filename.dll and all the functions in the dll are shown in command prompt. But for the Intel compiler the nm functions indicates there are no symbols in the dll.

Thanks for your help,

Mohammad

 

Mohammad

0 Kudos
Bruno_Repetto
Beginner
1,957 Views

Yes, there is a way, but unfortunately I must confess I don't remember how I did it.  My apologies.  Perhaps you may want to ask this question elsewhere in this forum.

Bruno W. Repetto, PhD

Bruno Repetto.

0 Kudos
IanH
Honored Contributor III
1,957 Views

The depends.exe (Depenency Walker) utility shows you the exports from a particular DLL (in addition to checking that all the other DLL prerequisites are available).

(A modern Windows system that can't find kernel32.dll is rather broken!)

0 Kudos
Reply