C calling Fortran linkage problem

C calling Fortran linkage problem

Terry G.'s picture

Scenario:

A visual Studio solution with two very simple projects:

  1) A "C" project with the following main routine:

          #include <stdio.h>
          void hello_world();
          int main(int argc, char* arg[])
          {
               hello_world();
               printf("Finished\n");
                return 0;
           }

2: A "F90" static library  project with the following routine:

          subroutine hello_world()  
               write(*,*) 'hello world'
          end subroutine hello_world

Problem:

The F90 static library  builds OK.

Building the "C" client project  yields:

1>------ Build started: Project: CMAIN, Configuration: Debug Win32 ------
1>  main.c
1>main.obj : error LNK2019: unresolved external symbol _hello_world referenced in function _main
1>C:\LOCAL\CODE\LANGUAGES\FORTRAN\IVF\INTEROP\VSP12.C.F.INTEROP.S1\Debug\CMAIN.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Properties configuration:

The C project link property page has the location of the Fortran library as well as the library name specified.

Any suggestions?

10 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.
IanH's picture

You need to tell the Fortran compiler (via your source code) that the subroutine will be called from C.

Add the suffix BIND(C, NAME='hello_world') to the subroutine statement in the Fortran code.  This is a feature from the Fortran 2003 language standard.  (The NAME specifier might be redundant given the name on the C side is already lower case, but it doesn't hurt.)

Otherwise you need to match the name of the function in C with the name that the Fortran compiler generates without the BIND(C) suffix.  That name is platform/compiler/compiler options specific.

Tim Prince's picture

Besides the primary points IanH mentioned, you should expect surprises writing to stdout with multiple languages.

If you don't want to follow Ian's advice, dumpbin et al should become your friends so you can see the results of default mangling and upper-casing.

Within Fortran, a procedure with empty arguments has undefined results without explicit interface.

IanH's picture

Quote:

TimP (Intel) wrote:Within Fortran, a procedure with empty arguments has undefined results without explicit interface.

What do you mean by that?

Terry G.'s picture

That will work.

But, suppose I now add a third project - one with a Fortran client that calls the same function within rhe Fortran static library:

(In other words - both C and Fortran clients will call into this static library)

 Project #3 follows:

program main  

    call hello_world()   

    write(*,*) "Finished"

end

Again, We specify the static lib name and path in the "linker" property page

Now this one will NOT link to the static library's "hello_world" function with the "bind" annotation.

If I remove the "bind" annotation, project #3 (Fortran main) will then compile and link without errors. - But then the C project wont link!

Catch-22?

IanH's picture

That's because you need to explicitly tell the compiler when it is compiling Project #3 that hello_world is a subroutine that has been compiled to be interoperable with C.  You do this by having an explicit interface for the procedure accessible in the scope where the procedure is referenced.  Then the compiler has explicit knowledge about the characteristics of the procedure (as opposed to implicit knowledge obtained by looking at the procedure call).

The easiest (and in my opinion, best, by far) way of providing an explicit interface is to put the subroutine in a module, and then use that module in the program unit in Project #3. A module procedure automatically has an explicit interface in program units that use the module.

(Internal procedures also automatically get an explicit interface, but use of BIND(C) with internal procedures is a Fortran 2008 standard feature.)

An alternative is to provide an interface block for the subroutine in the specification part of the the scope where the the subroutine is called.  Something like:

INTERFACE
SUBROUTINE hello_world() BIND(C,NAME='hello_world')
END SUBROUTINE hello_world
END INTERFACE

In my opinion (and some disagree) the module approach is much more robust, to the extent that "stand-alone" external subprograms should only really be used where absolutely necessary.

Terry G.'s picture

That worked.

The missing ingredient to the recipe is the interface statement WITH the  "bind" annotation.

BTW timP, the above code is only to illustrate the problem I was encountering - I dont need a "hello world" routine buried in a Fortran static lib.

And dumpbin  does report the Fortran hello_world subroutine name as "_HELLO_WORLD"  - the presence of the"bind" attribute does not appear to "mangle" the name. Also, note the leading underscore.

For folks visiting this thread in the future, here is the code. ( I am using one Visual Studio solution with three projects):

The working version of the Fortran main linking to the Fortran static lib (that can also link to a C main) is:

program main

  interface
     subroutine hello_world()  bind(c,name='hello_world')
     end subroutine hello_world
  end interface

  call hello_world()

  write(*,*) "Finished"

end

And the working version of the Fortran static library subroutine is:

subroutine hello_world()  bind(c, name='hello_world')
 
  write(*,*) 'hello world'
 
end subroutine hello_world

And the working version of a  C language main that calls the Fortran static library's subroutine is:

#include <stdio.h>

void hello_world();

int main(int argc, char* arg[])
{
  hello_world();

  printf("Finished\n");

  return 0;
}

mecej4's picture

Perhaps, part of your (Terry G.) confusion is traceable to how external symbols that are defined or referenced in Fortran and C source files are given name decoration by the Fortran and C compilers.

Unless you are using and adhering to the tight rules of Fortran-C Interoperability, you may not assume that either compiler knows or cares about how the other compiler does name decoration. Rather, you, the programmer, are in control. Most of your linker problems are related to mismatches such as Fortran producing _HELLO_WORLD as an unsatisfied reference, if you leave out the "bind(C)..." qualifier, and C producing _hello_world as a globally visible text section symbol.

For the linker to succeed in producing an EXE or DLL, the decorated symbols output by both compilers must match exactly (unless you tell the linker to ignore the case of symbols).

Terry G.'s picture

 mecej4,  Can you provide a link to a description of the complete linkage process when interface between C and Fortran?

Might be a good read tonight.

Your comments lead me to another experiment.

First, remove the bind annotation from the Fortran static library subroutine.

subroutine hello_world()  
 
  write(*,*) 'hello world'
 
end subroutine hello_world

Next modify the C main routine to:

#include <stdio.h>

void HELLO_WORLD();

int main(int argc, char* arg[])
{
  HELLO_WORLD();

  printf("Finished\n");

  return 0;
}

And modify the Fortran main routine by removing the "bind" annotation:

program main

  interface
       subroutine hello_world()  
      end subroutine hello_world
    end interface

  call hello_world()

  write(*,*) "Finished"

end

Issue a "build solution" command....

Result:

========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Now, we can run both the C main and the Fortran main using the Fortran static lib subroutine without using the bind annotation.

So if you use uppercase for Fortran subroutine names in your C code, you cant slip by without the "bind" annotation!

Which means I dont have to change anything in the actual Fortran static library (which is quite large and is maintained by anther group) Ito use in my real code.

Probably not the "appropriate" way - but it works!   :)

Lesson: External names are CASE SENSITIVE during the linkage process

IanH's picture

Be mindful that if you take out the BIND(C), then tomorrow if you change the platform, compiler, compiler version, compiler options or the direction that you are facing while compiling, then your program may fail to link again.  BIND(C) was added to the language for good reasons.  While it is a Fortran 2003 language standard feature, it is pretty widely supported across actively maintained Fortran compilers.

You can always write an interface layer that links your C code with your third party static library.  Your Fortran code can either call into the C interface layer or go directly to the Fortran library.  For example:

MODULE my_c_interface_layer
  IMPLICIT NONE
CONTAINS
  SUBROUTINE hello_world_c() BIND(C, NAME='hello_world_c')
    INTERFACE
      ! Interface for the Fortran subroutine in the static library 
      ! that doesn't have BIND(C)
      SUBROUTINE hello_world()
      END SUBROUTINE hello_world
    END INTERFACE
    CALL hello_world
  END SUBROUTINE hello_world_c
END MODULE my_c_interface_layer

void hello_world_c();
int main()
{
  hello_world_c();
  return 0;
}

Login to leave a comment.