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

Cannot link Fortran routine to C main

stankoz
Beginner
1,252 Views
Hello,

I posted this in the C++ forum as well since it seems to apply to both. Sorry if this is not proper etiquette. Please bare with me as I am new to VS. I am using the most recent Parallel Studio XE with the most recent C++ and Visual Fortran 12 composers with Windows 7

I am trying to make an algorithm written in C call a Fortran simulation model. The solution file for the simulation model has been provided to me and I added a C project to it with the associate dependancies. I am familiar with most of the nuances when trying to communicate b/w the two languages but I receive an error at the end of the build process as follows:

3>ipo : warning #11021: unresolved swt_v4_
3> Referenced in ipo_76685obj.obj
3>ipo : error #11023: Not all components required for linking are present on command line
3> xilink: executing 'link'
3>ipo_76685obj.obj : error LNK2019: unresolved external symbol swt_v4_ referenced in function func
3>: fatal error LNK1120: 1 unresolved externals


swt_v4_ is the Fortran program that I converted to a subroutine within the C code.

One thing that is concerning me is that under the property pages for the swt_v4 simulation, the configuration type still says application (which was the original intention), but now I just need it to be a routine and the C project to be the application. I am not sure how to specify this within the project properties.

I also get these errors and I don't really understand how to manage libraries:

2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: calloc already defined in LIBCMT.lib(calloc.obj)
2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: free already defined in LIBCMT.lib(free.obj)
2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: printf already defined in LIBCMT.lib(printf.obj)
2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: rand already defined in LIBCMT.lib(rand.obj)
2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: srand already defined in LIBCMT.lib(rand.obj)
2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: fprintf already defined in LIBCMT.lib(fprintf.obj)
2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: fflush already defined in LIBCMT.lib(fflush.obj)
2>MSVCRT.lib(MSVCR100.dll) : error LNK2005: fwrite already defined in LIBCMT.lib(fwrite.obj)
2>LINK : warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use /NODEFAULTLIB:library
2>gmg.lib(SOLVERS.obj) : error LNK2019: unresolved external symbol resprint referenced in function PCG_eval
2>libifcoremt.lib(for_main.obj) : error LNK2019: unresolved external symbol MAIN__ referenced in function main
2>x64\\Release/swt_v4x64.exe : fatal error LNK1120: 2 unresolved externals


I also saw that there is a -nofor_main option for the command line but I can't find where to invoke this in VS

Please let me know what other information you might need in order to help with this issue.

Thanks
0 Kudos
13 Replies
Steven_L_Intel1
Employee
1,252 Views
It would help us help you if you could attach the .sln, .vfpropj and .vcxproj files (ZIP them and then attach).

First of all, what is the name of the Fortran subroutine as shown in the Fortran code? And how do you declare that routine in the C code? You have a trailing underscore which is not Windows standard - it's a Linux convention. Also, Intel Fortran will upcase routine names by default. If you are moving code from Linux to Windows, you may need to make adjustments here.

You need to structure the application as a solution with two projects. A C project that is an executable application (probably a Win32 Console application), and a Fortran project that is a static library. The Fortran project needs to be made a dependent of the C project. It looks to me as if you made the Fortran project a console application or similar, rather than static library.

The other thing I see here, the various MSVCRT.lib errors, are due to having inconsistent settings for the run-time libraries. MSVC defaults to using the DLL libraries whereas Intel Fortran defaults to using static libraries. You need to change one or the other to match. I suggest changing C++ > Code Generation to /MT (static multithreaded).

-nofor_main is not used on Windows.
0 Kudos
stankoz
Beginner
1,252 Views
Thank you Steve. Your response was very helpful and guided me to fixing a few things. The Fortran project was indeed loaded as an application so I deleated it, started a static library project, and loaded the Fortran source code. I recall changing the DLL setting but it must have returned to the default so I changed it to /MT again.

The undefined references have been resolved but there is still an unresolved external symbol swt_v4 during linking trying to link the fortran routines.

