At least I think it is a stack problem causing my program to crash
Here is what is causing the problem, stripped down to it's basic form
DlgProc
....
....
....
invoke ParseString,addr RecvBuffer,addr HexBuffer,addr ParseBuffer
DlgProc endp
ParseString proc uses ebx ebp esi edi BinData:DWORD, AsciiData:DWORD, TextBuffer:DWORD
; mov esi,BinData
; mov edi,AsciiData
; mov edx,TextBuffer
; xor ebp,ebp
; ......
; ......
; ......
call ParseZLoop
ret
ParseZLoop
; .....
; .....
; .....
ret
ParseString endp
As soon as I call ParseZLoop my program just dissappears, no windows error message, it's just gone from the Task Manager as well as the screen. I trierd REM out the lines that initialise esi,edi,ebp,edx and it is still crashing I also simplified ParseZLoop to a single ret opcode.
Now if I try this
ParseECM proc BinData:DWORD, AsciiData:DWORD, TextBuffer:DWORD
; mov esi,BinData
; mov edi,AsciiData
; mov edx,TextBuffer
; xor ebp,ebp
call ParseZLoop
ret
ParseZLoop
ret
ParseString endp
Then it does not crash, just returns to the main dialog without doing anything useful, as expected. So i figure it is a stack problem. But obviously I have the proc USES syntax as I'm gonna use those registers and need to preserve them
But I thought that proc USES just pushes the registers and pops them when the procedure ends so surely here we have
on entry to ParseString
push ebx
push ebp
push esi
push edi
call ParseZLoop -> push program counter
ret <- pop program counter
ret
pop edi
pop esi
pop ebp
pop ebx
So what's the problem causing the crash?? OK I'm not sure which order the USES statement pushes/pops the registers but the above seems about right to my mind.
TIA for helping
dicky
Try to avoid using ebp:
Quote%EBP - Base Pointer
This 32-bit register is used to reference all the function parameters and local variables in the current stack frame. Unlike the %esp register, the base pointer is manipulated only explicitly. This is sometimes called the "Frame Pointer".
Thanks Phoenix
I can accept the advice and not use ebp and I'll bet that will fix things
.... but I still don't understand the actual reason why my program bombs as I thought the stack pointer was ESP and I don't see where I was unbalancing the stack
You have ParseZLoop inside the ParseString PROC.
ParseZLoop is not recognized as a PROC, but the RET in it is interpreted as a return from ParseString. With EBP unchanged, it will simply return directly from ParseString.
In a PROC, EBP is used to reset the stack on a RET. If you don't change EBP, the stack is reset to where it was on PROC entry. Thus you will not get expected results if ParseZLoop is called from somewhere in the middle of ParseString. Instead of returning to ParseString, it will abort ParseString.
Move the ParseZLoop subroutine outside the ParseString PROC.
?
dicky,
All that is happening with your register usage is you are assuming a stack frame with the PROC layout you are using then modifying one of the registers required to maintain a stack frame. If you use a normal MASM "PROC / ENDP" then you do not modify either EBP or ESP as they are pointers to the base and stack addresses respectively.
When you write very small procedures that get called at a very fast rate, you can write a procedure that does not have a stack frame which does two things, reduces the call overhead and make EBP available within your procedure but such procedures are a bit more restricted in what you can do with them.
With a normal procedure its a good coding habit to start using LOCAL variables first then once you have the proc up, running and reliable you can see if you can make it faster by replacing local variables with registers but if you start the other way around, you usually run out of registers before you get the procedure working.
Thanks again Hutch - u must get fed up of digging me out of the dirt on a regular basis :lol
I think I need (ok, I WANT) to know more about stack frames and the use of this base address register
Is there some good tutorial on this topic
Also how to write a procedure that does not use a astack frame would be interesting
In reality this ParseZLoop is just a subroutine that gets called often from different parts of my procedure, so I just stuck it in a call --- ret so I don't have to writre irt several times
But of course you knew that.....
dicky
Quote from: dicky96 on September 06, 2006, 08:54:41 AMAlso how to write a procedure that does not use a astack frame would be interesting
The easiest way (IMO) is to use my pmacros (http://www.masm32.com/board/index.php?topic=1063.0) :wink
dicky96,
Quote
Also how to write a procedure that does not use a astack frame would be interesting
Because you asked for it, I have a license to show you. This method is not for everyone, especially beginners. If it is too heavy for you, just forget it and learn subroutines the conventional way. Ask if you have any questions. Ratch
http://www.masm32.com/board/index.php?topic=380.15 reply #15
http://www.masm32.com/board/index.php?topic=4650.0
http://www.masm32.com/board/index.php?topic=3938.0 reply #10
http://www.masm32.com/board/index.php?topic=2371.0
Hi guys
Well, I'm still having Stacks of problems LOL
I've come back to this part of my code, and re-wrote it without using ebp, but I'm still having problems. I
have a procedure that concatenates both asciiz strings and non-termianted data strings that have a len
parameter to make one big string.
I still couldn't understand why using EBP was screwing up my software, as I have another procedure in this
same program that has the Proc USES ebp.... syntax and that one works. The only difference I can see is taht
procedure does not call subroutines within itself and this one does. So here is the results of my
experimenting(or messing about!)
Here is example 1/ This has the Proc Uses .... syntax
ParseMess proc uses ebx esi edi BinData:DWORD, AsciiData:DWORD, TextBuffer:DWORD
mov esi,BinData
mov edi,AsciiData
mov edx,TextBuffer
lea ebx,ParseHeader ;address of header asciiz string
call Zloop ;write to output buffer
mov ebx,[offset_Data1] ;offset to data
mov cl, byte ptr [ebx+esi] ;byte count
call Cloop
lea ebx,ParsePRO ;address of Pro asciiz
call Zloop
mov ebx,[offset_DataPro] ;offset to ProData data
mov cl, byte ptr [ebx+esi] ;byte count
call Cloop
lea ebx,ParseCert ;address of Cert asciiz
call Zloop
mov ebx,[offset_CertData] ;offset to Cert data
mov cl, byte ptr [ebx+esi] ;byte count
call Cloop
ret
Zloop:
mov al,byte ptr [ebx]
.if al!=0
mov byte ptr [edx],al
inc ebx ;next char
inc edx ;next char
jmp Zloop
.endif
mov eax,eax
ret
Cloop:
shl cl,1 ;two bytes in ascii for 1 byte in bin
add ebx,1 ;first char is one byte after len byte
shl ebx,1 ;ebx = ebx*2
mov al,byte ptr [ebx+edi] ;get ascii char
mov byte ptr [edx],al ;and store in output buffer
inc ebx ;next source
inc edx ;next dest
dec cl
.if cl!=0 ;until cl = 0
jmp Cloop
.endif
ret
ParseMess endp
What is happening in this instance is the first time Zloop is called, it copies the first asciiz string
correctly, then the Zloop subroutine does not RET properly. It quits the ParseMess procedure without the rest
of the processing occuring. I was interested to see what is causing this so I used OllyDebug.
Now this is how MASM is assembling ZLoop
00401590 /$ 8A03 /MOV AL,BYTE PTR DS:[EBX]
00401592 |. 0AC0 |OR AL,AL
00401594 |. 74 06 |JE SHORT LemonAid.0040159C
00401596 |. 8802 |MOV BYTE PTR DS:[EDX],AL
00401598 |. 43 |INC EBX
00401599 |. 42 |INC EDX
0040159A |.^EB F4 \JMP SHORT LemonAid.00401590
0040159C |> 8BC0 MOV EAX,EAX
0040159E |. 5F POP EDI
0040159F |. 5E POP ESI
004015A0 |. 5B POP EBX
004015A1 |. C9 LEAVE
004015A2 \. C2 0C00 RETN 0C <-- My ParseMess Proc incorrectly terminates here :(
And this is how it assembles Cloop.
004015A5 /$ D0E1 /SHL CL,1
004015A7 |. 83C3 01 |ADD EBX,1
004015AA |. D1E3 |SHL EBX,1
004015AC |. 8A041F |MOV AL,BYTE PTR DS:[EDI+EBX]
004015AF |. 8802 |MOV BYTE PTR DS:[EDX],AL
004015B1 |. 43 |INC EBX
004015B2 |. 42 |INC EDX
004015B3 |. FEC9 |DEC CL
004015B5 |. 0AC9 |OR CL,CL
004015B7 |. 74 02 |JE SHORT LemonAid.004015BB
004015B9 |.^EB EA \JMP SHORT LemonAid.004015A5
004015BB |> 5F POP EDI
004015BC |. 5E POP ESI
004015BD |. 5B POP EBX
004015BE |. C9 LEAVE
004015BF \. C2 0C00 RETN 0C
Also at the RET on the end of ParseMess I also have the following, but that never gets executed
POP EDI
POP ESI
POP EBX
LEAVE
RETN 0C
So first question - why is MASM changing my simple RET at the end of Zloop and Cloop to add this extra code -
as that is what is causing my program to behave in this strange manner. My best guess is I have found some
strange bug in the Proc USES macro???
So next I changed my software like below example 2/
ParseMess proc BinData:DWORD, AsciiData:DWORD, TextBuffer:DWORD
push ebx
push esi
push edi
mov esi,BinData
mov edi,AsciiData
mov edx,TextBuffer
lea ebx,ParseHeader ;address of header asciiz string
call Zloop ;write to output buffer
mov ebx,[offset_Data1] ;offset to data
mov cl, byte ptr [ebx+esi] ;byte count
call Cloop
lea ebx,ParsePRO ;address of Pro asciiz
call Zloop
mov ebx,[offset_DataPro] ;offset to ProData data
mov cl, byte ptr [ebx+esi] ;byte count
call Cloop
lea ebx,ParseCert ;address of Cert asciiz
call Zloop
mov ebx,[offset_CertData] ;offset to Cert data
mov cl, byte ptr [ebx+esi] ;byte count
call Cloop
pop edi
pop esi
pop ebx
ret
Zloop:
mov al,byte ptr [ebx]
.if al!=0
mov byte ptr [edx],al
inc ebx ;next char
inc edx ;next char
jmp Zloop
.endif
mov eax,eax
ret
Cloop:
shl cl,1 ;two bytes in ascii for 1 byte in bin
add ebx,1 ;first char is one byte after len byte
shl ebx,1 ;ebx = ebx*2
mov al,byte ptr [ebx+edi] ;get ascii char
mov byte ptr [edx],al ;and store in output buffer
inc ebx ;next source
inc edx ;next dest
dec cl
.if cl!=0 ;until cl = 0
jmp Cloop
.endif
ret
This now assembles Zloop as follows
00401590 /$ 8A03 /MOV AL,BYTE PTR DS:[EBX]
00401592 |. 0AC0 |OR AL,AL
00401594 |. 74 06 |JE SHORT LemonAid.0040159C
00401596 |. 8802 |MOV BYTE PTR DS:[EDX],AL
00401598 |. 43 |INC EBX
00401599 |. 42 |INC EDX
0040159A |.^EB F4 \JMP SHORT LemonAid.00401590
0040159C |> 8BC0 MOV EAX,EAX
0040159E |. C9 LEAVE
0040159F \. C2 0C00 RETN 0C
And Cloop as follows
004015A2 /$ D0E1 /SHL CL,1
004015A4 |. 83C3 01 |ADD EBX,1
004015A7 |. D1E3 |SHL EBX,1
004015A9 |. 8A041F |MOV AL,BYTE PTR DS:[EDI+EBX]
004015AC |. 8802 |MOV BYTE PTR DS:[EDX],AL
004015AE |. 43 |INC EBX
004015AF |. 42 |INC EDX
004015B0 |. FEC9 |DEC CL
004015B2 |. 0AC9 |OR CL,CL
004015B4 |. 74 02 |JE SHORT LemonAid.004015B8
004015B6 |.^EB EA \JMP SHORT LemonAid.004015A2
004015B8 |> C9 LEAVE
004015B9 \. C2 0C00 RETN 0C
However that still did not fix the bug, my simple CALL - RET just does not return to the right place becuse it
seems like MASM is adding code of it's own
PS - I know I have a bug in Cloop looping to the wrong place - just ignore that pls.
Ahh after trying a few combinations I now have this one that works
ParseMess proc uses ebx esi edi BinData:DWORD, AsciiData:DWORD, TextBuffer:DWORD
mov esi,BinData
mov edi,AsciiData
mov edx,TextBuffer
lea ebx,ParseHeader ;address of header asciiz string
invoke Zproc ;write to output buffer
mov ebx,[offset_Data1] ;offset to data
mov cl, byte ptr [ebx+esi] ;byte count
invoke Cproc
lea ebx,ParsePRO ;address of Pro asciiz
invoke Zproc
mov ebx,[offset_DataPro] ;offset to ProData data
mov cl, byte ptr [ebx+esi] ;byte count
invoke Cproc
lea ebx,ParseCert ;address of Cert asciiz
invoke Zproc
mov ebx,[offset_CertData] ;offset to Cert data
mov cl, byte ptr [ebx+esi] ;byte count
invoke Cproc
ret
Zproc proc
Zloop:
mov al,byte ptr [ebx]
.if al!=0
mov byte ptr [edx],al
inc ebx ;next char
inc edx ;next char
jmp Zloop
.endif
mov eax,eax
ret
Zproc enp
Cproc proc
Cloop:
shl cl,1 ;two bytes in ascii for 1 byte in bin
add ebx,1 ;first char is one byte after len byte
shl ebx,1 ;ebx = ebx*2
mov al,byte ptr [ebx+edi] ;get ascii char
mov byte ptr [edx],al ;and store in output buffer
inc ebx ;next source
inc edx ;next dest
dec cl
.if cl!=0 ;until cl = 0
jmp Cloop
.endif
ret
Cproc endp
Now MASM assembles my code like this
Zproc
0040145A /$ 8A03 /MOV AL,BYTE PTR DS:[EBX]
0040145C |. 0AC0 |OR AL,AL
0040145E |. 74 06 |JE SHORT LemonAid.00401466
00401460 |. 8802 |MOV BYTE PTR DS:[EDX],AL
00401462 |. 43 |INC EBX
00401463 |. 42 |INC EDX
00401464 |.^EB F4 \JMP SHORT LemonAid.0040145A
00401466 |> 8BC0 MOV EAX,EAX
00401468 \. C3 RETN
Cproc
00401469 /$ D0E1 /SHL CL,1
0040146B |. 83C3 01 |ADD EBX,1
0040146E |. D1E3 |SHL EBX,1
00401470 |. 8A041F |MOV AL,BYTE PTR DS:[EDI+EBX]
00401473 |. 8802 |MOV BYTE PTR DS:[EDX],AL
00401475 |. 43 |INC EBX
00401476 |. 42 |INC EDX
00401477 |. FEC9 |DEC CL
00401479 |. 0AC9 |OR CL,CL
0040147B |. 74 02 |JE SHORT LemonAid.0040147F
0040147D |.^EB EA \JMP SHORT LemonAid.00401469
0040147F \> C3 RETN
Which has the effect I expected all along..... but noiw i'm really confused, if an invoke is just push some stuff on the stack, followed by a CALL.... then surely an invoke with no parameters is the same as a CALL
Please help save my sanity as it feels like MASM is doing very strange things to my code - adding stuff to ret rather than just assembling it, etc etc.
TIA
dicky
Ahaaa I just realised that tenkey explained all this about 9 days ago when i first posted this thread
quote:
ParseZLoop is not recognized as a PROC, but the RET in it is interpreted as a return from ParseString. With EBP unchanged, it will simply return directly from ParseString.
Please forgive me for being so dumb not to undertand his post until I did enough experiments to find all this out for myself the hard way.... thanks Tenkey, now I understand what you were trying to tell me all along: :U
So, in retrospect, is all this due to a bug (or limitation) in the PROC macro? not being able to tell the difference between a RET from the procedure, and a RET from a subroutine called within the procedure. At least that is how I understand it now.
And as a matter of interest where are these things like PROC USES.... defined? can they be changed or, errmmmm, debugged? :wink
Thanks again
dicky......... still a beginner i guess :red ::) :red
Quote from: dicky96 on September 14, 2006, 09:19:24 PM
So, in retrospect, is all this due to a bug (or limitation) in the PROC macro? not being able to tell the difference between a RET from the procedure, and a RET from a subroutine called within the procedure. At least that is how I understand it now.
Basically, yes, though it's as much the fault of the standard form of a procedure. The epilogue macro (more on this next) contains code to clean up a stack frame which, as part of this, moves the stack pointer to just below the original procedure entry. So then when the actual return command is called it returns from the original procedure.
There are two solutions. The first is to give your subroutine a stack frame. This is (generally) bad. If your subroutine needs a stack frame it should be its own procedure.
The second is to use RETN at the end of your subroutine. RET is actually a directive (like PROC) which calls an epilogue macro. RETN is the actual instruction and will simply pop the return address off and go there, rather than cleaning up the stack.
Quote from: dicky96 on September 14, 2006, 09:19:24 PM
And as a matter of interest where are these things like PROC USES.... defined? can they be changed or, errmmmm, debugged? :wink
I mentioned the epilogue macro used for the RET directive above. There is also a prologue macro for PROC. Both of these macros can be substituted for your own. There is a bit of information in the MASM32.HLP file which comes with MASM32. I have also posted everything I know about them here (http://msdnwiki.microsoft.com/en-us/mtpswiki/4zc781yh(VS.80).aspx), at the MSDN Wiki.
However, I don't think that overriding the prologue and epilogue code will help. Just use RETN at the end of your subroutine :wink
Cheers,
Zooba :U
quote
RET is actually a directive
Ahh that explains why I could not find RET in the assembler mnemonics but could see RETN - I thought it was just a missprint
but then again I once got lost on the London Underground as I thought that "Bank" was short for Enbankment" :bdg