Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28446 Discussions

How to get around errors during linking a Windows DLL with code in SUBMODULEs using /IFACE:CVF?

FortranFan
Honored Contributor II
787 Views

This inquiry is an attempt to assist a team at work who currently encounter error(s) during linking a Windows DLL with a Fortran codebase that uses SUBMODULEs (due to my prior advice) and which also employs the /IFACE:CVF compiler option.

Unfortunately I don't have all the details and due to the sensitive nature of the work involving external customers and US DOE, I am not authorized to review any code besides a few API procedures which I am not allowed to refactor either.  Thus I am constrained in what I can do. 

Based on the limited information the team shared with me verbally, I came up with the following reproducer:

  1. Say there is a module 'm' in a file 'm.f90' that defines some procedure interfaces, say a subroutine named 'SUB' and a function 'FUNC' 
    module m
       interface
          module subroutine sub(a)
          !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_SUB' :: sub
          !DIR$ ATTRIBUTES DLLEXPORT :: sub
             integer, intent(inout) :: a
          end subroutine 
          module function func(x) result(r)
          !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_FUNC' :: func
          !DIR$ ATTRIBUTES DLLEXPORT :: func
             integer, intent(in) :: x
             integer :: r
          end function 
       end interface
    end module 
    ​
  2. Say the subroutine 'SUB' is implemented in a SUBMODULE in a file named 'sub.f90' 
    submodule(m) sub_sm
    contains
       module subroutine sub( a )
       !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_SUB' :: sub
       !DIR$ ATTRIBUTES DLLEXPORT :: sub
          integer, intent(inout) :: a
          a = func( a ) + 42
       end subroutine 
    end submodule​
  3. Similarly the function 'FUNC' in a SUBMODULE in a file named 'func' 
    submodule(m) func_sm
    contains
       module function func(x) result(r)
          !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_FUNC' :: func
          !DIR$ ATTRIBUTES DLLEXPORT :: func
          integer, intent(in) :: x
          integer :: r
          r = x + 1
       end function 
    end submodule​

The requirements due to the project agreements among all the parties include:

  1. The target platform for the Windows DLL shall be IA-32,
  2. The use of the DLL among different parties imply the protocol for the external procedures shall be STDCALL, REFERENCE, and MIXED_STRING_LEN_ARG for character string length argument passing i.e., /IFACE:CVF
  3. There must be ALIASes for the API procedures but which are not based on `BIND(C)`.

The naïve reproducer above strives to include these 3 requirements.  Here is an illustration of the linker error(s) encountered with this reproducer:

C:\temp>ifort /c /standard-semantics /iface:cvf /Qm32 m.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.0 Build 20220726_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\temp>ifort /c /standard-semantics /iface:cvf /Qm32 sub.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.0 Build 20220726_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\temp>ifort /c /standard-semantics /iface:cvf /Qm32 func.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.0 Build 20220726_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\temp>link m.obj sub.obj func.obj /dll /out:m.dll
Microsoft (R) Incremental Linker Version 14.33.31630.0
Copyright (C) Microsoft Corporation.  All rights reserved.

func.obj : error LNK2005: _M already defined in sub.obj
   Creating library m.lib and object m.exp
LINK : warning LNK4217: symbol '_M_FUNC@4' defined in 'func.obj' is imported by 'sub.obj' in function '_M_SUB'
m.dll : fatal error LNK1169: one or more multiply defined symbols found

C:\temp>

 Now consider the same exact code built using /IFACE:default option: 

 

C:\temp>ifort /c /standard-semantics /iface:default /Qm32 m.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.0 Build 20220726_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\temp>ifort /c /standard-semantics /iface:default /Qm32 sub.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.0 Build 20220726_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\temp>ifort /c /standard-semantics /iface:default /Qm32 func.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.0 Build 20220726_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.


C:\temp>link m.obj sub.obj func.obj /dll /out:m.dll
Microsoft (R) Incremental Linker Version 14.33.31630.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library m.lib and object m.exp
LINK : warning LNK4217: symbol '_M_FUNC' defined in 'func.obj' is imported by 'sub.obj' in function '_M_SUB'