Here are some other facts that might be helpful:

Everything here is being built on windows 64bit, although I have tried compiling with gcc through mingw64 as well (and I might try mingw32 next if I cannot get this to work).

I am trying to compile everything with an x64 configuration

The main C routine does not attempt to exchange information yet, I am just trying to compile with the calling statement first.

The solution actually has 3 projects, one being additional C code that is used within the Fortran simulation. These compiled and ran fine together before I wrapped the whole thing in the C optimization code.

I found the VS2010 example of C_calls_Fortran and prepared my project accordingly. The readme says at the bottom that C projects must explicitly name the fortran library as an input to the linker so I added this to the additional dependancies: ..\swtz\$(Platform)\$(Configuration)\swtz.lib

The fortran subroutine is swt_v4. In the C code it is declared as:
extern void swt_v4(double [], double [], int *, int *);
in the header file fun-con.h and then called with:
swt_v4(qs, hs, &lenq, &lenh);

As a side question, do I need to use ISO_C_BINDING in the Fortran code? I believe that I have associated the correct variable types manually so I shouldn't need the (C_INT), (C_DOUBLE) type commands right? Do I still need to use BIND (C) for the subroutine though?

Thank you for your help and patience. I have been browsing forums for four straight days trying to teach myself this stuff. I have found many of your posts in other forums helpful as well.

Zak
0 Kudos
Steven_L_Intel1
Employee
1,252 Views
You don't need USE ISO_C_BINDING unless you are going to use one of the symbols declared in that module, such as C_INT. Whether you "need" BIND(C) depends on what the Fortran code looks like and if you've done other things to address differences between C and Fortran. I can say that based on what you have shown here, adding BIND(C) may fix your problems, but I'd need to see the Fortran code to be sure.

I am puzzled that you say you are getting a reference to swt_v4_ with a trailing underscore. Are you really?
0 Kudos
stankoz
Beginner
1,252 Views
Sorry about the confusion. My approach has been a lot of trial and error. I had initially appended the trailing underscore myself thinking that C needed this. I have since removed it and added the BIND(C) and am no longer getting the unresolved external symbol swt_v4.

The Fortran routine begins like this:

SUBROUTINE swt_v4(Qs,Hs,LENQ,LENH) BIND(C)
USE ISO_C_BINDING
DOUBLE PRECISION, DIMENSION(*),INTENT(IN) :: Qs
DOUBLE PRECISION, DIMENSION(*),INTENT(OUT) :: Hs
INTEGER :: LENQ, LENH

I am then using allocatable global arrays in a Fortran module that are assigned to the incoming C pointers
USE SBLSO
ALLOCATE(QSLOC(LENQ),HSLOC(LENH))
QSLOC = Qs(1:LENQ)
HSLOC = Hs(1:LENH)

The linker is now complaining as follows (and I know this to be an underscoring issue now):

1>ipo : warning #11021: unresolved MF2KGMG_ALLOCATE
1> Referenced in swtz.lib(gmg1.obj)
1>ipo : warning #11021: unresolved MF2KGMG_ASSEMBLE
1> Referenced in swtz.lib(gmg1.obj)
1>ipo : warning #11021: unresolved MF2KGMG_EVAL
1> Referenced in swtz.lib(gmg1.obj)
1>ipo : warning #11021: unresolved MF2KGMG_BIGH
1> Referenced in swtz.lib(gmg1.obj)
1>ipo : warning #11021: unresolved MF2KGMG_UPDATE
1> Referenced in swtz.lib(gmg1.obj)
1>ipo : warning #11021: unresolved MF2KGMG_FREE
1> Referenced in swtz.lib(gmg1.obj)
1>ipo : error #11023: Not all components required for linking are present on command line
1> xilink: executing 'link'
1>swtz.lib(gmg1.obj) : error LNK2019: unresolved external symbol MF2KGMG_ALLOCATE referenced in function GMG1ALG
1>swtz.lib(gmg1.obj) : error LNK2019: unresolved external symbol MF2KGMG_ASSEMBLE referenced in function GMG1AP
1>swtz.lib(gmg1.obj) : error LNK2019: unresolved external symbol MF2KGMG_EVAL referenced in function GMG1AP
1>swtz.lib(gmg1.obj) : error LNK2019: unresolved external symbol MF2KGMG_BIGH referenced in function GMG1AP
1>swtz.lib(gmg1.obj) : error LNK2019: unresolved external symbol MF2KGMG_UPDATE referenced in function GMG1AP
1>swtz.lib(gmg1.obj) : error LNK2019: unresolved external symbol MF2KGMG_FREE referenced in function GMG1DA
1>C:\Users\zstanko\Documents\Visual Studio 2010\Projects\nsga_zexp\x64\rel_int_x64\nsga_zexp.exe : fatal error LNK1120: 6 unresolved externals


