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

Crash after using derived type with allocatable component in DLL

johnyb_
New Contributor I
958 Views

I have a derived type that contains an allocatable array. A bound procedure exists that allows adding elements to that array. The adding involves a call to move_alloc. I call a procedure in a DLL with an argument of that derived type. The DLL procedure adds an element to the array. If after the return from the DLL procedure I add another element to the array -> crash.

I think this is somehow related to the fact that a Windows DLL has its own heap and pointers from allocations done in a DLL must not be used in the main program. However before I completely rework the way our derived type works, I would like to know if actually this is the cause or if it is perhaps a bug in my program or ifort (hope's small but you never know). Someone might even know a magic workaround !

Here is a small reproducer, our real code is a lot more complex:

the module that defines the derived type

module m_t_x
    type t_x
        integer, allocatable :: ints(:)
    contains
        procedure :: add
    end type

    contains
    subroutine add(this, val)
        class(t_x), intent(inout) :: this
        integer,    intent(in)    :: val
        
        integer, allocatable :: temp(:)
        integer s
        
        if (.not. allocated(this%ints)) then
           allocate(this%ints(1:1))
           this%ints(1) = val
        else
            s = size(this%ints)
            allocate(temp(1:s+1))
            temp(1:s) = this%ints
            temp(s+1) = val
            call move_alloc(temp, this%ints)
        endif
    end subroutine
end module

the dll:

subroutine add_dll(x,i)
!DEC$ ATTRIBUTES DLLEXPORT :: ADD_DLL
    use m_t_x
    type(t_x), intent(inout) :: x
    integer,   intent(in)    :: i
    
    call x%add(i)
    
end subroutine

and the main program:

program test
    use m_t_x
    
    type(t_x) :: var1, var2, var3, var4
    
    interface 
        subroutine add_dll(x, i)
            import
            type(t_x), intent(inout) :: x
            integer,   intent(in)    :: i
        end subroutine
    end interface

    print *, "Adding local"
    call var1%add(1)
    print *, var1%ints
    call var1%add(2)
    print *, var1%ints
    
    print *, "Add in dll"
    call add_dll(var2,1)
    print *, var2%ints
    call add_dll(var2,2)
    print *, var2%ints
    
    print *, "Add local first, then in dll"
    call var3%add(1)
    print *, var3%ints
    call add_dll(var3,2)
    print *, var3%ints

    ! actually we never get here because the previous crashes
    print *, "Add in dll first, then local"
    call add_dll(var4,1)
    print *, var4%ints
    call var4%add(1)
    print *, var4%ints
    
 end program    

I am running Windows 8.1 and the compiler version is

Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.0.110 Build 20150815

I build the example like this:

ifort mod.f90 /c
ifort dll.f90 mod.obj /dll
ifort test.f90 mod.obj dll.lib

And when I run it:

C:\TEMP\x>test
 Adding local
           1
           1           2
 Add in dll
           1
           1           2
 Add local first, then in dll
           1
forrtl: severe (157): Program Exception - access violation
Image              PC                Routine            Line        Source
ntdll.dll          00007FFF7E8F6CC9  Unknown               Unknown  Unknown
libifcoremd.dll    00007FFF628478D8  Unknown               Unknown  Unknown
libifcoremd.dll    00007FFF6284AB76  Unknown               Unknown  Unknown
libifcoremd.dll    00007FFF627BBD70  Unknown               Unknown  Unknown
dll.dll            00007FFF7A6A137F  Unknown               Unknown  Unknown
dll.dll            00007FFF7A6A10C6  Unknown               Unknown  Unknown
test.exe           00007FF7E8AF1484  Unknown               Unknown  Unknown
test.exe           00007FF7E8B453DE  Unknown               Unknown  Unknown
test.exe           00007FF7E8B31224  Unknown               Unknown  Unknown
KERNEL32.DLL       00007FFF7E6516AD  Unknown               Unknown  Unknown
ntdll.dll          00007FFF7E9334A5  Unknown               Unknown  Unknown

C:\TEMP\x>

In our real program, the crash in Visual Studio looks like this:

crash.png

Thanks for any feedback 

Johny

 

0 Kudos
1 Solution
Arjen_Markus
Honored Contributor II
958 Views

I can reproduce the problem and I think I have a solution. What struck me is that you need mod.obj in the link statement for the program itself. I used gfortran to check if that was behaving in the same way, but that was not the case - and I did not need the second use of mod.obj, as that was already contained in the DLL. IIRC, the GCC compilers export everything by default.

So my solution was to export the subroutine ADD from mod.f90/.obj. And then it works fine. I think the problem is the dual occurrence of the ADD subroutine - there is a copy in the program and in the DLL. But that is just a hunch, it is not based on any deep understanding of what goes on with linking a program and its components.

View solution in original post

0 Kudos
7 Replies
Arjen_Markus
Honored Contributor II
959 Views

I can reproduce the problem and I think I have a solution. What struck me is that you need mod.obj in the link statement for the program itself. I used gfortran to check if that was behaving in the same way, but that was not the case - and I did not need the second use of mod.obj, as that was already contained in the DLL. IIRC, the GCC compilers export everything by default.

So my solution was to export the subroutine ADD from mod.f90/.obj. And then it works fine. I think the problem is the dual occurrence of the ADD subroutine - there is a copy in the program and in the DLL. But that is just a hunch, it is not based on any deep understanding of what goes on with linking a program and its components.

0 Kudos
johnyb_
New Contributor I
958 Views

Hi Arjen,

A quick test shows that your solution works here also. As we have several types with an "ADD" procedure, I tried a test case with a second type, identical to t_x, just multiplying the values by 2 to make sure the right ADD is called which is what happens.

The only small worry are warnings about the exported procedures:

C:\TEMP\x>ifort mod.f90 /c
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.0.110 Build 20150815
Copyright (C) 1985-2015 Intel Corporation.  All rights reserved.


C:\TEMP\x>ifort dll.f90 mod.obj /dll
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 16.0.0.110 Build 20150815
Copyright (C) 1985-2015 Intel Corporation.  All rights reserved.

ipo: warning #11082: C:\TEMP\x\dll.obj: locally defined symbol __imp_M_T_X_mp_ADD_X imported
ipo: warning #11082: C:\TEMP\x\dll.obj: locally defined symbol __imp_M_T_X_mp_ADD_Z imported
Microsoft (R) Incremental Linker Version 11.00.61030.0
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:dll.dll
-dll
-implib:dll.lib
dll.obj
mod.obj
   Creating library dll.lib and object dll.exp
LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library
dll.obj : warning LNK4217: locally defined symbol M_T_X_mp_ADD_X imported in function ADD_DLL
dll.obj : warning LNK4217: locally defined symbol M_T_X_mp_ADD_Z imported in function ADD_DLL_Z

C:\TEMP\x>

I will try your solution on our real project, which involves several complex types.

Many thanks for your very insightful answer

Johny

0 Kudos
Arjen_Markus
Honored Contributor II
958 Views

Hi Johny,

I hope this works indeed in the larger context too. I have seen such warnings from time to time myself, but I have no idea if they are important or not.

 

0 Kudos
johnyb_
New Contributor I
958 Views

Hi Arjen,

there are about 8 types involved, with all in all about a hundred type bound procedures. It will take me a day or two before I can post back the outcome.

But anyway it will be fun to see what comes out of it :-)

