将Fortran程序与任意二进制文件链接

时间:2014-12-15 14:41:04

标签: windows gcc fortran gfortran fortran-iso-c-binding

我想将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。

1 个答案:

答案 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循环打印数据中看到这一点。