我想将Fortran程序与任意二进制文件链接起来。我正在使用gfortran,我发现here使用来自binutils的objcopy gcc,同样的任务很容易。
然而,我无法与gfortran合作。
这是一个使用gcc
的简单例子首先,build.c
构建数据文件,仅包含数字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;
}
然后cbin.c
,打印号码。
# include <stdio.h>
extern double val;
int main() {
printf("%lf\n", val);
return 0;
}
然后,为了构建可执行文件,我这样做
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
请注意,数据的开头已重命名为_val
。
我用gfortran
尝试了以下内容program forbin
implicit none
double precision :: val
common val
print *, val
end program
然后
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
现在,数据的开头重命名为_val_
,以遵循gfortran命名约定。编译步骤有效,但在执行时打印数字0而不是pi,所以出了问题。我甚至不确定common
是否正确,我想知道地址和价值之间是否存在一些混淆。
另外,我会对数组数据感兴趣,而不是单个标量(我也可以在C中使用val上的指针)。
对于我应该做些什么才能使这项工作?
如果它很重要,我正在使用here的gcc 4.9.1,Windows 7,32位。最初,我这样做是为了构建一个在Excel中使用的DLL。我可以在C中执行此操作,但如果可能的话,我更喜欢纯粹的Fortran方法。
修改
根据高性能标记的建议和Stack Overflow的this page,这是标量数据的解决方案
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
当然,我使用C名称_val
代替_val_
。
但是,使用数组执行此操作时,我仍然看不到如何使用data.o
中的符号在Fortran中声明数组。我可以手工编写维度,但它看起来不太稳健(如果我更新数据,很容易忘记更新Fortran代码)。
以下是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
全局_binary_data_bin_end
是数据结尾的标记,类似于我重命名为_binary_data_bin_start
的{{1}},_val
是“绝对”值。在C中,可以用
_binary_data_bin_size
但是,我甚至不理解指针如何在这里完成工作,即使我不考虑编译器的警告。它有效,但我不知道如何,也不知道如何使它适应gfortran。
答案 0 :(得分:4)
如评论中所述,您希望使用iso_c_binding
内在Fortran模块来提供C互操作性功能。
在x86_64-pc-linux-gnu上使用GCC 4.9.2生成以下代码/示例。
考虑以下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
这使用包含变量var
的Fortran模块,该变量与C类型double
兼容。 bind(C)
属性将导致对象文件中公开的变量被解除标记(例如var
没有强调)。注意模块的使用是必要的,因为你不能在主程序范围中应用bind属性(或者至少GCC不允许这样做,我没有参考标准)。
我使用你的代码生成data.bin,但对于我的系统,我必须修改objcopy
,如下所示:
objcopy -I binary -O elf64-x86-64 -B i386 --redefine-sym _binary_data_bin_start=val data.bin data.o
更改是生成我的系统可以使用的对象的bfdname,并将符号名称更改为plain val
以匹配Fortran编译器所期望的。
其余的作为例子:
% 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
和
% 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
请注意,此警告不会导致程序失败,但会影响程序的性能。我不确定您的平台上是否存在对齐问题。最后:
% ./fbin
3.1415926535897931
成功。
这是一个修改过的build.c,用于生成用于测试的1-d值数组:
#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;
}
这将生成data.bin,其中包含15个8字节值,范围为0到14 * M_PI。使用与我的标量示例中相同的命令生成data.o
。
这是一个示例Fortran程序,它被硬编码以加载具有形状(3,5)的秩2数组。
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
这有点不那么简单,也许不是实现这一目标的唯一方法,但它是我想到的那个。该模块具有标量值val
和数组指针。子例程使用c_loc
获取val的地址,并将Fortran指针array
与该地址相关联并使用给定的形状。您只需在代码中调用该子例程,然后array
按预期工作。
此阵列是硬编码的,但您可以使用其他数据文件(或具有多个符号的单个数据文件)将数组等级和维度打包到数据对象中,然后更改Fortran以使用取决于实际数据的形状。
如果没有输出,没有例子是完整的:
% ./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
请注意,Fortran和C不使用相同的数组顺序。 C是行主,Fortran是专业列,您可以在我的Fortran循环打印数据中看到这一点。