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

Intel Fortran External Library Linking Issue

Lamb__Tripp
Novice
1,231 Views

I am trying to create a Fortran project that uses two different libraries. One is a static lib and one is a dll. The static lib uses the `default` calling convention. The dll uses the `iface:cref` calling convention. If I use either on their own everything is fine by changing the Calling Convention project setting; however, I can't seem to get the compiler options to get the project to build using both. Is there a way to get this to work?

Using Intel Fortran 2019 Update 5, Visual Studio 2017, and Windows 10

0 Kudos
1 Solution
Steve_Lionel
Honored Contributor III
1,231 Views

On Windows IA-32 (32-bit), there are two different calling conventions: C and STDCALL. They differ in who is responsible for popping arguments off the stack. With the C convention, the caller does it, but with STDCALL, the called procedure does it through a value on the RET instruction, thus saving one instruction. Since the called routine needs to know exactly how much to pop, there is a naming convention for STDCALL routines where the name ends in @n, where n is the number of bytes to pop. This protects you against calling the routine with the wrong number of arguments.

On x64, there is only one convention: C.

The /iface option changes more than the convention. /iface:stdcall downcases the routine name and passes most things by value. /iface:stdref is like stdcall except arguments are passed by reference. /iface:cref again downcases routine names. The Intel Fortran default is uppercase names and the C convention, but this is not the same as any of the /iface options.

The first thing you need to know is the exact spelling, including case, of each routine you want to call. Given the last sentence in your most recent reply, I wonder if STDCALL is being used at all.

What I would recommend is opening a Fortran command prompt window, setting default (cd) to the folder with a library, and typing the command:

dumpbin -symbols thislib.lib > thislib.txt

where thislib.lib is the name of your library. This will create the text file containing (among other things), the exact global name of each routine in the library. Are they upper case, lower case or mixed-case? Do the names end with @n or not? Are there trailing underscores for any name? (This is a Linux/UNIX convention you should not see on Windows.)

Since you say that "One links correctly with default and one links correctly with cref.", STDCALL is probably not in play and you're simply dealing with downcased names. There's a simple way to deal with those, and a better way.

The simple way is to add a line that looks like this:

!DEC$ ATTRIBUTES ALIAS:"routinename" :: ROUTINENAME

for each routine in the library with lowercase names. This must be in the same program unit that calls the routine.

The better solution is to write an interface block, but this means you must know what the arguments are like. An example might be:

interface
  function example (a,b) bind(C)
  use, intrinsic :: ISO_C_BINDING
  integer(C_INT) :: example ! Return value
  real(C_FLOAT), value :: a,b
  end function example
end interface

The bind(C) downcases the name. I've made up types of the return value and arguments and shown how they would be declared. The VALUE attribute is needed if the argument is passed by value.

You can use derived types here too. If the routines you're calling were written in C, it would be necessary to add BIND(C) to the TYPE declaration as otherwise you could not pass an object of that type to a BIND(C) routine. 

If the restrictions of BIND(C) are too much, you can do this another way.

View solution in original post

0 Kudos
6 Replies
Steve_Lionel
Honored Contributor III
1,231 Views

Sure - don't use the compiler options to change the calling conventions. Instead, use explicit interfaces for the routines in the STDCALL library that include an appropriate !DEC$ ATTRIBUTES STDCALL directive. You may also need REFERENCE and possibly ALIAS and DECORATE.

0 Kudos
Lamb__Tripp
Novice
1,231 Views

EDIT: This has worked, but now it needs me to add these declarations to hundreds of other functions that the top level ones are dependent on. Is this expected behavior? I'm still interested in the below questions as well if you have time.

________________________

That sounds like just what I need, but I don't understand the implementation of what you said well enough. I've been looking for something like this for a while but I've been unable to find it. Can you direct me to some documentation or examples?

Also, is there anything I should be aware of when using this with methods of derived types?

