Linking Fortran and C++ Binaries Using Gcc

Linking Fortran and C++ binaries using gcc

You're looking for
g++ main.o print_hi.o -o main -lgfortran to link in the standard Fortran libraries.

You can also use gfortran by passing -lstdc++.

How to properly link gfortran and gcc?

Firstly, your C program testigrf.c is defective. As it is,
you have the warning:

testigrf.c: In function ‘main’:
testigrf.c:23:5: warning: implicit declaration of function ‘igrf12syn_’ [-Wimplicit-function-declaration]
igrf12syn_(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^

which occurs because your have declared a function called igrf12syn and invoked
one called igrf12syn_, about which the compiler knows nothing.

Change igrf12syn_ to igrf12syn, recompile, and then the errors revealed are:

testigrf.c: In function ‘main’:
testigrf.c:23:26: warning: passing argument 3 of ‘igrf12syn’ from incompatible pointer type [-Wincompatible-pointer-types]
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘int *’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:44: error: incompatible type for argument 7 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:46: error: incompatible type for argument 8 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:48: error: incompatible type for argument 9 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:50: error: incompatible type for argument 10 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^

Read the diagnostics and fix the errors before you go any further. They
might be fixed, for example, by changing:

double r, th, ph,year,alt2,lat,colat,elong,x,y,z,f;

to

double r, th, ph,year,alt2,lat,colat,elong,x,y,z,f,itype;

and changing:

igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);

to:

igrf12syn(&isv,&year,&itype,&r, &th, &ph,&x,&y,&z,&f);

That will satisfy the compiler, though it may not express your intentions,
which are not clear to me.

You understand that you cannot link two programs into a single program,
since the linker will find duplicate main functions. You need to link
the C main program just with the Fortran subroutine igrf12syn.

Next, download and save the file https://www.ngdc.noaa.gov/IAGA/vmod/igrf12.f.
Open it in a text/programming editor. Select all the of lines - the complete
lines, including leading space - that compose the subroutine igrf12syn, from:

  subroutine igrf12syn (isv,date,itype,alt,colat,elong,x,y,z,f)

to:

 end

Save this selection exactly as copied in a new file igrf12syn.f in the
same directory as testigrf.c.

Then, open a console in that directory and execute the following commands:

$ gcc -c -o testigrf.o testigrf.c
$ gfortran -fno-underscoring -c -o igrf12syn.o igrf12syn.f
$ gfortran -o testigrf testigrf.o igrf12syn.o

These will compile and link your program as testigrf in the same directory.

Finally, repeat the first two commands, each with the additional option -Wall,
to see the warnings that you are not yet aware of. Consider fixing the program
to remove the warnings, as they may well mean that the program will not
behave as you expect. Always include -Wall in your gcc/g++/gfortran compiler
options when compiling code of any importance to you.

Linking a Fortran program with an arbitrary binary file

As mentioned in the a comment, you want to use the iso_c_binding intrinsic Fortran module to provide C interoperability features.

The following code/example is produced with GCC 4.9.2 on x86_64-pc-linux-gnu.

Scalar data

Consider the following Fortran:

module data
use iso_c_binding
implicit none
real(kind=c_double), bind(C) :: val
end module

program fbin
use data
implicit none
print *,val
end program

This uses a Fortran module containing the variable var which is compatible with the C type double. The bind(C) attribute will cause the variable exposed in the object file to be unmangled (e.g. var with no underscoring). Note the use of the module is necessary because you cannot apply the bind attribute in the main program scope (or at least GCC won't allow this, I haven't consulted the standard).

I used your code to produce data.bin, but for my system I had to modify the objcopy as follows:

objcopy -I binary -O elf64-x86-64 -B i386 --redefine-sym _binary_data_bin_start=val data.bin data.o

The changes are the bfdname to produce an object my system can consume and changing the symbol name to plain val to match what the Fortran compiler will expect.

The rest follows as your example:

% objdump -t data.o                                                                          

data.o: file format elf64-little

SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g .data 0000000000000000 val
0000000000000008 g .data 0000000000000000 _binary_data_bin_end
0000000000000008 g *ABS* 0000000000000000 _binary_data_bin_size

and