C:\temp>

But for the LNK4217 warning, the Microsoft linker succeeding in creating the DLL.  Now consider a trivial caller of this DLL in a file named 'p.f90':

   use m, only : sub
   integer :: n
   n = 0
   call sub( n )
   print *, n
end

 Here is the program response consuming the above DLL:

 

C:\temp>ifort p.f90 m.lib /standard-semantics
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.0 Build 20220726_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.33.31630.0
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:p.exe
-subsystem:console
p.obj
m.lib

C:\temp>p.exe
 43

C:\temp>

Thus code is correct enough to work with /IFACE:default setting, but the linker fails when the code is processed using /IFACE:CVF option.

The questions I am grappling with include: 1)  why does the linker throw errors with /IFACE:CVF and 2) how can this team get around the linker error while satisfying their program requirements.  My hunch is the answer to 1) is a bug in Intel Fortran compiler: therefore I have submitted a support request with Intel Online Support Center.  And I have asked about 2).

Does anyone have any suggestions or feedback on why the linker error(s) and how to get around them?

Thank you,

 

0 Kudos
6 Replies
mfinnis
New Contributor II
762 Views

Do you get what you want by using /IFACE:DEFAULT and putting CVF in the !DIR$ ATTRIBUTES statements (eg !DIR$ ATTRIBUTES CVF, DECORATE, ALIAS : 'M_SUB' :: sub) ? It seems to compile and link ok with ifort 19.1.2.254 (though the function names in the warning are different)

1>Compiling with Intel(R) Visual Fortran Compiler 19.1.2.254 [IA-32]...
1>m.f90
1>func.f90
1>sub.f90
1>Linking...
1>Creating library G:\Sandbox\Scratch\temps01\Console1\Dll1\Debug\Dll1.lib and object G:\Sandbox\Scratch\temps01\Console1\Dll1\Debug\Dll1.exp
1>sub.obj : warning LNK4217: locally defined symbol _M_mp_M_FUNC@4 imported in function _M_mp_M_SUB

 

0 Kudos
FortranFan
Honored Contributor II
739 Views

@mfinnis ,

Thank you very much for your reply and your looking into this, greatly appreciated.  I have forwarded the link to this thread and your reply to this team.  They tell me they are currently constrained to apply the /IFACE:CVF to the Fortran MODULE files also due to several factors involving external customers and a custom build system from a 3rd party simulation vendor.  And with that /IFACE:CVF option, the linker error persists unfortunately.

0 Kudos
Steve_Lionel
Honored Contributor III
737 Views

This is indeed a compiler bug, but there is a workaround. (In fact, the workaround is preferable to using /iface).

Compare the symbol dump from submodule M compiled with /iface:cvf 

 

 

 

000 00000000 SECT1  notype       Static       | .text
    Section length   30, #relocs    1, #linenums    0, checksum        0
002 00000000 SECT1  notype ()    External     | _M@SUB_SM.@0
003 00000000 SECT1  notype ()    External     | _M
004 00000010 SECT1  notype ()    External     | _M_SUB@4
005 00000010 SECT1  notype ()    External     | _M_SUB
006 00000000 UNDEF  notype       External     | __imp__M_FUNC@4
007 00000000 SECT2  notype       Static       | .drectve
    Section length   FD, #relocs    0, #linenums    0, checksum        0
009 00000001 ABS    notype       Static       | @feat.00

 

 

 

and without:

000 00000000 SECT1  notype       Static       | .text
    Section length   30, #relocs    1, #linenums    0, checksum        0
002 00000000 SECT1  notype ()    External     | _M@SUB_SM.
003 00000010 SECT1  notype ()    External     | _M_SUB
004 00000000 UNDEF  notype       External     | __imp__M_FUNC
005 00000000 SECT2  notype       Static       | .drectve
    Section length   C9, #relocs    0, #linenums    0, checksum        0
