使用 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
块而不是其组成部分。我还发现我需要同时导入 a
和 b
,即使我只需要 b
。 (当只需要 a
时,只导入 a
就足够了。)
当变量是 COMMON
块的一部分时,我无法从 Fortran EXE 正确访问 Fortran DLL 中的变量。
我有一个简单的代码 simple.f90,我使用 MSYS64/MinGW-w64 gfortran 9.2 as
编译成一个 DLLx86_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 在
首先,对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
块而不是其组成部分。我还发现我需要同时导入 a
和 b
,即使我只需要 b
。 (当只需要 a
时,只导入 a
就足够了。)