These 6 routines are defined in a header to the C source that is a dependant of swt_v4

/* Append underscore to functions called from FORTRAN GMG interface */
#ifdef _UF
#define MF2KGMG_ALLOCATE mf2kgmg_allocate_
#define MF2KGMG_FREE mf2kgmg_free_
#define MF2KGMG_ASSEMBLE mf2kgmg_assemble_
#define MF2KGMG_EVAL mf2kgmg_eval_
#define MF2KGMG_UPDATE mf2kgmg_update_
#define MF2KGMG_BIGR mf2kgmg_bigr_
#define MF2KGMG_BIGH mf2kgmg_bigh_
#endif

Should I add the BIND(C) argument to each of these routines when they are called?
0 Kudos
Steven_L_Intel1
Employee
1,252 Views
BIND(C) would have to go on the declaration of the routine - either the routine itself or an explicit interface for the routine. Let me suggest that if you NOT define _UF in the C build that it may suddenly work....
0 Kudos
stankoz
Beginner
1,252 Views
I tried both changing #ifdef _UF to ifndef _UF and commenting out that whole section. Each resulted in the exact same error. I have no idea what _UF is or what affect it has on the program.

There was instructions on compiling the original Fortran simulation using:

gfortran -c -D_UF -Ilocation file.

where 'location' points to the drive and directory where the include files for gfortran are located.

