- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Using a program like this:
program main
call crash()
end program
subroutine crash()
use kernel32
integer(HANDLE) hFile
integer(DWORD) iSize
integer(DWORD) iSizeRW
integer iTotalSize,iTotalSizeRW
integer(BOOL) iResult
iTotalSize=78439242
hFile=0
iSize = sizeof(iTotalSize)
iResult = 1
iSizeRW = iSize
iResult=WriteFile(hFile,loc(iTotalSize),iSize,loc(iSizeRW),NULL)
end subroutine
With /integer_size:64 (and only on the Win32 platform), and using ifort 2015 Update 4, at the end of subroutine crash(), I get ebp-esp = 4, and the error message:
Run-Time Check Failure #0 - 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.
So ... what did I mess up now?
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
When I tried your code with version 16.0.0.110 32 bits and /integer_size:64, it ran to completion with iResult = 0 and GetLastError() = 6 because hFile = 0 was an invalid handle. Maybe the version you are using messed up one of the KINDs of the arguments so that it pushed the wrong number of argument bytes onto the stack?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It looks like that's what it is. I looked at the disassembly with 8-byte ints vs 4-byte ints;
00F81046 mov eax,dword ptr [HFILE]
00F81049 mov dword ptr [esp],eax
00F8104C lea eax,[ITOTALSIZE]
00F8104F mov dword ptr [esp+4],eax
00F81053 mov eax,dword ptr [ISIZE]
00F81056 mov dword ptr [esp+8],eax
00F8105A lea eax,[ISIZERW]
00F8105D mov dword ptr [esp+0Ch],eax
00F81061 mov dword ptr [esp+10h],0
00F81069 mov dword ptr [esp+14h],0
00F81071 call _WriteFile@20 (0F826B0h)
00F81076 mov dword ptr [ebp-4],eax
00F81079 mov eax,dword ptr [ebp-4]
00F8107C mov dword ptr [IRESULT],eax
0032103F mov eax,dword ptr [HFILE]
00321042 mov dword ptr [esp],eax
00321045 lea eax,[ITOTALSIZE]
00321048 mov dword ptr [esp+4],eax
0032104C mov eax,dword ptr [ISIZE]
0032104F mov dword ptr [esp+8],eax
00321053 lea eax,[ISIZERW]
00321056 mov dword ptr [esp+0Ch],eax
0032105A mov dword ptr [esp+10h],0
00321062 call _WriteFile@20 (0322670h)
00321067 mov dword ptr [ebp-4],eax
0032106A mov eax,dword ptr [ebp-4]
0032106D mov dword ptr [IRESULT],eax
I'm really curious if there's a workaround. I tried:
iResult = WriteFile(hFile, loc(iTotalSize), iSize, loc(iSizeRW),
& null_overlapped)
But, because my debug build has /check:pointer, I get this error:
forrtl: severe (408): fort: (7): Attempt to use pointer NULL_OVERLAPPED when it is not associated with a target
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Kewl! Looks like it may be a bug with /integer_size:64, ALLOW_NULL, and NULL as actual argument. Here is the short form:
module M use IFWIN use ISO_C_BINDING implicit none interface function f(x) import implicit none !DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS: 'f' :: f integer(BOOL) f !DEC$ ATTRIBUTES REFERENCE, ALLOW_NULL :: x type(T_OVERLAPPED) x end function f end interface end module M subroutine S(i) use M implicit none integer i i = f(NULL) end subroutine S subroutine T(i) use M implicit none integer i type(T_OVERLAPPED), pointer :: x call C_F_POINTER(C_NULL_PTR,x) i = f(x) end subroutine T
After compiling with ifort /c /FA /integer_size:64 test.f90 we can see the results in test.asm:
_S PROC NEAR ; parameter 1: 4 + esp .B2.1: ; Preds .B2.0 L2:: ;17.12 sub esp, 8 ;17.12 pxor xmm0, xmm0 ;21.8 movq QWORD PTR [esp], xmm0 ;21.8 call _f@4 ;21.8 ; LOE eax ebx ebp esi edi .B2.2: ; Preds .B2.1 cdq ;21.4 mov ecx, DWORD PTR [4+esp] ;17.12 movd xmm1, eax ;21.4 movd xmm0, edx ;21.4 punpckldq xmm1, xmm0 ;21.4 movq QWORD PTR [ecx], xmm1 ;21.4 ret ;22.1 ALIGN 16 ; LOE ; mark_end; _S ENDP
_T PROC NEAR ; parameter 1: 4 + esp .B3.1: ; Preds .B3.0 L3:: ;24.12 push OFFSET FLAT: _T$X.0.3 ;29.9 push OFFSET FLAT: _ISO_C_BINDING_mp_C_NULL_PTR ;29.9 call c_f_pointer_set_scalar ;29.9 ; LOE ebx ebp esi edi .B3.6: ; Preds .B3.1 add esp, 8 ;29.9 ; LOE ebx ebp esi edi .B3.2: ; Preds .B3.6 push DWORD PTR [_T$X.0.3] ;30.8 call _f@4 ;30.8 ; LOE eax ebx ebp esi edi .B3.3: ; Preds .B3.2 cdq ;30.4 mov ecx, DWORD PTR [4+esp] ;24.12 movd xmm1, eax ;30.4 movd xmm0, edx ;30.4 punpckldq xmm1, xmm0 ;30.4 movq QWORD PTR [ecx], xmm1 ;30.4 ret ;31.1 ALIGN 16 ; LOE ; mark_end; _T ENDP
So we can see that when the actual argument was NULL as in subroutine S, the compiler made space for 8 bytes on the stack and filled it with zeros from xmm0.
When the actual argument was a Fortran pointer to TYPE(T_OVERLAPPED) with address of target set to C_NULL_PTR as in subroutine T, it pushed the DWORD address stored in the pointer descriptor (which may be all there is for a pointer to scalar).
So there's your bug reduced to a brief subroutine and a workaround.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
RO, thanks for the diagnosis and test case! I'll take it from here.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I was concerned that my workaround might not work if a pointer to a scalar doesn't contain any information beyond its address. If that's the case, the O.P. may have to provide his own interface body for WriteFile(). So to check, I came up with a test to measure the size of a pointer:
program P use IFWIN implicit none type T type(T_OVERLAPPED), pointer :: x end type T ! type T_OVERLAPPED ! integer x,y,z ! end type T_OVERLAPPED type(T) y write(*,*) sizeof(y) end program P
This got me the error (which I believe to be incorrect):
test1.f90(5): error #6457: This derived type name has not been declared. [T_OV ERLAPPED] type(T_OVERLAPPED), pointer :: x -----------^
Now if I uncomment the T_OVERLAPPED derived type definition, I get errors as one might expect, but the error messages are a little funky:
test1.f90(9): error #6401: The attributes of this name conflict with those made accessible by a USE statement. [T_OVERLAPPED] end type T_OVERLAPPED ------------^ test1.f90(9): error #6148: The name on this END TYPE statement is different from the name on the corresponding derived type statement. [T_OVERLAPPED] end type T_OVERLAPPED ------------^
Note that the first error message points at the END TYPE statement instead of the TYPE statement and the second message is just wrong.
Commenting out the USE IFWIN statement at this point allows the compilation to go through as expected, with output 4, indicating that the O.P. may indeed have to rework the interface body. If the lpOverlapped argument is OPTIONAL rather than ALLOW_NULL, will a non-present actual argument pass C_NULL_PTR by value in ifort's implementation?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I have escalated the original issue as DPD200381259. I was a bit surprised to find that if I passed 0_4 that the error was still there.
I think the simple workaround is:
type(T_OVERLAPPED), pointer :: NULL_OVERLAPPED => NULL()
and pass NULL_OVERLAPPED.
Now to look at the issues in post 6.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Post 6 issues escalated as DPD200381265. If the component isn't a pointer, or if the type is declared as a TYPE rather than a STRUCTURE, it's ok. (T_OVERLAPPED has to be a STRUCTURE because it has a UNION.)
I also reproduced the odd second error, though this may be just a fallout of the first one. I did ask the developers to look at that too.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I ended up declaring my own interface - seems like the simple solution of not having to worry about type definitions for something I'd like to be 0. I changed use kernel32 to use ifwinty, and:
INTERFACE
FUNCTION WriteFile(
& hFile,
& lpBuffer,
& nNumberOfBytesToWrite,
& lpNumberOfBytesWritten,
& lpOverlapped)
import
integer(BOOL) :: WriteFile ! BOOL
!DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE :: WriteFile
!DEC$ ATTRIBUTES ALIAS:'WriteFile' :: WriteFile
integer(HANDLE) hFile ! HANDLE hFile
integer(LPCVOID) lpBuffer ! LPCVOID lpBuffer
integer(DWORD) nNumberOfBytesToWrite ! DWORD nNumberOfBytesToWrite
integer(LPDWORD) lpNumberOfBytesWritten !LPDWORD lpNumberOfBytesWritten
integer(INT_PTR_KIND()) lpOverlapped ! LPOVERLAPPED lpOverlapped
END FUNCTION
C
FUNCTION ReadFile(
& hFile,
& lpBuffer,
& nNumberOfBytesToRead,
& lpNumberOfBytesRead,
& lpOverlapped)
import
integer(BOOL) :: ReadFile ! BOOL
!DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE :: ReadFile
!DEC$ ATTRIBUTES ALIAS:'ReadFile' :: ReadFile
integer(HANDLE) hFile ! HANDLE hFile
integer(LPVOID) lpBuffer ! LPVOID lpBuffer
integer(DWORD) nNumberOfBytesToRead ! DWORD nNumberOfBytesToRead
integer(LPDWORD) lpNumberOfBytesRead ! LPDWORD lpNumberOfBytesRead
integer(INT_PTR_KIND()) lpOverlapped ! LPOVERLAPPED lpOverlapped
END FUNCTION
END INTERFACE
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The post 6 error is fixed for a major version later this year.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page