I would like to link a Fortran program with an arbitrary binary file. I am using gfortran, and I found here that the same task is easy with gcc using objcopy from binutils.
However, I can't make it work with gfortran.
Here is a working trivial example with gcc
First, build.c
to build the data file, containing only the binary representation of the number pi=3.14...
#include <math.h>
#include <stdio.h>
int main() {
FILE *f;
double x = M_PI;
f = fopen("data.bin", "wb");
fwrite(&x, sizeof x, 1, f);
fclose(f);
return 0;
}
Then cbin.c
, to print the number.
# include <stdio.h>
extern double val;
int main() {
printf("%lf\n", val);
return 0;
}
Then, to build the executable, I do this
objcopy -I binary -O elf32-i386 -B i386 --redefine-sym _binary_data_bin_start=_val data.bin data.o
objdump -t data.o
gcc cbin.c data.o
Notice that the start of data is renamed to _val
.
I tried the following with gfortran
program forbin
implicit none
double precision :: val
common val
print *, val
end program
Then
objcopy -I binary -O elf32-i386 -B i386 --redefine-sym _binary_data_bin_start=_val_ data.bin data.o
objdump -t data.o
gfortran forbin.f95 data.o
Now, the start of data is renamed to _val_
, to follow gfortran naming convention. The compilation step works, but at execution the number 0 is printed instead of pi, so something went wrong. I'm not even sure the common
is the right thing to do, and I wonder if there could be some confusion between address and value.
Also, I would be interested in array data, rather than a single scalar (I can do this in C too, using a pointer on val).
Any idea on what I should do to make this work ?
In case it's important, I'm working with gcc 4.9.1 from here, on Windows 7, 32 bits. Initially, I'm doing this to build a DLL for use in Excel. I could do this in C, but I would prefer a pure Fortran approach if possible.
edit
Following suggestion from High Performance Mark and this page from Stack Overflow, here is a solution for scalar data
module binmod
use iso_c_binding, only: c_double
real(c_double), bind(c) :: val
end module
program forbin
use binmod
implicit none
print *, val
end program
And of course I use the C name _val
instead of _val_
.
However, when doing this with an array, I still don't see how to use the symbols from data.o
to declare the array in Fortran. I could write the dimensions by hand, but it does not seem very robust (it would be easy to forget to update the Fortran code if I update the data).
Here is the output from objdump -t data.o
00000000 l d .data 00000000 .data
00000000 g .data 00000000 _val
00000008 g .data 00000000 _binary_data_bin_end
00000008 g *ABS* 00000000 _binary_data_bin_size
The global _binary_data_bin_end
is a marker of end of data, similar to _binary_data_bin_start
that I renamed to _val
, and _binary_data_bin_size
is an "absolute" value. In C, it would be possible to print this latter value with
extern char binary_data_bin_size;
...
printf("%d\n", &binary_data_bin_size);
However, I don't even understand how a pointer can do the job here, even if I don't consider the warning from the compiler. It works, but I don't know how, nor how to adapt it to gfortran.
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:
This uses a Fortran module containing the variable
var
which is compatible with the C typedouble
. Thebind(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: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:
and
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:
Success.
Array data
Here is a modified build.c to produce a 1-d array of values for testing:
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).
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 withc_loc
and associates the Fortran pointerarray
with that address and using the given shape. You just need to call that subroutine in your code and thenarray
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:
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.