Silverfrost Forums

Welcome to our forums

How to link code compiled by FTN95-64 with third-party DLLs

16 Mar 2017 11:53 (Edited: 16 Feb 2019 2:16) #19143

I claimed in another thread that one could use routines contained in third-party DLLs from code compiled by FTN95-64. John Campbell asked me by private mail to provide a recipe, and I am responding here so that other readers interested in the topic can also see.

I give a how-to description using a very simple example. More complicated cases may respond to the same procedure, but you may run into difficulties if the DLL routines do I/O or the DLL was built with a different calling convention than the standard Windows-64 ABI (CDECL, first four arguments in RCX, RDX, R8 and R9, the rest on the stack, etc.), or there are descriptors to be passed, optional arguments are passed, there is global shared data, etc.

Note also that it may be next to impossible to debug the mixed-pedigree program that you produce. It is best to test and debug the DLL routine in some native environment first.

Here is the example. A driver main program compiled with FTN95 calls a routine compiled with Gfortran which returns a vector that contains the elements of an input vector but in reverse order.

The code for the DLL, file rvec.f90:

subroutine reverse_vec(x,y,n)
implicit none
integer, intent(in) :: n
real, dimension(n), intent(in) :: x
real, dimension(n), intent(out) :: y
integer :: i

do i=1,n
   y(n+1-i) = x(i)
end do

return
end subroutine reverse_vec

We compile this into a DLL using Gfortran 6.2-64bit:

gfortran -shared -o rvec.dll rvec.f90

The driver program, tvec.f90:

program tvec
implicit none
C_EXTERNAL reverse_vec 'reverse_vec_'
real :: x(4) = [1,2,3,4], y(4)
write(*,*)x
call reverse_vec(x,y,4)
write(*,*)y
end program tvec

Compile and link the driver using FTN95:

ftn95 tvec.f90 /64
slink64 tvec.obj rvec.dll /file:tvec

You may note that the DLL is quite big. That is because it has a fixed overhead of having to contain the parts of the Gfortran runtime that the DLL routine needs.

Also note that the DLL does not have to be produced from Fortran code or be generated using Gfortran.

17 Mar 2017 7:17 #19167

Quoted from John-Silver so basically it's just a question of declaring the dll outine via C:EXTERNAL in the calling program ?

Even that is unnecessary if the external program uses the same name decoration that FTN95 does (all uppercase, no underscore appended). One should also note that 'C' does not really mean that the external code has to be written in C, but that it must behave in the same way as C code compiled with a certain Fortran-compatible C compiler.

Of course the called DLL must be 64 bit also I assume.

Of course.

20 Mar 2017 12:45 #19183

mecej4,

Thanks very much for your example. I have it working.

Now to experiment with what can be placed in the .dll and how to transfer information between the FTN95 memory and the .dll.

Functionality like multi-threading and some write reporting could be good. Must this .dll be self sufficient or can it call FTN95 supplied routines ?

One thing I can't do is get an equivalent to Slink64's /map:tvec for the list of symbols in the .dll. When expanding the capabilities of 'rcec.dll', it would be good to have a list of:

what symbols are available to tvec.f90 from the .dll,

what symbols are included in the rvec.dll build (library routines) and

what other .dll's are required by rvec.dll

John

20 Mar 2017 2:26 #19184

It is easiest if you restrict yourself to passing data as routine arguments. Sharing global variables between a DLL and an EXE is complicated and errorprone. If more than one EXE may be running that depends on a DLL, you have to arrange for locks, etc. on those variables.

You can use the MS DUMPBIN utility or the GNU utility NM to display the symbols exported by a DLL. You can use Dependency Walker to list the other DLLs that are used by a specific DLL.

Please login to reply.