Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
47 Views

Calling conventions error? legacy FORTRAN

Jump to solution

Hey,

I am having trouble calling a function from a legacy program.

I am working in VisualStudio 15 and have two projects. One legacy code project and a F90 project. The compiling works fine but the linking is causing some trouble (unresolved external symbol LNK2019).

In a subroutine of the legacy program i want to call my function 

SUBROUTINE LEG
  USE MODF90

  iStat = FUN_1(array_1)
END SUBROUTINE LEG

 

MODULE MODF90
  PUBLIC :: SUB_1(array_1)
  CONTAINS

 FUNCTION FUN_1(array_1) RESULT(iStat)
   REAL(8), INTENT(IN) :: array_1( : , : )
   INTEGER :: iStat
 END FUNCTION FUN_1

END MODULE MODF90

I was investigating the error and found out the compiler options for the linker are set to CVF /iface:cvf. Unfortunately setting this options back to default is causing a lot off other errors, although the call of my specific function seems to be fixed. I could not really find an explanation about the difference of this settings and i also dont know how i can set up my function to be working with the CVF option.

Greetings Jules

0 Kudos

Accepted Solutions
Highlighted
Valued Contributor II
47 Views

Compaq Visual Fortran used the so-called stdcall calling convention - with the proliferation of 64-bits libraries this calling convention has become obsolete but traces of it still exist. You have apparently run into such a situation.

The default calling convention nowadays is "cdecl". And indeed the caller needs to use the same calling convention as the callee. In this case, I'd say, you should use the !DIR$ compiler directives to set the calling convention for the routine FUN_1 and leave the compile options alone, something along these lines:

!DIR$ ATTRIBUTES STDCALL:: FUN_1

function fun_1( ... )

There is quite a bit information on this stuff in the online documentation - you just have to know the right keywords.

 

View solution in original post

0 Kudos
18 Replies
Highlighted
Valued Contributor II
48 Views

Compaq Visual Fortran used the so-called stdcall calling convention - with the proliferation of 64-bits libraries this calling convention has become obsolete but traces of it still exist. You have apparently run into such a situation.

The default calling convention nowadays is "cdecl". And indeed the caller needs to use the same calling convention as the callee. In this case, I'd say, you should use the !DIR$ compiler directives to set the calling convention for the routine FUN_1 and leave the compile options alone, something along these lines:

!DIR$ ATTRIBUTES STDCALL:: FUN_1

function fun_1( ... )

There is quite a bit information on this stuff in the online documentation - you just have to know the right keywords.

 

View solution in original post

0 Kudos
Highlighted
Black Belt
47 Views

Julian, the second code that you provided is rejected by the compiler.

If you want to get help here with the troubleshooting, you will need to provide a complete set of source files that can be compiled and actually display the behavior that you described.

Furthermore, /CVF is usually an all-or-none option for the files in a project unless you have done some very careful planning. It is certainly not just a linker option.

Adding/removing the /iface:CVF just to avoid compile/link messages will most probably cause your program to crash. One could compare such an action to sweeping hot embers under the carpet.

0 Kudos
Highlighted
Beginner
47 Views

Arjen Markus wrote:

Compaq Visual Fortran used the so-called stdcall calling convention - with the proliferation of 64-bits libraries this calling convention has become obsolete but traces of it still exist. You have apparently run into such a situation.

The default calling convention nowadays is "cdecl". And indeed the caller needs to use the same calling convention as the callee. In this case, I'd say, you should use the !DIR$ compiler directives to set the calling convention for the routine FUN_1 and leave the compile options alone, something along these lines:

!DIR$ ATTRIBUTES STDCALL:: FUN_1

function fun_1( ... )

There is quite a bit information on this stuff in the online documentation - you just have to know the right keywords.

 

Thanks for the replies so far. The suggested solutions sounds exactly like what I need. I will give it a try on Monday at the office.

 

Because its an office project I can not commit the real source code and tried to build an minimal example to explain the situation. There is no really a need to have a working source code in this case because I know where the mistake is (two projects with different calling conventions interacting witch each other).

I switched the CVS option off to find the reason for my linking problems, definetly not to get rid of some compiling errors :) 

 

//Edit:

It worked  the way you suggested Markus. Thanks a lot.

0 Kudos
Highlighted
Beginner
47 Views

hey,

unfortunately a new problem appeared. I am passing an CHARACTER(80) in this function and just the first character is passed. I know that there a difference passing characters in the different calling conventions but shouldn't this be fixed with 

!DEC$ ATTRIBUTES STDCALL :: FUN_1

CVF (/iface:cvf)

SUBROUTINE LEG
  USE MODF90

  CHARACTER(80) :: A

  A = "quiteLongName"
  iStat = FUN_1(array_1, A)
END SUBROUTINE LEG

 

STDCALL (/iface:stdcall)

