|
forums.silverfrost.com Welcome to the Silverfrost forums
|
View previous topic :: View next topic |
Author |
Message |
wahorger
Joined: 13 Oct 2014 Posts: 1217 Location: Morrison, CO, USA
|
Posted: Wed Apr 25, 2018 5:58 pm Post subject: Speaking of stacks.... |
|
|
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.
Code: |
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:
Code: |
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. |
|
Back to top |
|
|
wahorger
Joined: 13 Oct 2014 Posts: 1217 Location: Morrison, CO, USA
|
Posted: Wed Apr 25, 2018 8:38 pm Post subject: |
|
|
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. |
|
Back to top |
|
|
wahorger
Joined: 13 Oct 2014 Posts: 1217 Location: Morrison, CO, USA
|
Posted: Thu Apr 26, 2018 5:26 pm Post subject: |
|
|
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. |
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Fri Apr 27, 2018 10:16 am Post subject: |
|
|
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.. |
|
Back to top |
|
|
wahorger
Joined: 13 Oct 2014 Posts: 1217 Location: Morrison, CO, USA
|
Posted: Fri Apr 27, 2018 2:00 pm Post subject: |
|
|
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. |
|
Back to top |
|
|
wahorger
Joined: 13 Oct 2014 Posts: 1217 Location: Morrison, CO, USA
|
Posted: Fri Apr 27, 2018 4:01 pm Post subject: |
|
|
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: | code
mov stack_at_entry,esp%
edoc
|
and current_stack.ins:
Code: | code
mov current_stack,esp%
edoc
|
The main body (forumtesting.for) is:
Code: |
! 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:
Quote: | 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 -8. 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: |
Stack values 56687648 56687656
Negative means likely corrupted stack: -8
Stack values 56687648 56687648
Negative means likely corrupted stack: 0
|
|
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Fri Apr 27, 2018 6:08 pm Post subject: |
|
|
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!
Last edited by mecej4 on Sat Apr 28, 2018 12:14 pm; edited 3 times in total |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7924 Location: Salford, UK
|
Posted: Fri Apr 27, 2018 10:14 pm Post subject: |
|
|
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@. |
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Sat Apr 28, 2018 2:51 am Post subject: |
|
|
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;
4) The address of the string '%sp&';
5) The integer 3, which is the number of arguments;
6) The string "*C*I*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.
Last edited by mecej4 on Sat Apr 28, 2018 9:28 am; edited 1 time in total |
|
Back to top |
|
|
wahorger
Joined: 13 Oct 2014 Posts: 1217 Location: Morrison, CO, USA
|
Posted: Sat Apr 28, 2018 4:06 am Post subject: |
|
|
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 |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7924 Location: Salford, UK
|
Posted: Sat Apr 28, 2018 7:56 am Post subject: |
|
|
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
Code: | 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. |
|
Back to top |
|
|
LitusSaxonicum
Joined: 23 Aug 2005 Posts: 2388 Location: Yateley, Hants, UK
|
Posted: Sat Apr 28, 2018 8:05 am Post subject: |
|
|
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 |
|
Back to top |
|
|
PaulLaidler Site Admin
Joined: 21 Feb 2005 Posts: 7924 Location: Salford, UK
|
Posted: Sat Apr 28, 2018 8:15 am Post subject: |
|
|
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...
Code: | module opengl
include <opengl.ins>
end module opengl
options(wide_source)
program main
use opengl
print*,"123456789012345678901234567890123456789012345678901234567890",GL_ACCUM
end |
|
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Sat Apr 28, 2018 8:51 am Post subject: Re: |
|
|
I urge that the suggested code
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
Code: | 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. |
|
Back to top |
|
|
mecej4
Joined: 31 Oct 2006 Posts: 1886
|
Posted: Sat Apr 28, 2018 9:39 am Post subject: Re: |
|
|
wahorger wrote: | 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.
Code: | 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.
2. Do not use MOD files with '$' in the name if your code is to be compiled with FTN95. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|