Silverfrost Forums

Welcome to our forums

Speaking of stacks....

25 Apr 2018 4:58 #21981

I was befuddled by a OpenGL error. The program would crash when a Fortran I/O statement was executed after about 150 iterations. If I removed the I/O statement, it works well. I suspected a stack issue.

So, I put in a stack checker, in-line assembly code that allows me to save the ESP register. I'd used this before with a different stack issue, to good success.

I simplified the first section of the code, generating a OpenGL window. Each Winio@ call causes the stack pointer to change. FYI: the OpenGL calls also do the same. I suspect this causes the crash when looping. If this were in a subroutine, the stack pointer is always returned to the proper value just prior to the return.

        use mswin
        integer i,ctrl
        integer stack_val
        code
        mov stack_val,ESP%
        edoc
        print *,'Before call=',stack_val
        call abcd
        code
        mov stack_val,ESP%
        edoc
        print *,'After call=',stack_val
        end
        subroutine abcd	
        integer i,ctrl
        integer stack_val
        i=winio@('%es&')
        code
        mov stack_val,ESP%
        edoc
        print *,'%es=',stack_val
        i=winio@('%ca[Simple OpengGL Sample]&')
        code
        mov stack_val,ESP%
        edoc
        print *,'%ca=',stack_val
        i=winio@('%sp&',0, 0)
        code
        mov stack_val,ESP%
        edoc
        print *,'%sp=',stack_val
        i=winio@('%ww[not_fixed_size]&')
        code
        mov stack_val,ESP%
        edoc
        print *,'%ww=',stack_val
        i=winio@('%og[static]&',800, 800)
        code
        mov stack_val,ESP%
        edoc
        print *,'%og=',stack_val
        i=winio@('%lw', ctrl)
        code
        mov stack_val,ESP%
        edoc
        print *,'%lw=',stack_val
        return
        end

On my system, the outputs show:

Before call=56687760
%es= 56687508
%ca= 56687504
%sp= 56687500
%ww= 56687496
%og= 56687492
%lw= 56687488
After call=56687608

Using compiler V8.3, Plato, compile options for RELEASE32=/SAVE /FPP /CFPP

FYI: The OpenGL calls affect the stack pointer by way more than just 4 bytes.

25 Apr 2018 7:38 #21986

N.B.: When I moved the looped sections of OpenGL from the Main to a subroutine (so the routine will get called 360 times), the program crashed after the first iteration. Moving the code back into Main means that I get the stack overflow eventually, but more than 1 iteration works.

The crash lists no routine names, just absolute offsets.

26 Apr 2018 4:26 #21998

Just as another possible reason why I can get things to crash (or not), it appears that the HEAP and STACK are allocated after the .reloc (reference the Section Map), and in that order, HEAP first, then STACK.

