Case sensitiveness issue when calling an external DLL in Fortran and compiling with gfortran - dll

I'm trying to call a DLL from a Fortran code compiled with gfortran. I'm importing DISCON_32.dll with:
!GCC$ ATTRIBUTES DLLIMPORT :: DISCON
and compiling with:
mingw32-gfortran "DISCON_32.dll" -cpp -ffree-line-length-none -fno-automatic -Wall -fdefault-real-8 -fno-underscoring -static BladedDLLInterface.o -o "my_program"
I get the error:
BladedDLLInterface.o:BladedDLLInterface.f90(.text+0x6cd): undefined reference to 'discon'
Note that entry point "discon" is here called as lowercase. When looking into the DLL with Dependency Walker, entry point is "DISCON" (uppercase).
I cannot change the case of the DLL. Is there a way to tell my Fortran code to look for "DISCON" entry point instead of "discon"?
(My extended googling of the question did not get me closer to a solution)

There are 3 ways to do this: let's make an example DLL and Fortran main to demonstrate the problem.
! dll1.f90
! gfortran dll1.f90 -shared -odll1.dll -Wl,--out-implib,libdll1.a
module not_used
use ISO_C_BINDING
implicit none
contains
function F(x) bind(C,name='F')
!GCC$ ATTRIBUTES DLLEXPORT :: F
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) ::x
F = x**2
end function F
end module not_used
The command line use to build dll1.dll is shown in the comments.
Now we have a Fortran main:
! main1.f90
! fails:
! gfortran main1.f90 dll1.dll -omain1
! gfortran main1.f90 -L. -ldll1 -omain1
! works:
! gfortran main1.f90 dll1.dll -L. -ldll2 -omain1
! gfortran main1.f90 -L. -ldll1 -ldll2 -omain1
module mod1
implicit none
interface
function F(x)
import
implicit none
!GCC$ ATTRIBUTES DLLIMPORT :: F
double precision F
double precision, intent(in) :: x
end function F
end interface
end module mod1
program main1
use mod1
implicit none
double precision x, y
x = 13
y = f(x)
write(*,*) y
end program main1
The first two gfortran commands shown above fail to build main1.exe. The second two work, but we need a libdll2.a file, and we can create it with dlltool.exe. We start with dll2.def
; dlltool -z dll2.def --export-all-symbol dll1.dll
; dlltool -d dll2.def -l libdll2.a
LIBRARY dll1.dll
EXPORTS
f = F
The first comment line above shows how to dlltool.exe to create a starting point for dll2.def. The output is not very useful in our case, so mostly we used a text editor to modify the starting point. The dll2.def file will create a libdll2.a file that is usefule for linking with dll1.dll, thus the LIBRARY line above. The symbol that got exported was 'F', but gfortran is going to look for 'f', so we rename it in the exports section.
Then we use dlltool.exe to create libdll2.a via the second comment line above. Having a libdll2.a means we can compile main1.f90 according to the comments in lines 6 or 7.
The second method uses a binding name for the function. This requires the function to be interoperable, but if it isn't then you probably couldn't invoke it in any simple way given that it probably wasn't compiled by gfortran. Here we don't need an extra dll2.lib, just a different declaration of the function in main2.f90:
! main2.f90
! gfortran main2.f90 dll1.dll -omain2
! gfortran main2.f90 -L. -ldll1 -omain2
module mod2
use ISO_C_BINDING
implicit none
interface
function F(x) bind(C,name='F')
import
implicit none
!GCC$ ATTRIBUTES DLLIMPORT :: F
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) :: x
end function F
end interface
end module mod2
program main2
use mod2
implicit none
double precision x, y
x = 13
y = f(x)
write(*,*) y
end program main2
This compiles with either of the gfortran commands given in the comments.
Finally you could use dynamic linking as in main3.f90:
! main3.f90
! gfortran main3.f90 dll1.dll -omain3
! gfortran main3.f90 -L. -ldll1 -omain3
module mod3
use ISO_C_BINDING
use ISO_C_BINDING, HANDLE => C_INTPTR_T
use ISO_C_BINDING, C_INTPTR_T => C_INTPTR_T
implicit none
abstract interface
function F(x) bind(C)
import
implicit none
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) :: x
end function F
end interface
interface
function LoadLibrary(lpFileName) bind(C,name='LoadLibraryA')
import
implicit none
!GCC$ ATTRIBUTES STDCALL :: LoadLibrary
integer(HANDLE) :: LoadLibrary
character(kind=C_CHAR) lpFIleName(*)
end function LoadLibrary
function GetProcAddress(hModule,lpProcName) bind(C,name='GetProcAddress')
import
implicit none
!GCC$ ATTRIBUTES STDCALL :: GetProcAddress
type(C_FUNPTR) GetProcAddress
integer(HANDLE), value :: hModule
character(kind=C_CHAR) lpProcName(*)
end function GetProcAddress
end interface
end module mod3
program main3
use mod3, F1 => F
implicit none
double precision x, y
type(C_FUNPTR) ptr
procedure(F1), pointer :: F
integer(HANDLE) hModule
hModule = LoadLibrary('dll1.dll'//C_NULL_CHAR)
ptr = GetProcAddress(hModule,'F'//C_NULL_CHAR)
call C_F_PROCPOINTER(ptr,F)
x = 13
y = f(x)
write(*,*) y
end program main3
Either of the gfortran commands in the comments successfully builds main3.exe.
Note: on retesting, the main2.f90 and main3.f90 methods worked, but for some reason the method with main1.f90 and dll2.def no longer works, failing at runtime because it's trying to find the symbol 'f' in main1.exe rather than dll1.dll. Can't figure out why at this point.
EDIT: Well, I found a way to make the first approach work with the dll1.dll created above and the main1.f90 as it is, but I don't really like it so I'll wait a few days to see if someone else comes up with a more palatable solution. If, after that time, this hasn't happened and someone is still interested, he should remind me and I may post what I came up with.

Related

errors when transforming program into module with subroutine

Given a simple program, that runs just fine, e.g.:
program test
implicit none
real, allocatable :: x(:)
real :: su, si
allocate(x(3))
x(:) = (/1,2,3/)
su=sum(x(:))
si=size(x(:))
end program
I tried to transform it into an external module with a subroutine:
module test
implicit none
real, allocatable, intent(in) :: x(:)
real, intent(out) :: su, si
contains
subroutine sumsize(x(:),su,si)
su=sum(x(:))
si=size(x(:))
end subroutine
end module
However I cannot get it compiled with several error messages of which I believe most are based on x(:), su ,and si all not being DUMMY variables.
Where and how are dummies defined? It seems like this is a simple formal error.
Another error states junk in the subroutine head, and fades when defining it with subroutine sumsize(x,su,si). Why can the input for the subroutine not be a vector?
Compiling with gfortran - gcc v. 8.3.0-6 (Debian) via gfortran -ffree-form -c module_test.f -o test.o.
Dummy arguments, also called function parameters in other programming languages, must be declared inside the function or subroutine.
Also, the argument list (the argument names in the parentheses after the subroutine name) should not contain any additional parentheses.
module test
implicit none
contains
subroutine sumsize(x,su,si)
real, intent(in) :: x(:)
real, intent(out) :: su, si
su=sum(x(:))
si=size(x(:))
end subroutine
end module
Also, you typically do not want the dummy argument to be allocatable. That is normally used when the array is allocated or deallocated inside the subroutine or if you want to allow working with an unallocated array.
program test
implicit none
real, allocatable :: main_x(:)
real :: main_su, main_si
allocate(main_x(3))
main_x(:) = (/1,2,3/)
call sumsize(main_x, main_su, main_si)
end program
I renamed the variables with the main_prefix. It is not needed, it is just to make clear that they are distinct from the names inside the subroutine.

how to call dll in fortran

I have one dll file which contains a function "ProcessWeather" and want to call this function in a fortran code, as shown below:
PROGRAM myprog
!DEC$ ATTRIBUTES DLLIMPORT :: EPLUSWTH
INTERFACE
SUBROUTINE ProcessWeather(InType,OutType,InFileName,OutFileName,ErrFlag)
CHARACTER(len=*), INTENT(IN) :: InType ! InputFile Type
CHARACTER(len=*), INTENT(IN) :: OutType ! OutputFile Type
CHARACTER(len=*), INTENT(IN) :: InFileName ! InputFile Name (Full path)
CHARACTER(len=*), INTENT(IN) :: OutFileName ! OutputFileName (Full path)
REAL(4), INTENT(OUT) :: ErrFlag ! If errors are found,
! set to true and put
! description put in file.
END SUBROUTINE
END INTERFACE
END PROGRAM
The dll file is put to the same place where I put the fortran file to.
When I compile the fortran code, I got the following error:
undefined reference to processweather_
Any suggestion will be highly appreciated.
If you are compiling with gfortran (as your comment seem to suggest) the !$DEC directives will be ignored. But you could use the GCC directives, namely the ATTRIBUTES directive and declare the attribute DLLIMPORT in it.
You could also just use the standard bind(C, name="the_name"). Also, as far as I remember, GCC did not require any attributes to make symbols externally visible when creating a DLL.

Use of IFDEF for giving an attribute

I have the following working situation in a .f90 file that is generating a dynamic library (DLL in Windows, SO in Linux):
#ifdef __linux__
subroutine FCCORR (i1,i2) bind(c, name='FCCORR')
use, intrinsic :: iso_c_binding
#endif
!
#ifdef _WIN32
subroutine FCCORR (i1,i2)
!DEC$ ATTRIBUTES DLLEXPORT, DECORATE, ALIAS : "FCCORR" :: FCCORR
!DEC$ ATTRIBUTES REFERENCE :: i1,i2
#endif
!
integer, intent(in) :: i1
integer, intent(out) :: i2
!
i2 = i1*3
!
end subroutine
I want to know if it's possible to avoid the separation of subroutines in the two different cases: in particular I want to know if it's possible to use the #ifdef for appending the bind attribute only, like this:
subroutine FCCORR (i1,i2)
#ifdef __linux__
bind(c, name='FCCORR')
use, intrinsic :: iso_c_binding
#endif
!
#ifdef _WIN32
!DEC$ ATTRIBUTES DLLEXPORT, DECORATE, ALIAS : "FCCORR" :: FCCORR
!DEC$ ATTRIBUTES REFERENCE :: i1,i2
#endif
!
integer, intent(in) :: i1
integer, intent(out) :: i2
!
i2 = i1*3
!
end subroutine
But obviously line 3 produce an error:
(error #5082: Syntax error, found END-OF-STATEMENT when expecting one
of: :: /
bind(c, name='TEST')
Well, it is possible, but you must use the Fortran rules for line continuation
subroutine FCCORR (i1,i2) &
#ifdef __linux__
bind(c, name='FCCORR')
the problem is that you need to continue the line somehow in the Windows case too and there is no option I can see for that.
But I don't understand why you cant use the bind(C) attribute in Windows as well.
BTW, I definitely can't see why you use use, intrinsic :: iso_c_binding because there is no reference to the module in the code. If the code needed it, it would have been needed also in the Windows version.

Fortran .mod file doesn't update when compiling [duplicate]

I am working with GFortran and CodeBlocks but I'm having an issue about Modules and Multiple files.
i keep getting this error:
Fatal Error: Can't open module file 'mesh.mod' for reading at (1): No such file or directory
For some reason, GFortran is not building the 'mesh.mod' file.
This problem does not occur when I put all the code in a single .f90 file.
Bellow is an example of code that this error happens.
main.f90
MODULE MESH
IMPLICIT NONE
INTEGER :: IMAX,JMAX,NMAX
REAL(8), ALLOCATABLE :: XD(:),YD(:),FX(:,:),FY(:,:)
REAL(8) :: PI,E,DX,DY,H,L,RHO,MU
PARAMETER (PI = ACOS(-1.D0))
PARAMETER (E = 2.718)
END MODULE MESH
!**************************************************************
program Cavity
Use Mesh
implicit none
Real(8), Allocatable :: func(:)
Real(8) :: Der,DfDx
integer :: i
IMAX=10
DX=1./10
Allocate(xd(IMAX),func(IMAX))
Do i=1,IMAX
xd(i)=i*DX
End Do
Do i=1,IMAX
func(i) = xd(i)**2
End Do
Der=Dfdx(func,2)
Write(*,*) Der
End program Cavity
Derivatives.f90
Real(8) Function DfDx(f,i)
Use Mesh
implicit none
Real(8) :: f(1:Imax)
integer :: i
DfDx=(f(i+1)-f(i-1))/(2d0*dx)
return
end function DfDx
When I use console command line compilation instead of CodeBlocks interface I already solved this problem (Compiling Multiple Files with modules) but I'm still getting this problem with CodeBlocks.
Does anyone know how to solve this issue?
Assuming what you have written is how your code is, then it appears that the problem is that the module mesh is inside the main program and not a separate file. You should have three files: Mesh.f90, Derivatives.f90 and Main.f90.
Mesh.f90 is exactly as you have it,
module Mesh
implicit none
integer :: IMAX,JMAX,NMAX
real(8), allocatable :: XD(:),YD(:),FX(:,:),FY(:,:)
real(8) :: PI,E,DX,DY,H,L,RHO,MU
parameter (PI = ACOS(-1.D0))
parameter (E = 2.718)
end module Mesh
Derivatives.f90 should be written as another module, using contains:
module Derivatives
use mesh
contains
real(8) function dfdx(f,i)
real(8) :: f(i:imax)
integer :: i
DfDx=(f(i+1)-f(i-1))/(2d0*dx)
end function dfdx
end module Derivatives
and the Main.f90 will then use both modules. Note that I had to eliminate the variable DfDx; this is because it conflicts with the function DfDx in module Derivatives
program Cavity
Use Mesh
use Derivatives
implicit none
Real(8), Allocatable :: func(:)
Real(8) :: Der
integer :: i
IMAX=10
DX=1./10
Allocate(xd(IMAX),func(IMAX))
Do i=1,IMAX
xd(i)=i*DX
End Do
Do i=1,IMAX
func(i) = xd(i)**2
End Do
Der=Dfdx(func,2)
Write(*,*) Der
End program Cavity
I do not know how CodeBlocks works, but I would presume it lets you choose the compilation order. If that is the case, you should compile Mesh.f90 first, then Derivatives.f90, then compile Main.f90 before linking them to an executable.
When I compiled & linked them, I got an answer of 0.200000002980232; hopefully that links up to what you have as well.
On codeblock, you may go to Project properties > Build targets
Then select the file you want to build first (say mod.f90).
In the "Selected file properties" go to "Build"
Here,change the priority weight. Lower weight implies the file will be built first.
The problem is that in CodeBlocks "projects are built in the order of appearence, from top to bottom" (CodeBlocks Wiki), in other words, the files are compiled alphabetically.
Which means that in my case, Derivatives.f90 was being compiled before than Main.f90 causing the error.
A way to circumvent the problem is to set only the Main.f90 file as build target in CodeBlocks:
Menu Project/Properties...
In Build Target Files at the tab Build targets check only Main.f90
And use the command Include 'File_Name.f90' inside the Main.f90 code to include the other f90 files for compilation in the right order.

object oriented features with Solaris/Oracle Fortran compiler

I have been trying to compile an O-O fortran code with the fortran compiler of the Oracle Solaris Studio 12.4 suite (the latest as far as I know). But the compiler crashes. Here is a simplified version of my problem.
I define two simple types with one type-bound procedure each. One of the procedures has a variable of the other type as dummy argument:
MODULE MY_MODULE
type type0
real :: value = 0
contains
procedure :: print_value
end type type0
type type1
real :: value = 0
contains
procedure :: print_diff
end type type1
CONTAINS
subroutine print_value(self)
class(type0), intent(in) :: self
print*, self%value
end subroutine print_value
subroutine print_diff(self,var0)
class(type1), intent(in) :: self
type(type0), intent(in) :: var0
print*,self%value - var0%value
end subroutine print_diff
END MODULE MY_MODULE
PROGRAM MY_PROG
use my_module, only: type0,type1
type(type0) :: var0
type(type1) :: var1
var0%value = 3
var1%value = 10
call var1%print_diff(var0)
END PROGRAM MY_PROG
This program compiles and executes fine with gfortran:
[> gfortran myprog.f03 -o myprog.x
[> ./myprog.x
7.0
However, compilation with the Solaris f95 crashes:
[> f95 myprog.f03 -o myprog.x
f90: Internal Error, code=fw-interface-ctyp1-796, last src=myprog.f03:4
If I do any further simplification to the source code, then f95 compiles successfully. For instance, it works fine if:
type0 has no type-bound procedure
type1 has no type-bound procedure
procedure print_diff is replaced by a subroutine with no other argument than self
Is there anything I am doing wrong?
Is there an installation problem with my Solaris compiler? Is someone able to compile this code successfully with an other Solaris compiler?
Does someone know what the error code means (I haven't been able to find that out)?
After reporting my problem to Oracle, I just got their answer:
Thank you for reporting this and sorry for the problem. This is a known problem. It has already been fixed in our current development and ported to Studio 12.4. If you have a support contract, you can get the Studio 12.4 patch for it, otherwise the next release will contain the fix.