使用 gfortran 从 DLL 导出 COMMON 块

Export COMMON block from DLL with gfortran

当变量是 COMMON 块的一部分时,我无法从 Fortran EXE 正确访问 Fortran DLL 中的变量。

我有一个简单的代码 simple.f90,我使用 MSYS64/MinGW-w64 gfortran 9.2 as

编译成一个 DLL
x86_64-w64-mingw32-gfortran simple.f90 -o simple.dll -shared
! simple.f90

module m
    implicit none
    integer :: a, b
   !common /numbers/ a, b
end module

subroutine init_vals
    use m
    implicit none
    a = 1
    b = 2
end subroutine

这个库是从一个更简单的程序 prog.f90 中使用的,编译为

x86_64-w64-mingw32-gfortran prog.f90 -o prog -L. -lsimple
! prog.90

program p

    use m
    implicit none

    print *, 'Before', a, b
    call init_vals
    print *, 'After', a, b

end program

COMMON/numbers/ 被注释掉时,代码工作并打印预期结果:

 Before           0           0
 After           1           2

但是,当我取消注释 COMMON 块时,输出变为

 Before           0           0
 After           0           0

好像程序使用的变量突然与库中使用的变量不同。

这两种变体在基于 Linux 的 OS 和 gfortran 9.1 中工作得同样好。

我知道 "On some systems, procedures and global variables (module variables and COMMON blocks) need special handling to be accessible when they are in a shared library," 如此处所述: https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gfortran/GNU-Fortran-Compiler-Directives.html 。但是,我无法插入

类型的语句
!GCC$ ATTRIBUTES DLLIMPORT :: numbers

!GCC$ ATTRIBUTES DLLEXPORT :: numbers

代码中的任何地方都不会被编译器捕捉到。

正如 M. Chinoune 在 , current gfortran lacks the ability to import common blocks from DLLs. Even though there has been a patch 中指出的那样,它尚未合并。最后,我需要两件事来使上面的代码工作:

首先,对GCC 9.2应用以下补丁,并在MSYS2中手动编译编译器:

--- gcc/fortran/trans-common.c.org  2019-03-11 14:58:44.000000000 +0100
+++ gcc/fortran/trans-common.c      2019-09-26 08:31:16.243405900 +0200
@@ -102,6 +102,7 @@
 #include "trans.h"
 #include "stringpool.h"
 #include "fold-const.h"
+#include "attribs.h"
 #include "stor-layout.h"
 #include "varasm.h"
 #include "trans-types.h"
@@ -423,6 +424,9 @@
   /* If there is no backend_decl for the common block, build it.  */
   if (decl == NULL_TREE)
     {
+      unsigned id;
+      tree attribute, attributes;
+
       if (com->is_bind_c == 1 && com->binding_label)
        decl = build_decl (input_location, VAR_DECL, identifier, union_type);
       else
@@ -454,6 +458,23 @@

       gfc_set_decl_location (decl, &com->where);

+      /* Add extension attributes to COMMON block declaration. */
+      if (com->head)
+       {
+         attributes = NULL_TREE;
+         for (id = 0; id < EXT_ATTR_NUM; id++)
+           {
+             if (com->head->attr.ext_attr & (1 << id))
+               {
+                 attribute = build_tree_list (
+                   get_identifier (ext_attr_list[id].middle_end_name),
+                   NULL_TREE);
+                 attributes = chainon (attributes, attribute);
+               }
+           }
+         decl_attributes (&decl, attributes, 0);
+       }
+
       if (com->threadprivate)
        set_decl_tls_model (decl, decl_default_tls_model (decl));


二、仅行

!GCC$ ATTRIBUTES DLLIMPORT :: a, b
主程序中需要

(就在 implicit none 之后),但在任何地方都不需要任何导出。这显然是与 Intel Fortran 不同的句法方法,其中导入 COMMON 块而不是其组成部分。我还发现我需要同时导入 ab,即使我只需要 b。 (当只需要 a 时,只导入 a 就足够了。)