The Fortran routines that call these C functions are compiled with gmg1.f then placed in swtz.lib. As an example, a routine is declared in the header as
void MF2KGMG_FREE();
then defined in the C subprogram as
void MF2KGMG_FREE(){
and called from the Fortran routine with
CALL MF2KGMG_FREE()

which results in the error
3>ipo : warning #11021: unresolved MF2KGMG_FREE
3> Referenced in swtz.lib(gmg1.obj)


and

3>swtz.lib(gmg1.obj) : error LNK2019: unresolved external symbol MF2KGMG_FREE referenced in function GMG1DA

I am assuming this is either an underscore or a lowercase issue. Considering that these are C routines that are called from the Fortran simulation which is then wrapped in the main C algorithm, could there be some calling convention discrepancy? Does the fact theat the main is now C instead of Fortran mean I need to change the format of these function names and/or declarations?
0 Kudos
stankoz
Beginner
1,252 Views
I believe I am a step closer. I found that the runtime library for the other C project (gmg, which contained the undefined references) had returned to the default DLL as well so I changed that. Then I added the gmg.lib to the dependancies of the main C project (nsga_zexp). Now the only unresolved element is a routine that is burried in the gmg project (resprint) and is called by one of the Fortran routines.

3>ipo : warning #11021: unresolved resprint
3> Referenced in gmg.lib(SOLVERS.obj)
3>ipo : error #11023: Not all components required for linking are present on command line
3> xilink: executing 'link'
3>gmg.lib(SOLVERS.obj) : error LNK2019: unresolved external symbol resprint referenced in function PCG_eval
3>C:\nsga_zexp.exe : fatal error LNK1120: 1 unresolved externals
3>

Shouldn't the resprint function be contained in the gmg.lib?

Do I need to convert this one function into a library to link to the main program or can I just link the object code? In either case, I'm not shure how to do that with VS.
0 Kudos
Steven_L_Intel1
Employee
1,252 Views
If resprint is supposed to be in gmg.lib and the reference to it is within that library, the only thing left is that there is an inconsistency in naming. Do this:

Open a Fortran Command Prompt window (from the Start menu under Intel Parallel Studio XE). Change directory (cd) to the folder containing gmg.lib and type this command:

dumpbin -symbols gmg.lib > gmg.txt

Now open gmg.txt and search it for occurrences of "resprint". Reply here and paste in all of the lines in the file that mention resprint.
0 Kudos
stankoz
Beginner
1,252 Views
I apologize in advance for the verbosity and the onslaught of questions.

Here is the only section that lists resprint:

027 00000000 SECTE notype () External | PCG_eval
028 00000000 UNDEF notype () External | r_zero
029 00000000 UNDEF notype () External | GEN_eval
02A 00000000 UNDEF notype () External | r1_gets_r2_minus_r1
02B 00000000 UNDEF notype () External | r_dotprd
02C 00000000 UNDEF notype () External | resprint
02D 00000000 UNDEF notype () External | r1_gets_r1_plus_cr2


All of the other functions here are declared similarly in the C code but they all seem to be defined elsewhere in the library such as:

0A0 00000000 SECT3F notype () External | GEN_eval

The other thing to consider is that resprint, unlike all the other functions, has this additional section in the header:

/* Append underscore to fortran modual name */
#ifdef _UF
#define resprint resprint_
#endif


Can you explain why these #ifdef sections are used (the other one is in my previous post with all the MF2KGMG issues) and why they are not needed in this case?

I found this in the fortran gmg subroutine interfaces (DOH!)

SUBROUTINE RESPRINT(IOUT,I,RES,CFAC)
! The following line is needed when GMG is compiled with Intel
! Visual Fortran 9.0 and MS Visual C++ .NET 2003. It should
! be treated as a comment by other compilers.
C--SEAWAT: NOTE THAT TO COMPILE FOR X64, USE THE SECOND DEC ATTRIBUTE STATEMENT
!DEC$ ATTRIBUTES ALIAS:'_resprint' :: RESPRINT
c !DEC$ ATTRIBUTES ALIAS:'resprint' :: RESPRINT


I switched the commented attribute line and the whole solution finally compiled and linked.

It is now my understanding that this error occured because this particular function is defined in a fortran subroutine and used in the C code, while the others are all defined in the C files within the gmg routines. In addition, x64 architectures must not add the leading underscore like 32 bit systems do. Can you explain, or point me to documentation that explains, what this !DEC$ statement means? I've never seen it before.

This whole underscore thing is REALLY confusing. Do you have a suggestion for some documentation that attempts to explain the important distinctions b/w operating systems and languages? I did find the whole "Mixed Language Programming" section of the ifort documentation to be thorough and helpful.

Next step: Make sure that the data is being passed back and forth correctly. I have yet to try debugging with VS, but I will read up on it (seems like it might be a nightmare with such mixed programming).

If the fortran simulation is declared here:
extern void swt_v4(double [], double [], int *, int *);

and called within this function:
void func(population *pop_ptr)
{

double qs[20], hs[20];
int lenq=20, lenh=20;

swt_v4(qs, hs, &lenq, &lenh);


My big question now is what is the simplest and/or fastest way to use these variables in the fortran routine? FYI, qs is a vector of decision variables being passes to the simulation and hs is the state variable response vector. Right now its set up as:

SUBROUTINE swt_v4(Qs,Hs,LENQ,LENH) BIND(C)
USE ISO_C_BINDING
USE SBLSO
DOUBLE PRECISION, DIMENSION(*),INTENT(IN) :: Qs
DOUBLE PRECISION, DIMENSION(*),INTENT(OUT) :: Hs
INTEGER :: LENQ, LENH


but I figured I should use local variables for the simulation so I put them in a module (would a common be better?)
MODULE SBLSO
DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: QSLOC
DOUBLE PRECISION, DIMENSION(:), ALLOCATABLE :: HSLOC
End MODULE


then allocate them later in the routine:

ALLOCATE(QSLOC(LENQ),HSLOC(LENH))
QSLOC = Qs(1:LENQ)
HSLOC = Hs(1:LENH)


Could this same thing be accomplished without the module and by adding the VALUE attribute to the shared variables? They do not necessarily need to be allocatable in Fortran, but there length (specified in the C main) might be different from one run to the next.

Eventually, I will need to parallelize the whole thing. Do you recommend the intel MPI package? Where do you think I should start with that? Can this guided auto parallelism be used on the C main or is it only for the fortran compiler?

Your guidance as an expert would be much appreciated moving foreward as I am relatively new to VS, intel, and C. If this is not the place for such questions or too much to ask of you, please just ignore them and I will keep learning through trial and error, such fun!

Thanks Steve, you're the man!
0 Kudos
Steven_L_Intel1
Employee
1,252 Views
As you say, lots of questions.

The !DEC$ lines are "directives" - special lines that, while formatted as comments, are interpreted by the compiler. The typical convention among Fortran implementors is that directives start with ! (or C for fixed form), have two or three letters that are implementation specific, and then a $. What follows is interpreted by the respective compiler. You can read about Intel Fortran's use of directives in the documentation chapter Directive Enhanced Compilation.

In the case of !DEC$ (Intel Fortran also supports !DIR$ as an alternative), the ATTRIBUTES directive modifies defaults for the named symbol. Here, the ALIAS attribute specified that the external name for procedure RESPRINT should be "_resprint" with the leading underscore. Each platform has its own conventions for "name decoration". On Windows, there are three sets of conventions:
  • 32-bit C - an underscore is prefixed and there is no suffix
  • 32-bit STDCALL - an underscore is prefixed and @n is suffixed, where n is the number of bytes of argument list pushed onto the stack
  • 64-bit - no prefix and no suffix

Linux/UNIX has its own conventions, not always uniformly implemented. Typically, there is no name decoration except that for some Fortran compilers (not ours), Fortran names have an underscore suffix. It is this last that the #ifdef _UF is trying to deal with. Whoever wrote that code wanted you to define the preprocessor symbol _UF if the Fortran compiler being used downcased routine names and added the underscore. An example of a compiler where this would be needed is gfortran.

The best way to deal with such things in a mixed-language environment is to use BIND(C), with NAME= optional in the BIND specification. This tells the compiler to downcase the name and apply whatever decoration the corresponding C compiler would use, so you don't have to worry about underscores and the like. If you want an external name other than all lowercase, use BIND(C,NAME='MixedCaseName'). Intel Fortran also supports a DECORATE attribute which, added to ALIAS on the ATTRIBUTES line, would supply the underscore where it is needed and not where it isn't.

Now, on to your next question. I am not sure why you want to have local copies of the arrays. You certainly CAN do that if you want. But at a minimum let me suggest that instead of:

DOUBLE PRECISION, DIMENSION(*),INTENT(IN) :: Qs
DOUBLE PRECISION, DIMENSION(*),INTENT(OUT) :: Hs
INTEGER :: LENQ, LENH

you use instead:

INTEGER(C_INT), INTENT(IN) :: LENQ, LENH ! Declare these first
REAL(C_DOUBLE), DIMENSION(LENQ), INTENT(IN) :: Qs
REAL(C_DOUBLE), DIMENSION(LENH), INTENT(OUT) :: Hs

This makes the sizes of the arrays known to Fortran. If you wanted to make a local copy of one of the arrays, you could create a local ALLOCATABLE and populate it this way:

REAL(C_DOUBLE), ALLOCATABLE, DIMENSION(:) :: QSLOC
...
ALLOCATE (QSLOC,SOURCE=Qs)

as an alternative, you can simply do:

QSLOC = Qs

but only of you have "Enable F2003 Semantics" enabled - this won't work by default.

Any local allocatable arrays will be automatically deallocated when the routine exits. But do you really need these or can you just use the arguments?

As for parallelization, I would first suggest autoparallel - both Intel C++ and Intel Fortran support that. You may also want to use the /guide option (Guided Auto Parallel) in a test build to see what advice the compiler has on improving parallelization (and vectorization).

The next step to consider would probably be OpenMP. You certainly could use MPI, but that's more suited for very large problem sets distributed across a cluster.

Later this year we will be introducing a new version of our Intel Parallel Advisor tool called Intel Advisor XE. This will support Fortran and can be used to evaluate your serial program and suggest where parallelism could be added. This will be part of the Intel Parallel Studio XE suite.

0 Kudos
stankoz
Beginner
1,252 Views
Thanks a million! Almost everything makes sense now.

Does this mean that I can use the BIND(C) instead of the !DEC$ or do they serve different purposes?

I do not need local copies, your suggestion is perfect.

I am mostly used to Linux and gfortran, but just for writing my own programs; I am not a developer.
Is there an easy way to envoke command line options with VS if I know what they are in Linux? This is the major problem I have always had with Microsoft products. Not only do you need to figure out what option you need to specify, but you need to know where to find it in a convoluted mess of icons, menus, and option dialogs. I would like to get used to the VS command prompt, but The command line options visible in the property pages are overly complex:

/Zi /nologo /W3 /O2 /Oi /Qipo /D "_MBCS" /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Qstd=c99 /Fp"x64\rel_int_x64\nsga_zexp.pch" /Fa"x64\rel_int_x64" /Fo"x64\rel_int_x64" /Fd"x64\rel_int_x64\vc100.pdb"

For instance: Yes, Iwant to enable /guide. But in VS, how do I do that? The option for Parallelization in the property pages is /Qparallel which I'm assuming is different. Then the options Tools>Options>Intel Visual Fortran>GAP seem to have what I want but kind of conflicts with /Qparallel, doesn't it? The Intel Fortran documentation is helpful, but a simple search for /guide turns up blank.

I'm pretty sure we will have to use a cluster (we have access to a windows one) or the whole thing could take a month to run (thousands of calls to a complex simulation), even using open MP with a core i7. Maybe I can get our organization to spring for the Advisor XE tool!

Cheers!
0 Kudos
Steven_L_Intel1
Employee
1,252 Views
BIND(C) is part of the "C interoperability" features added in the Fortran 2003 standard. Before F2003, vendors created implementation-specific extensions to ease mixed-language programming. !DEC$ ATTRIBUTES is part of that. If you CAN use the standard C interop features, then my advice is that you should. This will make your code portable across many implementations. There's more than just BIND(C) here - you may want to read the chapter on C interoperability in the Fortran 2008 standard. Links are in the Useful Links section at the top of this forum. A catch is that, at the time I write this, the j3-fortran.org web site is not working, but it may come back later this week. A feature that may not be mentioned directly there is the VALUE attribute which you can put on a dummy argument declaration. This specifies pass-by and receive-by value.

However, not all things one can say in !DEC$ ATTRIBUTES can be expressed in standard Fortran. Still, a lot of what was used for mixed C-Fortran can be replaced with standard-conforming code.

Our documentation lists, for each option, where it is in the IDE (if it is - not all options have VS properties.) We used to have a table that cross-referfenced Windows and Linux options, but it got removed. A trick, though - go to the index and type in -switchname or /switchname, and you'll be taken to the page that describes it. The variants on the other operating systems are listed there.

GAP is an "advisory" option - you do a special build with GAP enabled, along with /Qparallel or /Qopenmp. You don't get an executable from a GAP build, but you get a report that you can look at.
0 Kudos
stankoz
Beginner
1,252 Views
Got it!

I can't thank you enough for your time.

Zak
0 Kudos
Reply