Maybe I am misunderstanding what you mean by STDCALL, but if I use that calling convention option neither library links correctly. One links correctly with default and one links correctly with cref.

0 Kudos
Steve_Lionel
Honored Contributor III
1,232 Views

On Windows IA-32 (32-bit), there are two different calling conventions: C and STDCALL. They differ in who is responsible for popping arguments off the stack. With the C convention, the caller does it, but with STDCALL, the called procedure does it through a value on the RET instruction, thus saving one instruction. Since the called routine needs to know exactly how much to pop, there is a naming convention for STDCALL routines where the name ends in @n, where n is the number of bytes to pop. This protects you against calling the routine with the wrong number of arguments.

On x64, there is only one convention: C.

The /iface option changes more than the convention. /iface:stdcall downcases the routine name and passes most things by value. /iface:stdref is like stdcall except arguments are passed by reference. /iface:cref again downcases routine names. The Intel Fortran default is uppercase names and the C convention, but this is not the same as any of the /iface options.

The first thing you need to know is the exact spelling, including case, of each routine you want to call. Given the last sentence in your most recent reply, I wonder if STDCALL is being used at all.

What I would recommend is opening a Fortran command prompt window, setting default (cd) to the folder with a library, and typing the command:

dumpbin -symbols thislib.lib > thislib.txt

where thislib.lib is the name of your library. This will create the text file containing (among other things), the exact global name of each routine in the library. Are they upper case, lower case or mixed-case? Do the names end with @n or not? Are there trailing underscores for any name? (This is a Linux/UNIX convention you should not see on Windows.)

Since you say that "One links correctly with default and one links correctly with cref.", STDCALL is probably not in play and you're simply dealing with downcased names. There's a simple way to deal with those, and a better way.

The simple way is to add a line that looks like this:

!DEC$ ATTRIBUTES ALIAS:"routinename" :: ROUTINENAME

for each routine in the library with lowercase names. This must be in the same program unit that calls the routine.

The better solution is to write an interface block, but this means you must know what the arguments are like. An example might be:

interface
  function example (a,b) bind(C)
  use, intrinsic :: ISO_C_BINDING
  integer(C_INT) :: example ! Return value
  real(C_FLOAT), value :: a,b
  end function example
end interface

The bind(C) downcases the name. I've made up types of the return value and arguments and shown how they would be declared. The VALUE attribute is needed if the argument is passed by value.

You can use derived types here too. If the routines you're calling were written in C, it would be necessary to add BIND(C) to the TYPE declaration as otherwise you could not pass an object of that type to a BIND(C) routine. 

If the restrictions of BIND(C) are too much, you can do this another way.

0 Kudos
Lamb__Tripp
Novice
1,231 Views

This is incredibly helpful. Thank you. I'll dig into it tomorrow and try out the interface block and try some more with the !DEC$. I'm also very interested in the command to get the exact names, until now I have never heard of a way to get that.

The static lib that works using default, is a library I'm in charge of so if there are better ways to link everything there I have access to do that. I don't have any access to the DLL library code, so the dumpbin command seems incredibly helpful there.

Once again thank you.

0 Kudos
mecej4
Honored Contributor III
1,231 Views

Lamb, Tripp wrote:
The static lib that works using default, is a library I'm in charge of so if there are better ways to link everything there I have access to do that. I don't have any access to the DLL library code

In that case, the simplest way to build is to compile both the static library sources and the EXE sources with the /iface:cref compiler option; rebuild the static library, and then link the OBJ files of the EXE with the static library and the import library of the DLL.

No changes to the sources would be needed if this approach is acceptable.

0 Kudos
Lamb__Tripp
Novice
1,231 Views

@ mecej4, I must have been working on this too long last week because that is definitely the most elegant solution for my current problem. I came to the same thought on Sunday after I had left it alone for a couple days. So, far this has worked really well.

I'm glad I have a better understanding of it all now, as I see more Fortran interoperability in my near future. Thank you both for your help.

0 Kudos
Reply