% gfortran -o fbin fbin.f90 data.o
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/../../../../x86_64-pc-linux-gnu/bin/ld: Warning: alignment 1 of symbol `val' in data.o is smaller than 8 in /tmp/ccYcttlP.o

Note that this warning does not cause the program to fail, but will impact the performance of the program. I'm not sure if you'll have alignment issues on your platform. Finally:

% ./fbin 
3.1415926535897931

Success.

Array data

Here is a modified build.c to produce a 1-d array of values for testing:

#include <math.h>
#include <stdio.h>

int main() {
FILE *f;
int i;

double x;
f = fopen("data.bin", "wb");
for (i=0; i<15; i++) {
x = M_PI*i;
fwrite(&(x), sizeof x, 1, f);
}
fclose(f);
return 0;
}

This produces data.bin with 15 8-byte values from 0 to 14*M_PI. The data.o is produced with the same command as in my scalar example.

Here is an example Fortran program that is hardcoded to load a rank 2 array with shape (3,5).

module data
use iso_c_binding
implicit none
integer, parameter :: n = 3
integer, parameter :: m = 5
real(kind=c_double), target, bind(C) :: val
real(kind=c_double), dimension(:,:), pointer :: array

contains

subroutine init_fort_array()
use iso_c_binding
implicit none
call c_f_pointer(c_loc(val), array, [n,m])
end subroutine
end module

program forbin
use data
implicit none
integer :: j
call init_fort_array()
print '(5(f8.5,2x))',(array(1:n,j), j=1,m)
end program

This is a little less straightforward and is perhaps not the only way to accomplish this, but it is the one that came to mind. The module has a scalar value val and an array pointer. A subroutine gets the address of val with c_loc and associates the Fortran pointer array with that address and using the given shape. You just need to call that subroutine in your code and then array works as expected.

This array is hardcoded but you could use other data files (or a single data file with multiple symbols) to pack the array rank and dimensions into the data object and then change the Fortran to use a shape dependent on the actual data.

No example would be complete without output:

% ./fbin                          
0.00000 9.42478 18.84956 28.27433 37.69911
3.14159 12.56637 21.99115 31.41593 40.84070
6.28319 15.70796 25.13274 34.55752 43.98230

Note that Fortran and C do not use the same array ordering. C is row-major and Fortran is column major and you can see this in how my Fortran loop prints the data.

Hybrid programming of Fortran and C++: Fortran can not call C++ subroutines

You need to link the executable against the c++ standard library:

g++ -c sub1.1.cxx
gfortran -o test sub2.f90 sub1.1.o -lstdc++

With gfortran/g++ you can also use g++ to link against the fortran library:

g++ -c sub1.1.cxx
gfortran -c sub2.f90
g++ -o test sub1.o sub2.o -lgfortran

Compile and link Fortran and C with ifort and icc

The -static flag will link all the libraries statically. In that case you need to have a static version (the .a files) of every library. For example, using -lm will search for libm.a. Those libraries are not installed by default, but may be in the -dev or -devel packages of your distribution.
If you only want to link statically the Intel libraries, then you should use -static-intel.

A good trick to avoid static linking is to:

1) Dynamically link your program with -static-intel and -Wl,-rpath=./lib

2) Use ldd to find which libraries your program needs

3) Create a directory lib where you copy all the required dynamic libraries

4) Instead of distributing your code as a single static binary you can disrtibute it as a binary + the lib directory (assuming the licenses of the libraries permit it).

Finally, if you need to try more things, I have succeeded to link an Intel Fortran file with gcc using this command:

$ gcc fortran_file.o c_main_file.o -lifcore -lirc -lcomposerxe_gen_helpers_core_2.3

hope this helps...

Mixed C++ and Fortran Linking Issue

There are few issues here that don't let names of the objects match. First, specify in the C++ code that the external functions have the C signature:

In test.cpp:

extern "C" int Add( int *, int * );
extern "C" int Multiply( int *, int * );

See In C++ source, what is the effect of extern "C"? for more details.

In your Fortran code, make the interface explicit by placing procedures in the module, and use iso_c_binding to let Fortran objects appear as valid C objects. Notice that we can explicitly specify the names of the objects that the C or C++ programs will see through the bind keyword:

test_f.f90:

module mymod
use iso_c_binding
implicit none

contains

integer(kind=c_int) function Add(a,b) bind(c,name='Add')
integer(kind=c_int) :: a,b
Add = a+b
end function

integer(kind=c_int) function Multiply(a,b) bind(c,name='Multiply')
integer(kind=c_int) :: a,b
Multiply = a*b
end function

endmodule mymod

Compile (don't mind me using the Intel suite, my g++ & gfortran are very old):

$ ifort -c test_f.f90 
$ icpc -c test.cpp

Link:

$ icpc test_f.o test.o

Executing a.out should now work as expected.



Related Topics



Leave a reply



Submit