MODULE MODF90
  PUBLIC :: FUN_1
  CONTAINS

 FUNCTION FUN_1(array_1, A) RESULT(iStat)
   !DEC$ ATTRIBUTES STDCALL :: FUN_1
   REAL(8), INTENT(IN) :: array_1( : , : )
   CHARACTER(80), INTENT(IN) :: A
   INTEGER :: iStat

    WRITE(*,*) A
 END FUNCTION FUN_1

END MODULE MODF90

 

0 Kudos
Highlighted
Black Belt Retired Employee
47 Views

Use ATTRIBUTES CVF to get the correct conventions. STDCALL alone uses pass-by-value so you get one character only. I recommend not using /iface in general - /iface:cvf is not a 100% solution as there are some features (mainly anything involving callbacks) where that doesn't work.

0 Kudos
Highlighted
Black Belt
47 Views

You should look into using the /iface:mixed_str_len_arg compiler option if you are going to pass character arguments in legacy code using the Stdcall convention.

Unless you need to use a large library for which you do not have source code and/or a modern replacement, I suggest that you do away with /iface:cvf, etc.

0 Kudos
Highlighted
Beginner
47 Views

Steve Lionel (Ret.) wrote:

Use ATTRIBUTES CVF to get the correct conventions. STDCALL alone uses pass-by-value so you get one character only. I recommend not using /iface in general - /iface:cvf is not a 100% solution as there are some features (mainly anything involving callbacks) where that doesn't work.

Am I not going back to the original Problem by doing this or do I misunderstand you? 

!DEC$ ATTRIBUTES CVF :: FUN_1

I anyway tried it but I can not compile.

 

I found out that I somehow can force arguments passed by reference instead by value, which should solve the problem. http://csbi.mit.edu/technology/intel_fce/doc/main_for/mergedProjects/bldaps_for/common/bldaps_attrpr...

!DEC$ ATTRIBUTES STDCALL :: FUN_1
!DEC$ ATTRIBUTES REFERENCE :: A

But unfortunately its not working. "undefined address" VS says.

 

Also the option with the mixed_str did not work.

0 Kudos
Highlighted
47 Views
 FUNCTION FUN_1(array_1, A) RESULT(iStat)
   !DEC$ ATTRIBUTES STDCALL :: FUN_1
   REAL(8), INTENT(IN) :: array_1( : , : )

