1

I am having trouble correctly accessing a variable in a Fortran DLL from a Fortran EXE when the variable is part of a COMMON block.

I have a trivial code simple.f90 which I compile into a DLL using MSYS64/MinGW-w64 gfortran 9.2 as

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

This library is used from a even simpler program prog.f90, compiled as

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

When the COMMON block /numbers/ is commented out, the code works and prints the expected result:

 Before           0           0
 After           1           2

However, when I uncomment the COMMON block, the output becomes

 Before           0           0
 After           0           0

as if the variables used by the program were suddenly distinct from those used in the library.

Both variants work equally well in a Linux-based OS with gfortran 9.1.

I am aware that "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," as mentioned here: https://gcc.gnu.org/onlinedocs/gcc-4.9.0/gfortran/GNU-Fortran-Compiler-Directives.html . However, I was not able to insert a statement of the type

!GCC$ ATTRIBUTES DLLIMPORT :: numbers

or

!GCC$ ATTRIBUTES DLLEXPORT :: numbers

anywhere in the code without being snapped at by the compiler.

jacob
  • 1,535
  • 1
  • 11
  • 26
  • Try using `/numbers/` in the ATTRIBUTES directives. This is how it's done in ifort. – Steve Lionel Aug 22 '19 at 23:39
  • The code is invalid Fortran, so gfortran can do whatever it wants. – evets Aug 23 '19 at 02:07
  • @SteveLionel Thanks for suggestion, but gfortran spits out "Error: Invalid character in name" when i enclose `numbers` in slashes. @evets Could you be more specific what part of the code is invalid and how to fix it? – jacob Aug 23 '19 at 06:23
  • What value do you expect `a` and `b` to have when you try to print them with `print *, "Before", a, b`? `a` and `b` are undefined! If you're using modules then don't use `common`. – evets Aug 23 '19 at 07:13
  • @evets True, but I am interested in the "After" state only. The `common` needs to be there as this is a minimal reproducer of a [larger issue](https://stackoverflow.com/questions/57535963/msmpi-in-place-mpi-allreduce-not-working-with-mingw-w64-gfortran) that I am trying to crack. – jacob Aug 23 '19 at 09:35
  • Ah, this is actually duplicate of https://stackoverflow.com/questions/41491975/named-common-block-in-a-shared-library – jacob Aug 23 '19 at 14:25
  • 1
    This is a known bug in gfortran https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47030 – M. Chinoune Aug 26 '19 at 16:43

1 Answers1

0

As pointed out by M. Chinoune in the comment, current gfortran lacks the ability to import common blocks from DLLs. Even though there has been a patch for some time, it is not yet merged. In the end, I needed two things to make the above code work:

First, apply the following patch to GCC 9.2 and compile the compiler manually in 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));


Second, only the line

!GCC$ ATTRIBUTES DLLIMPORT :: a, b

was needed in the main program (right after implicit none), but not any exports anywhere. This is apparently a different syntactical approach then in Intel Fortran, where one imports the COMMON block rather than its constituents. I also found out that I needed to import both a and b even if I only needed b. (When only a was needed, importing a only was enough.)

jacob
  • 1,535
  • 1
  • 11
  • 26