- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello,
I am attempting to create dll files in Fortran. If I am not mistaken, I am supposed to export a .dll file from my project, which is a .f90 file as opposed to a .dll file, in order for another program (written in Basic) to link to it. To get things started, I have made a simple program in Fortran that goes as follows:
! TestDLL.f90
! ! FUNCTIONS/SUBROUTINES exported from TestDLL.dll: ! TestDLL - subroutine ! subroutine TestDLL ! Expose subroutine TestDLL to users of this DLL ! !DEC$ ATTRIBUTES DLLEXPORT::TestDLL ! Variables Integer K ! Body of TestDLL K = 1 Return end subroutine TestDLL
However, when I create the .dll file by running this program, I see the following when I open the .dll file in a text editor:
This program cannot be run in DOS mode. Stack around the variable '' was corrupted. The variable '' is being used without being initialized. The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention. A cast to a smaller data type has caused a loss of data. If this was intentional, you should mask the source of the cast with the appropriate bitmask. Changing the code in this way will not affect the quality of the resulting optimized code. Stack memory was corrupted A local variable was used before it was initialized Stack memory around _alloca was corrupted Unknown Runtime Check Error Runtime Check Error . Unable to display RTC Message. Run-Time Check Failure Unknown Filename Unknown Module Name Run-Time Check Failure Stack corrupted near unknown variable user32.dll wsprintfA Stack area around _alloca memory reserved by this function is corrupted Stack area around _alloca memory reserved by this function is corrupted A variable is being used without being initialized. Stack pointer corruption Cast to smaller type causing loss of data Stack memory corruption Local variable used before initialization Stack around _alloca corrupted <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel> </requestedPrivileges> </security> </trustInfo> </assembly>
Where could I going wrong here?
Thanks,
Jamal
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Alternatively, you can declare the function BIND(C) and then use !DEC$ ATTRIBUTES STDCALL, DLLEXPORT and then ifort will get the names and calling convention right for you.
! TestDLL.f90 ! Compile with ! ifort /DLL TestDLL.f90 ! ! FUNCTIONS/SUBROUTINES exported from TestDLL.dll: ! TestDLL - subroutine ! Integer Function TestDLL(QuickNum) bind(C,name='TestDLL') use ISO_FORTRAN_ENV implicit none ! Expose subroutine TestDLL to users of this DLL ! !DEC$ ATTRIBUTES STDCALL,DLLEXPORT::TestDLL ! integer in VBA corresponds to 16-bit integer ! Your VBA code specified passing Quicknum by value Integer(INT16), value :: Quicknum TestDLL = Quicknum end function TestDLL
Output of dumpbin /exports TestDLL.dll:
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file testDLL.dll File Type: DLL Section contains the following exports for TestDLL.dll 00000000 characteristics 55D64F72 time date stamp Thu Aug 20 16:06:42 2015 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 00001000 TestDLL 2 1 00001000 _TestDLL@2 Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .text
So we can see that ifort exports both the undecorated name that VBA and LoadLibrary/GetProcAddress want and also the decorated name that a program linking with the *.lib file needs when you use !DEC$ ATTRIBUTES STDCALL.
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You are trying to create a DLL from Fortran source. It looks as if you did that ok. Opening the DLL in a text editor is not useful.
Since you want to use this DLL from Visual Basic, there are more things you need to do, however. VB requires the STDCALL calling convention, which is not the Intel Fortran default. Second, you'll get a "decorated' name from Fortran which would require you to use the same decoration in VB, To fix both of these, replace:
!DEC$ ATTRIBUTES DLLEXPORT::TestDLL
with:
!DEC$ ATTRIBUTES STDCALL,REFERENCE,ALIAS:"TestDLL",DLLEXPORT::TestDLL
Make sure you call the routine "TestDLL" in VB.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you Steve, what you instructed me to do worked.
Now I am trying to pass variables from Basic to Fortran. I have changed the subroutine in Fortran to a function, which goes as follows:
! TestDLL.f90 ! ! FUNCTIONS/SUBROUTINES exported from TestDLL.dll: ! TestDLL - subroutine ! Integer Function TestDLL(QuickNum) ! Expose subroutine TestDLL to users of this DLL ! !DEC$ ATTRIBUTES STDCALL,ALIAS:"TestDLL",DLLEXPORT::TestDLL !DEC$ ATTRIBUTES REFERENCE :: QuickNum Integer Quicknum TestDLL = Quicknum end function TestDLL
This dll is called by the following Visual Basic program:
Public Class Form1 Declare Function TestDLL Lib "C:\Users\Jamal_S_\Documents\Visual Studio 2013\Projects\TestDLL\TestDLL\Debug\TestDLL.dll" (ByVal Quicknum As Integer) As Integer Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Button1.Text = TestDLL(1) End Sub End Class
Basically, the text of Button1 should just say whatever number I send to the dll. When I run it however, I get the following error:
An unhandled exception of type 'System.AccessViolationException' occurred in WindowsApplication1.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Again, where am I going wrong?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Try using 'ByVal Quicknum As Integer' in the VBasic definition.
It may be that the value 1 is being taken in the fortran as an address (you specify REFERENCE for the Fortran argument. That will put it clearly out of the allowed range.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Alternatively, you can declare the function BIND(C) and then use !DEC$ ATTRIBUTES STDCALL, DLLEXPORT and then ifort will get the names and calling convention right for you.
! TestDLL.f90 ! Compile with ! ifort /DLL TestDLL.f90 ! ! FUNCTIONS/SUBROUTINES exported from TestDLL.dll: ! TestDLL - subroutine ! Integer Function TestDLL(QuickNum) bind(C,name='TestDLL') use ISO_FORTRAN_ENV implicit none ! Expose subroutine TestDLL to users of this DLL ! !DEC$ ATTRIBUTES STDCALL,DLLEXPORT::TestDLL ! integer in VBA corresponds to 16-bit integer ! Your VBA code specified passing Quicknum by value Integer(INT16), value :: Quicknum TestDLL = Quicknum end function TestDLL
Output of dumpbin /exports TestDLL.dll:
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file testDLL.dll File Type: DLL Section contains the following exports for TestDLL.dll 00000000 characteristics 55D64F72 time date stamp Thu Aug 20 16:06:42 2015 0.00 version 1 ordinal base 2 number of functions 2 number of names ordinal hint RVA name 1 0 00001000 TestDLL 2 1 00001000 _TestDLL@2 Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .text
So we can see that ifort exports both the undecorated name that VBA and LoadLibrary/GetProcAddress want and also the decorated name that a program linking with the *.lib file needs when you use !DEC$ ATTRIBUTES STDCALL.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Noticed a couple of mistakes in my last post.
Firstly, TestDLL is typed in VBA as returning an integer, so I should have declared it as Integer(INT16) Function TestDLL
The other part is really strange. Shouldn't the caller always push multiples of the stack word size, which in 32-bit windows is 4 bytes? In that case there should be 4 bytes of arguments pushed on the stack and the name should be mangled as _TestDLL@4 but from the DUMPBIN output above it can be seen that ifort compiles it to _TestDLL@2 . gfortran compiles to _TestDLL@4 so who is right? I don't have MSVC++; what does cl do in this case? Is it ifort or gfortran that is completely broken?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
OK, I've determined that it's ifort that is completely broken in this instance.
subroutine sub(x,y) use ISO_FORTRAN_ENV implicit none integer(INT16) x, y interface subroutine s(x,y) bind(C,name='s') import implicit none !DEC$ ATTRIBUTES STDCALL :: s integer(INT16), value :: x, y end subroutine s subroutine t(x,y) import implicit none !DEC$ ATTRIBUTES STDCALL, DECORATE, ALIAS: 't' :: t !DEC$ ATTRIBUTES VALUE :: x, y integer(INT16) x, y end subroutine t end interface call s(x,y) call t(x,y) end subroutine sub
Now let's check the output with ifort /c /FA std2.f90
_SUB PROC NEAR ; parameter 1: 12 + esp ; parameter 2: 16 + esp .B1.1: ; Preds .B1.0 L1:: ;1.12 push esi ;1.12 push ebp ;1.12 mov ebp, DWORD PTR [16+esp] ;1.12 mov esi, DWORD PTR [12+esp] ;1.12 movsx eax, WORD PTR [ebp] ;21.9 push eax ;21.9 movsx edx, WORD PTR [esi] ;21.9 push edx ;21.9 call _s@4 ;21.9 ; LOE ebx ebp esi edi .B1.2: ; Preds .B1.1 movsx ebp, WORD PTR [ebp] ;22.9 push ebp ;22.9 movsx eax, WORD PTR [esi] ;22.9 push eax ;22.9 call _t@8 ;22.9 ; LOE ebx edi .B1.3: ; Preds .B1.2 pop ebp ;23.1 pop esi ;23.1 ret ;23.1 ALIGN 16 ; LOE ; mark_end; _SUB ENDP
Note that for both invocations, ifort is pushing 4 bytes per argument on the stack as it should, but when bind(C) was used ifort counted up the number of bytes on the stack incorrectly and mangled the name as _s@4 .
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you Anthony Richards and Repeat Offender for your input.
Repeat Offender, the amendments you made to my code made it work, I highly appreciate it. Thank you for clearing up all the confusion.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
My colleague Kevin has created a tracking ticket for the incorrect external name in the BIND(C) case above.
If you need to reference that ticket later, its id is: DPD200375220
thanks --
--Lorri
(Resolution Update on 02/27/2016): The defect DPD200375220 is fixed in the Intel® Parallel Studio XE 2016 Update 2 Release (PSXE 2016.2.055/ CnL 2016.2.180 - Windows)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks, Lorri & Kevin, for your attention to this matter. I suppose I have to retract the remark about ifort's behavior being 'totally broken'. I had been afraid that ifort might be generating some wrong code along with the incorrect name mangling, but further testing shows that ifort's code is fine except for the decorated names. I came up with an example where ifort must generate the names for the callee:
function f(x,y) bind(C,name='f') use ISO_FORTRAN_ENV implicit none !DEC$ ATTRIBUTES STDCALL :: f integer(INT16) f integer(INT16), value :: x, y f = x+y end function f function g(x,y) use ISO_FORTRAN_ENV implicit none !DEC$ ATTRIBUTES STDCALL, DECORATE, ALIAS: 'g' :: g integer(INT16) g integer(INT16), value :: x, y g = x+y end function g
Output of ifort /c /FA std3.f90
ALIGN 16 PUBLIC _f@4 _f@4 PROC NEAR PUBLIC _f _f:: ; parameter 1: 4 + esp ; parameter 2: 8 + esp .B1.1: ; Preds .B1.0 L1:: ;1.10 movsx eax, WORD PTR [4+esp] ;1.10 movsx edx, WORD PTR [8+esp] ;1.10 add eax, edx ;7.9 ret 8 ;8.1 ALIGN 16 ; LOE ; mark_end; _f@4 ENDP _TEXT ENDS _DATA SEGMENT DWORD PUBLIC FLAT 'DATA' _DATA ENDS ; -- End _f@4 _TEXT SEGMENT PARA PUBLIC FLAT 'CODE' ; -- Begin _g@8 _TEXT ENDS _TEXT SEGMENT PARA PUBLIC FLAT 'CODE' ; mark_begin; ALIGN 16 PUBLIC _g@8 _g@8 PROC NEAR PUBLIC _g _g:: ; parameter 1: 4 + esp ; parameter 2: 8 + esp .B2.1: ; Preds .B2.0 L2:: ;10.10 movsx eax, WORD PTR [4+esp] ;10.10 movsx edx, WORD PTR [8+esp] ;10.10 add eax, edx ;16.9 ret 8 ;17.1 ALIGN 16 ; LOE ; mark_end; _g@8 ENDP
So we can see that even with BIND(C), ifort pops the right number of bytes off the stack on return. In some sense there are 6 places in my two examples where the number of bytes is needed: in the name as seen by the caller and callee and in the ret N instruction in the epilog, for both DECORATE and BIND(C). In an ideal universe perhaps the same code would get reused in all 6 instances, but it seems that code was written de novo when BIND(C) + STDCALL was introduced, and SUM(SIZES_OF_ARGS_IN_BYTES) rather than SUM(IAND(SIZES_OF_ARGS_IN_BYTES+3,INT(Z'FFFFFFFC'))) was used in the new version to create the mangled names.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The defect with BIND(C) detailed in post #7 is fixed in the recently released PSXE 2016 Update 2 release (PSXE 2016.2.055/ CnL 2016.2.180 - Windows)

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page