If the program over-runs the STACK, then it starts into the HEAP, then other data (if it doesn't crash first).

Do I have the ordering correct?

Because what I think I'm seeing with my 'crashes' is the call(s) to OpenGL routines likely use the stack heavily to allocate temporaries, and if the stack is insufficient, this corrupts the heap and/or other data areas.

27 Apr 2018 9:16 #22006

Bill, monitoring the stack pointer the way that you did can provide useful information, but one should not put too much value on that information. A wandering ESP value does not provide proof of memory leakage or incorrect stack management.

Before the 80386, one could not use the stack pointer in the R/M field of an instruction. You had to move the value of SP to BP, adjust the bias if necessary, and use BP in the R/M field. That changed with the 80386, and now EBP could be used as just another register instead of being dedicated to use as a frame pointer. Gfortran does, in fact, have the option -fomit-frame-pointer.

For a CDECL call on the 8086, we had PUSH arg1; PUSH arg2;...;CALL ..; ADD SP,nn. With the 80386 and later, the code emitted by compilers shows a more relaxed attitude, and ESP is restored many instructions after the CALL or not at all. When the code contains a number of Fortran CALLS with few other statements, ESP may be restored after all the calls have been executed.

I have not studied the code emitted by FTN95 often enough to say whether the above comments apply to it, but the modern relaxed view of ESP and EBP is something to keep in mind when inserting very short CODE ... EDOC sequences in Fortran code..

27 Apr 2018 1:00 #22009

Thanks, mecej4,

There can be negative ramifications for the stack pointer being manipulated/'adjusted' while the current routine is running. If the stack pointer is incremented (equivalent of a POP), then the contents of the stack can/will be trashed, resulting in many bad things happening when the module is exited.

Or, in the case I brought up a couple of years ago, the constant decrement of the stack pointer can eventually result in the stack intruding into the data apace, corrupting those data.

What I'm seeing in the OpenGL crashes are instruction addresses that appear to be in the data space. This would be consistent with the stack being hammered by, perhaps, the PUSH of a data item address into the return address. There are a myriad of possibilities.

In the OpenGL module where I am having issues, the stack appears to be totally trashed. Meaning the stack pointer after the OpenGL calls is greater than the stack pointer upon entry to the test_routine. This would look like 1 or more POP instructions. Which means that a subsequent call will wipe out one or more addresses/registers on the stack, and possibly altering the return address for the current module.

The value of the stack pointer is important.

Of greater concern is the OpenGL calls, which leave the stack pointer decremented by way too much.

I'm working on a smaller version of the OpenGL issue that can be posted here showing the issues.

27 Apr 2018 3:01 #22010

So, here is an example of what can happen with a messed up stack pointer.

The code segments below were run under Plato as Release WIN32 with the options: /SAVE /WIDE_SOURCE /EXPLIST /FPP /CFPP

The two include files are: save_stack.ins:

	code
	mov stack_at_entry,esp%
	edoc

and current_stack.ins:

	code
	mov current_stack,esp%
	edoc

The main body (forumtesting.for) is:

!       winapp
        use mswin
        use opengl$
        integer ctrl,window_handle
        integer stack_at_entry,current_stack
        stack_at_entry = 0
        current_stack = 0
        include 'save_stack.ins'
        i=winio@('%es%ca[Simple OpengGL Sample]&')
        i=winio@('%sp%ww[not_fixed_size]%og[static]%hw%lw',0, 0, 400, 400,window_handle,ctrl)
        CALL glClearColor (0.0, 0.0, 0.0, 0.0)
        include 'current_stack.ins'
        print *,'Stack values',stack_at_entry,current_stack
        print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack
        CALL glClear(GL_COLOR_BUFFER_BIT)
        CALL glColor3f(1.0, 1.0, 1.0)
        CALL glMatrixMode(GL_PROJECTION)
        CALL glLoadIdentity()
        CALL glOrtho(0d0, 10d0, 10d0, 0d0, -1d0, 1d0)
        call glnewlist(1,gl_compile)
        call glBegin(GL_LINE_STRIP)
        call glVertex2f(0.,0.)
        call glVertex2f(10.,10.)
        call glEnd()
        call glEndlist()
        call glCalllist(1)
        call glFinish()
        call swap_opengl_buffers@()
        call glCalllist(1)
        call glFinish()
        call swap_opengl_buffers@()
        include 'current_stack.ins'
        print *,'Stack values',stack_at_entry,current_stack
        print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack
        pause
        end

Running this yielded the following output:

&') i=winio@('%sp%ww[not_fixed_size]%og[static]%hw%lw',0, 0, 400, 400,window_handle,ctrl) CALL glClearColor (0.0, 0.0, 0.0, 0.0) include 'current_stack.ins' print *,'Stack values',stack_at_entry,current_stack print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack CALL glClear(GL_COLOR_BUFFER_BIT) CALL glColor3f(1.0, 1.0, 1.0) CALL glMatrixMode(GL_PROJECTION) CALL glLoadIdentity() CALL glOrtho(0d0, 10d0, 10d0, 0d0, -1d0, 1d0) call glnewlist(1,gl_compile) call glBegin(GL_LINE_STRIP) call glVertex2f(0.,0.) call glVertex2f(10.,10.) call glEnd() call glEndlist() call glCalllist(1) call glFinish() call swap_opengl_buffers@() call glCalllist(1) call glFinish() call swap_opengl_buffers@() include 'current_stack.ins' print *,'Stack values',stack_at_entry,current_stack print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack pause end

Running this yielded the following output:

&') i=winio@('%sp%ww[not_fixed_size]%og[static]%hw%lw',0, 0, 400, 400,window_handle,ctrl) CALL glClearColor (0.0, 0.0, 0.0, 0.0) include 'current_stack.ins' print *,'Stack values',stack_at_entry,current_stack print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack CALL glClear(GL_COLOR_BUFFER_BIT) CALL glColor3f(1.0, 1.0, 1.0) CALL glMatrixMode(GL_PROJECTION) CALL glLoadIdentity() CALL glOrtho(0d0, 10d0, 10d0, 0d0, -1d0, 1d0) call glnewlist(1,gl_compile) call glBegin(GL_LINE_STRIP) call glVertex2f(0.,0.) call glVertex2f(10.,10.) call glEnd() call glEndlist() call glCalllist(1) call glFinish() call swap_opengl_buffers@() call glCalllist(1) call glFinish() call swap_opengl_buffers@() include 'current_stack.ins' print *,'Stack values',stack_at_entry,current_stack print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack pause end

Running this yielded the following output:

&') i=winio@('%sp%ww[not_fixed_size]%og[static]%hw%lw',0, 0, 400, 400,window_handle,ctrl) CALL glClearColor (0.0, 0.0, 0.0, 0.0) include 'current_stack.ins' print *,'Stack values',stack_at_entry,current_stack print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack CALL glClear(GL_COLOR_BUFFER_BIT) CALL glColor3f(1.0, 1.0, 1.0) CALL glMatrixMode(GL_PROJECTION) CALL glLoadIdentity() CALL glOrtho(0d0, 10d0, 10d0, 0d0, -1d0, 1d0) call glnewlist(1,gl_compile) call glBegin(GL_LINE_STRIP) call glVertex2f(0.,0.) call glVertex2f(10.,10.) call glEnd() call glEndlist() call glCalllist(1) call glFinish() call swap_opengl_buffers@() call glCalllist(1) call glFinish() call swap_opengl_buffers@() include 'current_stack.ins' print *,'Stack values',stack_at_entry,current_stack print *,'Negative means likely corrupted stack:',stack_at_entry-current_stack pause end

Running this yielded the following output: [quote:da5bdd8644]Stack values 56687648 56687656 Negative means likely corrupted stack: -8 Stack values 56687648 56687736 Negative means likely corrupted stack: 269709653

Note that the last calculation shown is NOT the proper value (shown as 269709653, S/B -88). This is the example of how the stack pointer can affect the results. If I restore the stack pointer just prior to this last set of statements, the results show: [quote:da5bdd8644] Stack values 56687648 56687656 Negative means likely corrupted stack: -8 Stack values 56687648 56687648 Negative means likely corrupted stack: 0

27 Apr 2018 5:08 (Edited: 28 Apr 2018 11:14) #22012

The code clarifies things, thanks. You are concerned specifically about ESP changing after returning from a call to an OpenGL or Winio@ routine, rather than during the course of a sequence of assignment statements.

I do not use graphics routines/libraries with FTN95, so I can only remark that the correct interfaces must be used. The interface module and the DLL that provides the OpenGL routines must be consistent. Since I see a number of senior users discussing Clearwin usage, I assumed that matters were in great shape.

Just to see the effect, I replaced your USE OPENGL$ by USE XGL, where xgl.mod was produced by compiling a three-line file containing

module xgl include 'opengl.ins' end

I found that the stack convention was quite different from that which I saw when I had USE OPENGL$. The EXE produced with XGL uses STDCALL for OpenGL calls whereas the OPENGL$ and WINIO@ calls appear to be CDECL.

Silverfrost should be able to clarify this issue, unless the answers are already in the help file. I agree, Bill, that a Fortran CALL should result in ESP being restored to its value after the execution of the previous statement. Perhaps, Eddie or Dan can provide their opinions.

P.S. (after reading Eddie's response below): There is a table showing how to provide the proper OpenGL interfaces for 64bit Fortran compilers other than FTN95 at https://www.silverfrost.com/ftn95-help/clearwinp/util/64bit.aspx (I found that page through Google Search, and I hope to find a similar page for 32-bit calls). Much of the information on that page is date-sensitive, and we may only infer the date from the download information for a particular distribution of GFortran -- around 2011. A README.TXT, updated for each release of FTN95, with a catalog of the files included and their intended purpose, would be helpful in avoiding what Wahorger did, namely, using with FTN95 a MOD file meant for use with Gfortran -- OPENGL$.mod . The recently released 8.30 compiler does include a README.TXT; please read it!

27 Apr 2018 9:14 #22014

The following FTN95 files appear to be correct...

opengl.ins opengl.mod opengl.mod64

The (gFortran) file opengl$.mod looks a first sight to be wrong. It may be built from third party code that uses the wrong binding. I will make a note that this needs investigating. However, there is no need to use this particular file when compiling with FTN95.

The whole of the Silverfrost ClearWin+ library uses C_EXTERNAL (i.e. CDECL) binding and this includes winio@.

28 Apr 2018 1:51 (Edited: 28 Apr 2018 8:28) #22015

If I am not mistaken, I think that there is a longstanding problem here.

This is what I see happening when the Fortran source contains calls to WINIO@ and is compiled for 32-bit. The arguments are pushed on the stack in the usual CDECL way. Then, two additional hidden arguments are pushed before the CALL is made. This first of these is a count of the arguments, and the second hidden argument is a null-terminated string, apparently containing information regarding the argument types. After returning from the WINIO@ routine, one of these hidden arguments is not accounted for when the usual ADD ESP, nn adjustment is made.

For example, for the Fortran statement i=winio@('%sp&',0, 0), six items are pushed on the stack:

  1. The integer 4, which is the hidden string length argument; 2), 3) The integer 0, twice;
  2. The address of the string '%sp&';
  3. The integer 3, which is the number of arguments;
  4. The string 'CI*I\x00', which seems to indicate that the arguments are a pointer to a character variable and two pointers to integers. After the call to WINDOW_PRINTF@@ with these arguments, the stack adjustment is done with add esp,=20, and we have lost 4 bytes, as Wahorger pointed out. We should have seen add esp,=24, instead.

