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

Dynamic Libraries and Modules

Pellegrini__Etienne
1,177 Views

Hello,

I am trying to call procedures defined in a module in a dll that I created myself. I am mostly following the instructions that I found here. In particular, for 'simplicity' purposes, I am trying to avoid using the !DEC$ ATTRIBUTES DLLIMPORT in the main. Indeed, I thought that the process described in the link above for sharing data through modules could work with procedures. And most times it does.

My problems comes when I call a routine whose arguments' dimensions are contained in another module in the library (the main, outside the library, calls a subroutine which is in a module in the library, and uses another module of the library for dimensions). In those cases, the compiler tells me that the constants used for the dimensions are unresolved symbols in the main. But they are NOT USED in the main. To be clearer, I included a MWE that reproduces the problem:

The following are the two modules compiled in the dll:

      MODULE constantsMod

        integer  :: nStates
          
      end MODULE

      MODULE testMod


      contains

        subroutine setupConstants(nIn)
        !DEC$ ATTRIBUTES DLLEXPORT, ALIAS : "setupConstants" ::  setupConstants 
          use constantsMod

          implicit none
          integer, intent(in) :: nIn
          
          nStates = nIn
          
        end subroutine
      
        subroutine test(nX, nY)
        !DEC$ ATTRIBUTES DLLEXPORT, ALIAS : "test" ::  test 
        
          use constantsMod
          implicit none
          real*8, intent(in)  :: nX(nStates)
          real*8, intent(out) :: nY(nStates)

          print*, nX, nStates
          nY = nX

        end subroutine

        subroutine test2(nStates, nX, nY)
        !DEC$ ATTRIBUTES DLLEXPORT, ALIAS : "test2" ::  test2 

          implicit none
          integer, intent(in) :: nStates
          real*8, intent(in)  :: nX(nStates)
          real*8, intent(out) :: nY(nStates)

          print*, nX, nStates
          nY = nX

        end subroutine

      end MODULE

The main is compiled on its own (and marked to be dependent on the library in Visual Studio: the library and the main are two projects in the same solution, with the same output directory so that the dll is in the same folder as the exe), and is as follows:

      PROGRAM main

        use testMod
        implicit none
        real*8  :: nX(6), nY(6)

        CALL setupConstants(6)
        
        CALL test(nX, nY)
        CALL test2(6, nX, nY)

      end program

When compiling the previous main, I get: "Error    1     error LNK2001: unresolved external symbol _CONSTANTSMOD_mp_NSTATES    main.obj   "

When commenting out the call to test and leaving test2, no problem. If I stop using testMod and add three !DEC$ DLLIMPORT, no problem either. However, because in my real project I have MANY functions being exported and I'd love to not have to DLLIMPORT all of them. 

Now, I understand that the reference I cited above only mentions sharing DATA through modules, and not procedures. I am also expecting the problem to come from the interfaces that are generated by the module. I was just wondering if there was a workaround that didn't involve writing the many DLLIMPORT statements I would need. 

Thanks!

 

0 Kudos
8 Replies
IanH
Honored Contributor III
1,177 Views

I just gave your source code a whirl... and I didn't see the error.  See below for the command line options I used.  What are you doing differently?

That module variable is part of the characteristics of a dummy argument of the module procedure, so I could envisage particular reasons why an implementation would want to reference it - perhaps as part of argument consistency checking, for example.  If an explicit interface isn't accessible at the point of call the compiler doesn't know about the characteristics of the module variable and so can't reference it as part of any checks. 

Have you tried putting DLLEXPORT on the variable?

>ifort /dll /check:all /warn:all /standard-semantics /Od "2014-10-18 dll-dll.f90"
Intel(R) Visual Fortran Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.0.108 Build 20140726
Copyright (C) 1985-2014 Intel Corporation.  All rights reserved.

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

"-out:2014-10-18 dll-dll.dll"
-dll
"-implib:2014-10-18 dll-dll.lib"
"2014-10-18 dll-dll.obj"
   Creating library 2014-10-18 dll-dll.lib and object 2014-10-18 dll-dll.exp

>ifort /check:all /warn:all /standard-semantics /Od "2014-10-18 dll-exe.f90" "2014-10-18 dll-dll.lib"
Intel(R) Visual Fortran Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.0.108 Build 20140726
Copyright (C) 1985-2014 Intel Corporation.  All rights reserved.

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

"-out:2014-10-18 dll-exe.exe"
-subsystem:console
"2014-10-18 dll-exe.obj"
"2014-10-18 dll-dll.lib"

 

0 Kudos
Steven_L_Intel1
Employee
1,177 Views

I can get the error if I compile the main program /debug.  I'll ask the developers if this is necessary.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,177 Views

This may be an issue of inter-file IPO where the function test is attempted to be inlined. As an experiment, try removing inter-file IPO (intra-file IPO is ok).

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
1,177 Views

One can't inline from a DLL. I will comment that the use of NSTATES in the declaration of the dummy argument is probably the important part. In some cases the calling code needs to evaluate variables in the specification expression, but not in this case. (I will try a case where it is needed.) Here, the reference to NSTATES is in the debug information but it should be skipped since it isn't exported.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,177 Views

>>One can't inline from a DLL

Then wouldn't it go to reason that one can't inline from a static library?

In earlier versions of IVF, when IPO came out, when the sources to a static library were locatable on a system where an application were compiled using this static library, I've experienced situations where the inlining would find those sources and incorporate them into the inlining...even when the sources were for a newer generation of code than that of the library being linked to. Now, this said, this may no longer be the case, but to the best of my knowledge I experienced this on one or more occasions. Regardless, it is easy enough to test for, just turn off inter-file IPO and perform a build. If this corrects for the problem then this is an old bug/quirk and should be reported. If it does nothing to fix the problem, then it wasted only one build time.

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
1,177 Views

No, one CAN inline from a static library if it was built with /Qipo. But once the linker is invoked - necessary to build a DLL - the intermediate code is gone.

0 Kudos
Steven_L_Intel1
Employee
1,177 Views

Escalated as issue DPD200362319.  You could add the linker option /force:unresolved to allow the link to complete.

0 Kudos
Pellegrini__Etienne
1,177 Views

Thank you all for your help. Adding the /force:unresolved linker option did solve the problem. Compiling in Release Mode too... I am not used to Visual Studio and /debug /release options, and didn't even think of checking the Release mode...

Thanks!

0 Kudos
Reply