007 00000001 ABS    notype       Static       | @feat.00

 

 

Notice that with /iface:CVF, there is an extraneous symbol _M defined - this should not be there. This symbol is properly created when module M is compiled (there is a version with and without STDCALL decoration), but it should not be there for the submodule.

The workaround? Instead of using /iface:cvf, add CVF to the ATTRIBUTES directive for the exported symbols. You'll need to do this in both the module and the submodule. For example:

 

 

 

module m
   interface
      module subroutine sub(a)
      !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_SUB' :: sub
      !DIR$ ATTRIBUTES DLLEXPORT, CVF :: sub
         integer, intent(inout) :: a
      end subroutine 
      module function func(x) result(r)
      !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_FUNC' :: func
      !DIR$ ATTRIBUTES DLLEXPORT, CVF :: func
         integer, intent(in) :: x
         integer :: r
      end function 
   end interface
end module 
submodule(m) sub_sm
contains
   module subroutine sub( a )
   !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_SUB' :: sub
   !DIR$ ATTRIBUTES DLLEXPORT, CVF :: sub
      integer, intent(inout) :: a
      a = func( a ) + 42
   end subroutine 
end submodule
submodule(m) func_sm
contains
   module function func(x) result(r)
      !DIR$ ATTRIBUTES DECORATE, ALIAS : 'M_FUNC' :: func
      !DIR$ ATTRIBUTES DLLEXPORT, CVF :: func
      integer, intent(in) :: x
      integer :: r
      r = x + 1
   end function 
end submodule

 

 

 

If we do that and compile sub.f90 without /iface, we get:

 

 

 

000 00000000 SECT1  notype       Static       | .text
    Section length   30, #relocs    1, #linenums    0, checksum        0
002 00000000 SECT1  notype ()    External     | _M@SUB_SM.
003 00000010 SECT1  notype ()    External     | _M_mp_M_SUB@4
004 00000010 SECT1  notype ()    External     | _M_mp_M_SUB
005 00000000 UNDEF  notype       External     | __imp__M_mp_M_FUNC@4
006 00000000 SECT2  notype       Static       | .drectve
    Section length  111, #relocs    0, #linenums    0, checksum        0
008 00000001 ABS    notype       Static       | @feat.00

 

 

 

Putting it all together:

 

 

 

D:\Projects>ifort /c /standard-semantics m.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.1 Build 20221019_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.

D:\Projects>ifort /c /standard-semantics sub.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.1 Build 20221019_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.

D:\Projects>ifort /c /standard-semantics func.f90
Intel(R) Fortran Intel(R) 64 Compiler Classic for applications running on IA-32, Version 2021.7.1 Build 20221019_000000
Copyright (C) 1985-2022 Intel Corporation.  All rights reserved.

D:\Projects>link m.obj sub.obj func.obj /dll /out:m.dll
Microsoft (R) Incremental Linker Version 14.33.31630.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library m.lib and object m.exp
LINK : warning LNK4217: symbol '_M_mp_M_FUNC@4' defined in 'func.obj' is imported by 'sub.obj' in function '_M_mp_M_SUB'

 

 

 

In general, avoid using command line options such as /iface and /names - use ATTRIBUTES directives if you need to.

By the way, that link warning can be ignored - it is telling you that you asked a symbol to be DLLIMPORTed, but it is defined in that DLL. This happens because the interface for func specifies DLLEXPORT, which turns into a DLLIMPORT when the module is used (implicitly by the submodules.)

 

0 Kudos
FortranFan
Honored Contributor II
712 Views

Thank you Steve, as I mentioned in my previous comment the team in question has the link to this thread plus I have forwarded to them your post.

0 Kudos
Barbara_P_Intel
Moderator
734 Views

AND, @FortranFan already filed a bug on this. Thank you!

 

0 Kudos
FortranFan
Honored Contributor II
712 Views

Thanks @Barbara_P_Intel , hopefully the Intel Fortran team will agree it is a bug and be able to provide a fix in IFORT in a future release.  

0 Kudos
Reply