The fifth and sixth items above appear to be intended for calling library routines with generic names and/or optional arguments.

28 Apr 2018 3:06 #22016

Paul, thanks for the update.

I tried including the opengl.ins, but my source is .for, with wide_source enabled and the include file doesn't support this. I've run into this before, and could use some insight into how to turn the .ins file into a .mod file! I've looked, but not found, a method.

Perhaps that is the reason why all this is going on with the OpenGL!

Still doesn't quite explain the winio@ stack stuff. Then again, it's not likely to have hundreds or thousands of winio@ calls that muck up the stack!

Bill

28 Apr 2018 6:56 #22019

mecej4

Many thanks for the feedback. I will make a note that this needs checking out.

Bill

opengl.mod should have been built (by us) from the program

module opengl
include <opengl.ins>
end module opengl

I will need to check this out but in the meantime you could add this code to your program (if necessary, put it in a separate file and compile using free format).

opengl.ins should also work for fixed format and wide source. Again, I will take a look to see why this does not work.

28 Apr 2018 7:05 #22020

Mecej4,

I'm sorry to say that I've never been able to fathom OpenGL, and lost interest in trying when I discovered that different graphics cards support (or don't) different features, so in a race to the bottom, the common factor wasn't that much different to the GDI, which is guaranteed to be present on every Windows PC. I had hoped to learn from a regular contributor to the forum, but he died some years ago. Almost certainly if he'd lived he would be on to 64 bit and OpenGL.

My basic position on anything to do with Fortran, graphics, Windows, life, the Universe and everything is that if it is there it should work, whether I personally need it or not. In the case where something did work, but now doesn't, or never worked at all, it is far easier to live with if one can find out about it before spending weeks struggling, and if there is a criticism in this it is about the failure to use the knowledgebase in the Forum or elsewhere to point out such issues. Take for example the Clearwin+ handle issue - it was public knowledge, and that was lost in the depths of the forum. Now it resurrected it is going to be dealt with. I must add that SF and Paul are good at addressing issues that are raised and the documentation does get updated, but less obviously.

Eddie

28 Apr 2018 7:15 #22021

Bill

/wide_source does not work with opengl.ins and fixed format because the trailing ampersand is not consumed correctly.

The following code works correctly. /wide_source is replaced by an OPTIONS compiler directive...

      module opengl 
      include <opengl.ins> 
      end module opengl
      options(wide_source)
      program main
      use opengl
      print*,'123456789012345678901234567890123456789012345678901234567890',GL_ACCUM
      end
28 Apr 2018 7:51 #22022

I urge that the suggested code

      module opengl 
      include <opengl.ins> 
      end module opengl
      options(wide_source)
      program main
      use opengl
      print*,'123456789012345678901234567890123456789012345678901234567890',GL_ACCUM
      end

be split into two pieces, with the first piece being just

      module opengl 
      include <opengl.ins> 
      end module opengl

Doing so will give two benefits: no imposing of the source format needs of one part on the other; no need to run large fixed content files such as OPENGL.INS through the compiler whenever a user program is recompiled.

28 Apr 2018 8:39 #22023

Quoted from wahorger I tried including the opengl.ins, but my source is .for, with wide_source enabled and the include file doesn't support this. I've run into this before, and could use some insight into how to turn the .ins file into a .mod file! I've looked, but not found, a method.

This is a repetition of what has been stated in other responses, but I don't want you to miss it:

  1. Create a separate source file in the same form as that of the INS file, with just three lines.

       module opengl 
       include <opengl.ins> 
       end module opengl
    

Compile this file, and USE the module, instead of including OPENGL.INS in your regular Fortran source files.

  1. Do not use MOD files with '$' in the name if your code is to be compiled with FTN95.
28 Apr 2018 11:33 (Edited: 29 Apr 2018 12:21) #22024

I tested Wahorger's program (with the stack probes removed, see below for source) with versions 6.35, 7.20 and 8.30 of the compiler. With all the compilers, we see the bug associated with 32-bit WINIO@ calls; each call to WINIO@ leaves the stack pointer ESP reduced by 4 bytes. To see this, compile with /debug, open the resulting program in SDBG, put breakpoints on the lines with the WINIO@ calls, open the registers window, and observe the ESP value before and after stepping over each of the two WINIO@ calls.

After discovering this bug, I went back to a number of bug reports submitted by users concerning graphics. I built the test codes that they submitted with the 32-bit compiler and watched the stack as I stepped over WINIO@ statements: I saw that 4 bytes are lost per call, every time.

If the correct OpenGL interface module is used (not opengl$ -- that is for compilers other than FTN95), there is no stack related problem with OpenGL calls.

There is no stack problem in 64-bit mode. The second WINIO@ call has more than 4 arguments, so the stack does get used.

Perhaps some users will be misled by the '64 bit Clearwin+' entry in the current FTN95 help file, since it pertains to non-FTN95 compilers.

        winapp 
        program bw
        use clearwin
        use opengl
        implicit none
        integer, external :: winio@ 
        integer :: i,ctrl
        integer(kind=7) :: window_handle 
        i=winio@('%es%ca[Simple OpengGL Sample]&') 
        i=winio@('%sp%ww[not_fixed_size]%og[static]%hw%lw',0, 0, 400, 400,window_handle,ctrl) 
        CALL glClearColor (0.0, 0.0, 0.0, 0.0) 
        CALL glClear(GL_COLOR_BUFFER_BIT) 
        CALL glColor3f(1.0, 1.0, 1.0) 
        CALL glMatrixMode(GL_PROJECTION) 
        CALL glLoadIdentity() 
        CALL glOrtho(0d0, 10d0, 10d0, 0d0, -1d0, 1d0) 
        call glnewlist(1,gl_compile) 
        call glBegin(GL_LINE_STRIP) 
        call glVertex2f(0.,0.) 
        call glVertex2f(10.,10.) 
        call glEnd() 
        call glEndlist() 
        call glCalllist(1) 
        call glFinish() 
        call swap_opengl_buffers@() 
        call glCalllist(1) 
        call glFinish() 
        call swap_opengl_buffers@() 
        pause 
        end 

The source code used to produce the two modules:

      module opengl
      include <opengl.ins>
      end module
      module clearwin
      include <clearwin.ins>
      end module
29 Apr 2018 4:28 #22026

After correcting the opengl.ins issues (local copy with edits), I got the following results.

  1. Three routines in opengl.ins are not found in the opengl32.dll. This is odd, but Dan doesn't use these three calls for his routines, so still working on this.

glClearColor glClear glColor3f

The routines do have a proper declaration in opengl.ins, but do not apparently exist in the opengl32.dll file in the c:\Windows\system32 folder that the linker uses (by default). I find this odd because these three routines are part of the original OpenGL baseline. But, I took them out, and things still work, so......

  1. Taking those calls out leaves me with **no issues **with the OpenGL calls remaining. No stack issues with OpenGL calls whatsoever. This is VERY helpful to me, and I thank all of you for weighing in.

  2. There exists a conflict between 'USE MSWIN' and 'INCLUDE 'opengl.ins'', specifically the routines in the opengl.ins file (right at the bottom) namely:

! ! Win32 API functions converted from wingdi.h ! STDCALL WGLUSEFONTOUTLINES 'wglUseFontOutlinesA' (VAL,VAL,VAL,VAL &,VAL,VAL,VAL,REF):LOGICAL4 STDCALL SWAPBUFFERS 'SwapBuffers' (VAL):LOGICAL4 STDCALL WGLUSEFONTBITMAPS 'wglUseFontBitmapsA' (VAL,VAL,VAL,VAL)
&:LOGICAL4 STDCALL WGLSHARELISTS 'wglShareLists' (VAL,VAL):LOGICAL4 STDCALL WGLMAKECURRENT 'wglMakeCurrent' (VAL,VAL):LOGICAL4 STDCALL WGLGETPROCADDRESS 'wglGetProcAddress' (STRING):INTEGER(7) STDCALL WGLGETCURRENTDC 'wglGetCurrentDC':INTEGER(7) STDCALL WGLGETCURRENTCONTEXT 'wglGetCurrentContext':INTEGER(7) STDCALL WGLDELETECONTEXT 'wglDeleteContext' (VAL):LOGICAL4 STDCALL WGLCREATECONTEXT 'wglCreateContext' (VAL):INTEGER(7)

In my modified opengl.ins file, I took these declarations out (commented), and no compile errors.

mecej4, I have not yet created a .MOD file. Thanks for the process!

29 Apr 2018 8:29 #22028

Bill,

I made search in my codes briefly and found glColor3f, glClear was still used (see links below ). But in one place i found my comment that glColor3f conflicted with transparency bit when you set the color 4-byte RGBT value where T is for transparency. That was so long ago i forgot any details. Probably this was a result of try-and-see.

Interesting that the part you commented is conflicting with something. This could be the reason that this part was on and off in opengl.ins for years and i complained about its extraction because wglUseFontBitmaps is used for displaying 3D fonts.

https://forums.silverfrost.com/Forum/Topic/1339&highlight=wglusefontbitmaps

https://forums.silverfrost.com/Forum/Topic/2722&highlight=font+opengl

29 Apr 2018 2:04 #22030

Quoted from wahorger

  1. Three routines in opengl.ins are not found in the opengl32.dll.

glClearColor glClear glColor3f

The routines do have a proper declaration in opengl.ins, but do not apparently exist in the opengl32.dll file in the c:\Windows\system32 folder that the linker uses (by default). I find this odd because these three routines are part of the original OpenGL baseline.

I looked at the opengl32.dll in my W10X64 system32 and syswow64 directories, and both do contain all three of the routines that you listed.

What are the date, file size and version number of your opengl32.dll?

29 Apr 2018 4:34 #22031

Don't forget that SLINK probably does not automatically scan openGL32.dll.

Please login to reply.