Johny

 

 

0 Kudos
FortranFan
Honored Contributor III
958 Views

@Johny,

See this thread: per Intel and Steve Lionel, "there isn't anything special a DLL":

https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/518615

So a rule of thumb that seems to serve us well is to first check if the problem in question can be reproduced with a typical Fortran program where a validation case is put together in the form a Fortran main program and all the library code that might normally be in DLLs are instead linked directly with the main program.  If there is a problem with such a test case, then the issues are most likely in the Fortran code itself; if not, then the issues with the DLL case are usually external to standard Fortran code i,e., they may have to do with compiler options/directives (e.g., missing/invalid/inconsistent use of STDCALL calling convention in the DLL compared to the calling executive) and/or linker configurations (wrong/multiple object files) and so forth.

0 Kudos
johnyb_
New Contributor I
958 Views

@FortranFan

That is of course sound advice, and it's what we did to isolate the cause of the problem.

While concerning the pure Fortran part, it is true that there is nothing special to a DLL, it is important to understand the way memory is managed. A DLL has its own heap and all allocations done by code running in the DLL are done from this heap and not from the one of the calling program. I have done a fair lot of programming in C and C++ and the rule is that either the caller or the callee is solely managing allocations and deallocations.

However, I am now doing things in Fortran that involve allocations and deallocations inside type bound procedures and it was not immediately obvious to me that I have to observe the same rule. You learn by your mistakes :-)

 

0 Kudos
johnyb_
New Contributor I
958 Views

Hi Arjen

Putting the shared code in a DLL and exporting all the type bound procedures resolves our memory issues.

Thanks again for your helpful advice.

Johny

 

 

0 Kudos
Reply