Search code examples
dllfortrangfortranmingw-w64

Export COMMON block from DLL with gfortran


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.


Solution

  • 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.)