Regardless of calling convention, array_1 is (is expected/required to be) a reference to an (The current compiler's) array descriptor due to the (:,:). The caller may be passing in the base of the array. If this is a case of Fortran calling Fortran, then the array descriptor formats must agree. For C calling Fortran, well C/C++ knows nothing about Fortran array descriptors.

Jim Dempsey

0 Kudos
Highlighted
Beginner
47 Views

jimdempseyatthecove wrote:

 FUNCTION FUN_1(array_1, A) RESULT(iStat)
   !DEC$ ATTRIBUTES STDCALL :: FUN_1
   REAL(8), INTENT(IN) :: array_1( : , : )

Regardless of calling convention, array_1 is (is expected/required to be) a reference to an (The current compiler's) array descriptor due to the (:,:). The caller may be passing in the base of the array. If this is a case of Fortran calling Fortran, then the array descriptor formats must agree. For C calling Fortran, well C/C++ knows nothing about Fortran array descriptors.

Jim Dempsey

Thanks for your reply.

The array is passed perfectly allright as a refernce, as it is mentioned in the source I found

http://csbi.mit.edu/technology/intel_fce/doc/main_for/mergedProjects/bldaps_for/common/bldaps_attrpr...

Like Lionel mentioned the stdcall is passing just the first character of a string. This is the problem I need to find a solution for, that the whole string is passed. This is why I tried to pass A explicitly as a reference. Unfortunately it did not work.

0 Kudos
Highlighted
Black Belt Retired Employee
47 Views

You don't say which compiler version you are using. If I recall correctly, ATTRIBUTES CVF was added in version 16. It is shorthand for:

ATTRIBUTES STDCALL, REFERENCE, MIXED_STR_LEN_ARG, ALIAS:"UPPERCASENAME"

0 Kudos
Highlighted
Beginner
47 Views

Steve Lionel (Ret.) wrote:

You don't say which compiler version you are using. If I recall correctly, ATTRIBUTES CVF was added in version 16. It is shorthand for:

ATTRIBUTES STDCALL, REFERENCE, MIXED_STR_LEN_ARG, ALIAS:"UPPERCASENAME"

Ok. I have the Intel® Parallel Studio XE 2015 so V15 instead of V16. I tried the following code:

FUNCTION FUN_1(array_1, A)
!DEC$ ATTRIBUTES STDCALL, REFERENCE, MIXED_STR_LEN_ARG :: FUN_1
  IMPLICIT NONE

  REAL(8), INTENT(IN) :: array_1( : , : )
  CHARACTER(256), INTENT(IN) :: A

END FUNCTION FUN_1

Its compiling fine, like in the attempt i came up by myself,

!DEC$ ATTRIBUTES STDCALL :: FUN_1
!DEC$ REFERENCE :: A

but debugging it, shows me the address is not passed correctly. Visual Studio is telling me "undefined address". I am really struggling solving this so easy looking problem.

 

Ok its working. Just a stupid mistake on my side. After "cleaning" the solution files and compiling from scratch its working. Thank you all.

0 Kudos
Highlighted
Valued Contributor III
47 Views

I have just read through this thread from the start. It seems you are trying different calling conventions and naming to 'get it to work' with some  trial and error basis. So, you have some Fortran source (does this form the main program?) that you compile that presumably have to link with some third party libraries that you do not have source  for? What are these libraries? How are they built -language/compiler/version/options? This leads to the name/calling conventions required? Getting your code to compile and link is no guarantee it will work, you need the correct settings! I think you need to take a step back and review and then provide some more 'overview' detail of your problem. People here have all the necessary knowledge to give answers but the correct questions are needed.

 

 

0 Kudos
Highlighted
New Contributor II
47 Views

Hi

To detect the naming convention, you can edit the external lib or obj files in an hexadecimal editor and you will find the real name of your functions somewhere at the end of the file. It would be a clue to the calling convention.

0 Kudos
Highlighted
Beginner
47 Views

andrew_4619 wrote:

I have just read through this thread from the start. It seems you are trying different calling conventions and naming to 'get it to work' with some  trial and error basis. So, you have some Fortran source (does this form the main program?) that you compile that presumably have to link with some third party libraries that you do not have source  for? What are these libraries? How are they built -language/compiler/version/options? This leads to the name/calling conventions required? Getting your code to compile and link is no guarantee it will work, you need the correct settings! I think you need to take a step back and review and then provide some more 'overview' detail of your problem. People here have all the necessary knowledge to give answers but the correct questions are needed.

 

 

 

In my inital post I was quite clear and provided a minimalistic code base to desribe the problem. Two Visual Studio Projects should be combined to one solution. This is an colaboration between me and an existing project. I have the source code of both projects. The existing project have the setttings /iface:CVF and my project has the settings /iface:stdcall. From the existing project I am calling a function of my actual project. To be able to call this function the correct way, I implemented the suggestions of Lionel, who seems to understand the problem. First he explained me with which settings i can compile and link my program. 

!DEC$ ATTRIBUTES STDCALL :: FUN_1

Further on he explained me why instead of a full string just the first character was passed into the function and provided the necessary solution

!DEC$ ATTRIBUTES STDCALL, REFERENCE, MIXED_STR_LEN_ARG :: FUN_1

Yes I tried to build a solution in visual studio and got an error, then i tried the solution Lionel and others provided, I got an other error, I tried the new suggested solution and it works. Not sure why this should be not a valid approach ;)

0 Kudos
Highlighted
Valued Contributor III
47 Views

So you have all the sources and all the sources are in Fortran? If so you do not want any non-default compiler options for calling convention or names and you do not want any directives either. Recompile everything with default calling and naming and it should work.

0 Kudos
Highlighted
Beginner
47 Views

andrew_4619 wrote:

So you have all the sources and all the sources are in Fortran? If so you do not want any non-default compiler options for calling convention or names and you do not want any directives either. Recompile everything with default calling and naming and it should work.

Thank you for your reply.

I just have the sources of the caller project and my project. The Project, which is calling my function is in the need of this compiler settings unfortunately because of other dependencies. Yes I really do not want this :)

0 Kudos
Highlighted
Valued Contributor III
47 Views

Julian H. wrote:
The Project, which is calling my function is in the need of this compiler settings unfortunately because of other dependencies. Yes I really do not want this :)

What other dependencies? You said there are no external libraries involved.

I am also confused by your use of the word "project" which has a distinct meaning in Visual Studio. Do you really have two separate projects in visual studio? What type of projects are they e.g. console, static lib, dll? The code snippets in original post would suggest that the called function is in the same Visual Studio project as the caller unless the caller project includes the MOD and OBJ files of the function...... 

 

0 Kudos
Highlighted
Beginner
47 Views

andrew_4619 wrote:

Quote:

Julian H. wrote:
The Project, which is calling my function is in the need of this compiler settings unfortunately because of other dependencies. Yes I really do not want this :)

 

What other dependencies? You said there are no external libraries involved.

I am also confused by your use of the word "project" which has a distinct meaning in Visual Studio. Do you really have two separate projects in visual studio? What type of projects are they e.g. console, static lib, dll? The code snippets in original post would suggest that the called function is in the same Visual Studio project as the caller unless the caller project includes the MOD and OBJ files of the function...... 

 

It is also used in a different solution. Therefore I would like to keep the compiler settings the same.

I am pretty much aware of the distinct meaning of "project" and used it on purpose. But anyway a working approach is already found and I am happy with that :) but still thank you for your effort to try to dig deeper.

0 Kudos