Where is the bug? The proc is 200+ lines, here are the 10% that caused a bug that was well hidden:
xor ecx, ecx
.if edx==STILL_ACTIVE
push chr$("'still active'")
.if [ebx.BuildVars.IsConsole] ; a Windows app is always still active...
dec ecx ; set the flag
.endif
.else
dec ecx
push str$(edx)
.endif
lea edi, buffer
jecxz @F
push eax
invoke lstrcpy, edi, Chr$("** RichMasm warning:", 13, 10, "exit code ")
push edi
call lstrcat
invoke lstrcat, edi, chr$(" **", 13, 10)
SetWin$ hStatus=edi
@@:
I caught it by comparing the stack at start and end of the proc with a dedicated macro that replaced the ret before the endp. Over 100 rets in the 21,000 lines source are ok, this one misbehaved.
The nastiest aspect here is that often you will not notice the problem, because the leave or mov esp, ebp makes sure you return to the correct address. But you are returning there with incorrectly restored esi, edi and ebx... and that may or may not cause a crash, and it may happen when you least suspect it. One of those bugs that will cause you sleepless nights :cool:
So far the routine works fine, and once it will be well tested and stable enough, I will post macro and *.lib file.
But I have a question: Does anybody have an idea how to safely recognise the start and end of a proc that has no stack frame?
In the meantime, I found at least a method to recognise that a proc has no stack frame.
The idea is very simple: in a large source, replace every ret that is followed by somecode endp with @MbRet. It takes an optional argument that should be the name of the proc; if there is no argument, the source line will be shown instead (as in the CodeNoArgs example below).
If the source has stack problems, they will be ## flagged to the console ##, even if the exe was built as a Windows application.
There are basically three cases:
1. too many push instructions: @MbRet will tell you about it, and correct the stack so that at least the uses esi edi ebx works properly.
2. too many pop instructions: @MbRet will tell you about it, and correct the stack but one or more of the regs in the uses esi edi ebx list will be trashed anyway.
3. stack corruption: the return address is somewhere in nirwana land. @MbRet will inform you about it, but the program will crash anyway.
I've tested it with a 20k lines source, and so far I've found one bug with it, see first post above. @MbRet tests runtime behaviour, so its messages will pop up only when you are really using a proc. So for rarely used exotic functions it may take a while until you discover the bug.
Once you have verified that there are no problems, put @MbRet off below the include line, so that only an ordinary ret will be generated.
Here is a little demo with some procs that treat the stack badly (tested ok with WinXP, Win7-64 and Win10):
include \masm32\include\masm32rt.inc ; purest Masm32 SDK code
include MbRet.inc ; must be in the exe's folder
; @MbRet off ; switch it off once tests are OK
.code
TwoArgsNoLocals proc uses esi edi ebx mode, crashArg
mov ebx, mode ; no Locals, but has args, and therefore a stack frame
print "2 args: ebx="
print str$(ebx), 13, 10
.while sdword ptr ebx>=0
push eax ; stack imbalances will be flagged
dec ebx
.Endw
pop eax ; stack is balanced
pop ecx ; for mode=3
pop edx
.if crashArg==99
mov dword ptr [ebp+4], 12345678h ; bad news for the return address ;-)
.endif
@MbRet 2ArgsNoLocals ; instead of the ret
TwoArgsNoLocals endp
ThreeArgsLocal123 proc uses ecx mode, arg2, arg3
Local buffer[123]:BYTE
push eax ; stack not balanced, will be flagged
@MbRet ThreeArgsLocal123
ThreeArgsLocal123 endp
CodeNoArgs proc uses esi edi ebx ecx ebp
print "CodeNoArgs - cannot work with @MbRet: "
; push eax ; activate to produce a crash
@MbRet ; no label -> line number 32; @MbRet will say "no frame found"
CodeNoArgs endp
start:
cls
m2m esi, 3
mov ebx, 12345678h
.Repeat
invoke TwoArgsNoLocals, esi, 123
print hex$(ebx), ": ebx after the invoke", 13, 10, 10
dec esi
.Until Zero?
invoke CodeNoArgs
invoke ThreeArgsLocal123, 123, 456, 789
inkey chr$(10, "hit any key for the stack corruption test:", 13, 10)
invoke TwoArgsNoLocals, 2, 99
MsgBox 0, "You won't see this box", "Hi", MB_OK
invoke ExitProcess, 0
end start
The procs have little problems with a sloppy use of the push and pop instructions. Here is the output:
2 args: ebx=3
### MbRet 2ArgsNoLocals: stack is -4 bytes off, 1 push(es) too many ###
12345678: ebx after the invoke
2 args: ebx=2
12345678: ebx after the invoke
2 args: ebx=1
### MbRet 2ArgsNoLocals: stack is 4 bytes off, 1 pop(s) too many - check uses ###
0040107A: ebx after the invoke
CodeNoArgs - cannot work with @MbRet: ### MbRet 31: no stack frame found ###
### MbRet ThreeArgsLocal123: stack is -4 bytes off, 1 push(es) too many ###
hit any key for the stack corruption test:
2 args: ebx=2
### stack corruption? 2ArgsNoLocals: retaddr 12345678h is above the code segment ###
After the stack corruption test, it will produce an exception.
Very interesting JJ :thumbsup:
When there is no stack perhaps you can make an @MbTestStack.
Text output is a little confusing, perhaps in a line " MbRet name [file , line]" , details in following lines and finally an empty line.
Of course a summary: N_stack_procs, N_non_stack_procs (yellow if > 0?), unbalances detected (green when 0, red some case) :biggrin:
It may help a little if you looked for the alignment padding after a RET but fundamentally a no stack frame procedure is hard to determine because you don't have an immediate technique to find the start and end of the proc. If it is being found with a disassembler, the output generally shows the start label (address) of the proc but if you have to do it in bare mnemonics you have a hard task in front of you.
With a disassembler it checks the address that a CALL mnemonic addresses which gives you the start address but there is no easy way to get the procedure end, possible the last RET before the next known CALL label and if it has alignment padding between the RET and the next label.
The current version can determine if it is a no-frame procedure, and where its end is. What it cannot do so easily is find the start of the proc. So at present it will just issue a line saying "can't check a no frame proc".
Is anybody aware of a big asm source in the public domain? I need a test case that looks different from my own stuff.
Quote from: jj2007 on March 06, 2020, 04:29:43 AM
What it cannot do so easily is find the start of the proc. So at present it will just issue a line saying "can't check a no frame proc".
iI you bother to write @MbRet at procedures ends, cost you just a cent to write @MbTestStack at its beginnings. :cool:
What about writing custom prologues and epilogues?
Biterider
Quote from: HSE on March 06, 2020, 04:55:46 AM
If you bother to write @MbRet at procedures ends, cost you just a cent to write @MbTestStack at its beginnings. :cool:
Correct, but replacing a hundred
ret..whatever
endp pairs in a big source is done in a single Find & Replace command. Inserting a macro on top, after the LOCALS, is a bit more involved.
Quote from: Biterider on March 06, 2020, 06:41:54 AM
What about writing custom prologues and epilogues?
Sure, that's the best option, but you risk breaking old code. The current @MbRet does not require such efforts, and it also doesn't generate any extra code once you switch if off with a single statement. It is an absolutely harmless add-on.
Custom prologues have much bigger implications. For example, you must manage 'by hand' all those cases where you restore prologuedef because a proc required option prologue:none. That's a can of worms.
Quote from: jj2007 on March 06, 2020, 07:18:23 AM
Inserting a macro on top, after the LOCALS, is a bit more involved.
Perhaps a little parser. :thumbsup:
if you make a test.asm piece that is made up with calling every proc,mixed with INT3's and run in debugger and it would be possible to find the faulty PROC
Quote from: daydreamer on March 06, 2020, 10:32:03 PM
if you make a test.asm piece that is made up with calling every proc,mixed with INT3's and run in debugger and it would be possible to find the faulty PROC
Great advice, Magnus :thumbsup:
Can you post one of your major sources, so that we can test it?
What I am not sure about is what format you are working with, if its source code, you would list every CALL mnemonic which includes any variant of invoke and list the targets by their line number once you had the list. I gather you already have a technique to find the end of the procedure, even if it has multiple RET instructions in it. If you are dealing with binary format mnemonics you would basically be designing a dis-assembler.
Hutch,
It's not source code, it's the executable:
MyStuff proc uses esi edi bla1 bla2
...
ret
MyStuff endp
MyStuff proc uses esi edi bla1 bla2
...
@MbRet ; <<<<<<<<<<<<< it it's OFF, just a ret; if it's ON, call TESTWHATEVER, then ret
MyStuff endp
Quote from: jj2007 on March 07, 2020, 12:23:21 AM
Quote from: daydreamer on March 06, 2020, 10:32:03 PM
if you make a test.asm piece that is made up with calling every proc,mixed with INT3's and run in debugger and it would be possible to find the faulty PROC
Great advice, Magnus :thumbsup:
Can you post one of your major sources, so that we can test it?
maybe if I put together all PROC's I wrote in one file converted to MACROs? :smiley:
Yes, go ahead, do that and post it here!
Hi jj2007,
if i get this right, you want an automated test, if the stack is balanced, when you leave any kind of procedure. What about doing it the other way round ?
Save ESP before the procedure call (invoke ...) and test it afterwards. You could wrap invoke with a macro and add the test code inside this macro. The more, the procedure name is already there as a parameter - so it´s easy to output a meaningful debug message, and it´s easy to adapt existing code, just replace "invoke" with your new macro .
Quote from: JK on March 11, 2020, 04:48:09 AMSave ESP before the procedure call (invoke ...) and test it afterwards. You could wrap invoke with a macro
Hi JK,
Your idea is good but there are three problems:
1. many people do not invoke but push parameters manually (even I do that often)
2. when invoking procs that have a stack frame, your macro will not see the problem because
esp is ok
3. when invoking procs that do have a stack frame, your macro will not be able to print the message because the return will be to no man's land.
there is alternative to pushs/pops to save/restore general purpose registers,bigger opcodes(3bytes) to save/restore in SIMD registers,but doesnt mess up stack
there is fastcall convention as alternative to use stack together with invoke if its your own made PROC's
you use registers instead of stack for data
windows api call you need to stick to invoke/stack
if you only call the PROC from one place in the code,a MACRO version of that proc,would get rid of possibility of ret to "nomansland",but if you want to use it several times,its no good to use it 5 times,because that will insert it inline resulting in 5 times bigger code than one PROC
Great ideas, daydreamer. Post some examples, please. Code :cool:
If its a test of the stack balance, that is in fact easy, allocate a variable then move ESP into it BEFORE you call the proc then display the variable in either a MessageBox or at the console.. After the proc has returned compare the two results, if they are the same, the stack is balanced, if not you have something to fix.
Quote from: hutch-- on March 11, 2020, 10:50:56 PM
If its a test of the stack balance, that is in fact easy, allocate a variable then move ESP into it BEFORE you call the proc then display the variable in either a MessageBox or at the console.. After the proc has returned compare the two results, if they are the same, the stack is balanced, if not you have something to fix.
Apart from being a clumsy way to do that check: Most of the time the stack will be balanced
even if you have a bug. The miraculous
leave instruction balances the stack for you, and leaves subtle little problems such as esi edi ebx out of tune. Often there is no immediate crash, but it may bite you later. Thanks to @MbRet, I've identified three little problems in my editor - and it looked stable before, I work with it every day.
This is what I don't understand what you are trying to do. At the minimum a proc with no stack frame has a start address and at one or more locations, it is exited by a RET. I still don't know if this is with a disassembly, parsed source code or assembled code. If you do the normal STDCALL preservation of registers, the stack increments by the size of the PUSH mnemonic up to the number of preserved registers.
I still don't know what you are trying to achieve.
Quote from: jj2007 on March 11, 2020, 10:15:23 PM
Great ideas, daydreamer. Post some examples, please. Code :cool:
inspired by windows message proc handler switch,using readable constants instead of numbers
so far,same problem with unbalanced stack so I solved with commented out so I use eax instead(fastcall)
maincompos proc ; vers:DWORD
mov vers2,eax
lea ebx,cbuffer
mov eax,0
mov ecx,0
@@L1:
mov [ebx+ecx*4],eax
inc ecx
cmp ecx,1000000
jne @@L1
;mov ebx,esi
switch vers2 ;check if mul,SSE2,SSE,fpu,AVX version
case ordinary
jmp @@v1
case sse2
;jmp SSE2vers
case sse
ssem
case fpu
;jmp fpuvers
case avx
jmp avxvers
endsw
gonna test conditional assembly on PROC's not needed when using MACRO version
I think he wanted code that actually worked.
Quote from: hutch-- on March 12, 2020, 02:56:48 AM
I think he wanted code that actually worked.
Correct :tongue:
Quote from: hutch-- on March 12, 2020, 12:35:41 AMAt the minimum a proc with no stack frame has a start address and at one or more locations, it is exited by a RET. I still don't know if this is with a disassembly, parsed source code or assembled code.
It won't work with frameless procs, sorry. There is no way to do certain calculations:
My editor has about 200 procs, counted as "endp", full word search. About 130 have now instead of
ret
xxx endp
@MbRet
xxx endp
It's a
simple Find & Replace action. What @MbRet does is:
- go back to the start of the proc
- check if there is a stack frame
- count the number of LOCALs allocated over the stack
- go back to the RET position
- count the number of POPs (mostly pop ebx, edi, esi from xxx proc
uses esi edi ebx)
- calculated the expected
esp- shout foul if it doesn't coincide with the factual
esp- correct the stack if necessary, so that the
uses work correctly
- do a quick check if the return address is in the .code section of your program
- print a line to the screen if it isn't (the program will crash but
after that line)
- last but not least: insert a
retIn the testing phase, the program runs a tiny bit slower because of all those checks, and it adds some bloat. Once @MbRet stops printing error messages, you simply switch it off, and then all it does is insert the
ret.
I have discovered three errors so far in the 200 procs and 20k lines of code. That is not much, but it was three bugs too many :cool:
P.S.: No MasmBasic needed :mrgreen:
P.P.S: Inspired by this thread, I've implemented a new function: find push
or pop :cool:
my point was to use registers instead of stack similar to 64bit mode does,Hutch probably has better example of 64bit using 4 general purpose registers and XMM0-XMM3 for floats
this thread inspired me to test conditional assembly together with MACRO version of PROC's
but I cant get it to work properly,when I want it to assemble
using one MACRO instead of PROC+
YESPROC=1 ;if assemble proc's or not
IFDEF (YESPROC)
...code
ENDIF
all unused PROC in .inc file is not assembled and results in more than -1k smaller exe
Zip it and post it, daydreamer. We are curious to see code - complete code.
magnus,
In win32 you can do a simulated version of FASTCALL but you only have 3 registers you can use, EAX ECX & EDX. As win32 does not normally use the 3 available registers and only works with stack memory with STDCALL, you can use the 3 registers and put any others on the stack with the normal PUSH notation. Whether you will see any difference is another matter but it can be done.
Hutch,from Intel optimize manual,its alternative save/restore registers to SIMD registers and dont messup stack,but it takes opcode takes 3bytes instead of 1byte for push/pop
maybe good outside innerloop,but when unrolling inside innerloop maybe too many 3byte MOVD makes it too big to fit into cacheline
You attached code, daydreamer :thumbsup:
Can you explain where exactly you are testing your ideas?
Quote from: jj2007 on March 15, 2020, 02:52:25 AM
You attached code, daydreamer :thumbsup:
Can you explain where exactly you are testing your ideas?
MACRO versions only take place when used in code,PROC versions only need to be assembled if you want to use them instead of MACRO version,calling from several places,if use MACRO's instead IFDEF is used to avoid twice size of code =MACRO version+PROC version
also IFDEF can be used to reduce size not assemble PROC's that are never called
No, I meant examples from the source you posted. Where do you test your ideas, and how?
Quote from: jj2007 on March 05, 2020, 01:40:24 PM
In the meantime, I found at least a method to recognise that a proc has no stack frame.
The idea is very simple: in a large source, replace every ret that is followed by somecode endp with @MbRet. It takes an optional argument that should be the name of the proc; if there is no argument, the source line will be shown instead (as in the CodeNoArgs example below).
If the source has stack problems, they will be ## flagged to the console ##, even if the exe was built as a Windows application.
There are basically three cases:
1. too many push instructions: @MbRet will tell you about it, and correct the stack so that at least the uses esi edi ebx works properly.
2. too many pop instructions: @MbRet will tell you about it, and correct the stack but one or more of the regs in the uses esi edi ebx list will be trashed anyway.
3. stack corruption: the return address is somewhere in nirwana land. @MbRet will inform you about it, but the program will crash anyway.
I've tested it with a 20k lines source, and so far I've found one bug with it, see first post above. @MbRet tests runtime behaviour, so its messages will pop up only when you are really using a proc. So for rarely used exotic functions it may take a while until you discover the bug.
Once you have verified that there are no problems, put @MbRet off below the include line, so that only an ordinary ret will be generated.
Here is a little demo with some procs that treat the stack badly (tested ok with WinXP, Win7-64 and Win10):
include \masm32\include\masm32rt.inc ; purest Masm32 SDK code
include MbRet.inc ; must be in the exe's folder
; @MbRet off ; switch it off once tests are OK
.code
TwoArgsNoLocals proc uses esi edi ebx mode, crashArg
mov ebx, mode ; no Locals, but has args, and therefore a stack frame
print "2 args: ebx="
print str$(ebx), 13, 10
.while sdword ptr ebx>=0
push eax ; stack imbalances will be flagged
dec ebx
.Endw
pop eax ; stack is balanced
pop ecx ; for mode=3
pop edx
.if crashArg==99
mov dword ptr [ebp+4], 12345678h ; bad news for the return address ;-)
.endif
@MbRet 2ArgsNoLocals ; instead of the ret
TwoArgsNoLocals endp
ThreeArgsLocal123 proc uses ecx mode, arg2, arg3
Local buffer[123]:BYTE
push eax ; stack not balanced, will be flagged
@MbRet ThreeArgsLocal123
ThreeArgsLocal123 endp
CodeNoArgs proc uses esi edi ebx ecx ebp
print "CodeNoArgs - cannot work with @MbRet: "
; push eax ; activate to produce a crash
@MbRet ; no label -> line number 32; @MbRet will say "no frame found"
CodeNoArgs endp
start:
cls
m2m esi, 3
mov ebx, 12345678h
.Repeat
invoke TwoArgsNoLocals, esi, 123
print hex$(ebx), ": ebx after the invoke", 13, 10, 10
dec esi
.Until Zero?
invoke CodeNoArgs
invoke ThreeArgsLocal123, 123, 456, 789
inkey chr$(10, "hit any key for the stack corruption test:", 13, 10)
invoke TwoArgsNoLocals, 2, 99
MsgBox 0, "You won't see this box", "Hi", MB_OK
invoke ExitProcess, 0
end start
The procs have little problems with a sloppy use of the push and pop instructions. Here is the output:
2 args: ebx=3
### MbRet 2ArgsNoLocals: stack is -4 bytes off, 1 push(es) too many ###
12345678: ebx after the invoke
2 args: ebx=2
12345678: ebx after the invoke
2 args: ebx=1
### MbRet 2ArgsNoLocals: stack is 4 bytes off, 1 pop(s) too many - check uses ###
0040107A: ebx after the invoke
CodeNoArgs - cannot work with @MbRet: ### MbRet 31: no stack frame found ###
### MbRet ThreeArgsLocal123: stack is -4 bytes off, 1 push(es) too many ###
hit any key for the stack corruption test:
2 args: ebx=2
### stack corruption? 2ArgsNoLocals: retaddr 12345678h is above the code segment ###
After the stack corruption test, it will produce an exception.
Hi jj2007,
this code does not work for me ..
0040104F | C2 0800 | RET 8 |
0019FF64 0019FF80
0019FF80 | DCFF | FDIV ST(7), ST(0) |
0019FF82 | 1900 | SBB DWORD PTR DS:[EAX], EAX |
0019FF84 | 74 7B | JE 1A0001 |
EAX : 00000002
EBX : 00000002
ECX : 00000002
EDX : 00000002
EBP : 00000003
ESP : 0019FF70
ESI : 00401076 <mbret4masm32.EntryPoint>
EDI : 12345678
EIP : 0019FF80
EFLAGS : 00000206 L'Ȇ'
ZF : 0
OF : 0 L'Ȁ'
CF : 0 L'Ā'
PF : 1
SF : 0
TF : 0 L'Ā'
AF : 0
DF : 0
IF : 1
LastError : 000036B7 (ERROR_SXS_KEY_NOT_FOUND)
LastStatus : C0150008 (STATUS_SXS_KEY_NOT_FOUND)
GS : 002B
ES : 002B
CS : 0023
FS : 0053
DS : 002B
SS : 002B '+'
x87r0 : 00000000000000000000
x87r1 : 00000000000000000000
x87r2 : 00000000000000000000
x87r3 : 00000000000000000000
x87r4 : 00000000000000000000
x87r5 : 00000000000000000000
x87r6 : 00000000000000000000
x87r7 : 00000000000000000000
x87TagWord : FFFF
x87ControlWord : 027F L'ɿ'
x87StatusWord : 0000
x87TW_0 : 3 (Empty)
x87TW_1 : 3 (Empty)
x87TW_2 : 3 (Empty)
x87TW_3 : 3 (Empty)
x87TW_4 : 3 (Empty)
x87TW_5 : 3 (Empty)
x87TW_6 : 3 (Empty)
x87TW_7 : 3 (Empty)
x87SW_B : 0
x87SW_C3 : 0
x87SW_TOP : 0 (ST0=x87r0)
x87SW_C2 : 0
x87SW_C1 : 0
x87SW_O : 0
x87SW_ES : 0
x87SW_SF : 0
x87SW_P : 0
x87SW_U : 0
x87SW_Z : 0
x87SW_D : 0
x87SW_I : 0
x87SW_C0 : 0
x87CW_IC : 0
x87CW_RC : 0 (Round Near)
x87CW_PC : 2 (Real8)
x87CW_PM : 1
x87CW_UM : 1
x87CW_OM : 1
x87CW_ZM : 1
x87CW_DM : 1 L'ā'
x87CW_IM : 1
MxCsr : 00001F80
MxCsr_FZ : 0
MxCsr_PM : 1
MxCsr_UM : 1
MxCsr_OM : 1
MxCsr_ZM : 1
MxCsr_IM : 1 L'ā'
MxCsr_DM : 1
MxCsr_DAZ : 0
MxCsr_PE : 0
MxCsr_UE : 0
MxCsr_OE : 0
MxCsr_ZE : 0
MxCsr_DE : 0
MxCsr_IE : 0
MxCsr_RC : 0 (Round Near)
MM0 : 0000000000000000
MM1 : 0000000000000000
MM2 : 0000000000000000
MM3 : 0000000000000000
MM4 : 0000000000000000
MM5 : 0000000000000000
MM6 : 0000000000000000
MM7 : 0000000000000000
XMM0 : 00000000000000000000000000000000
XMM1 : 00000000000000000000000000000000
XMM2 : 00000000000000000000000000000000
XMM3 : 00000000000000000000000000000000
XMM4 : 00000000000000000000000000000000
XMM5 : 00000000000000000000000000000000
XMM6 : 00000000000000000000000000000000
XMM7 : 00000000000000000000000000000000
YMM0 : 0000000000000000000000000000000000000000000000000000000000000000
YMM1 : 0000000000000000000000000000000000000000000000000000000000000000
YMM2 : 0000000000000000000000000000000000000000000000000000000000000000
YMM3 : 0000000000000000000000000000000000000000000000000000000000000000
YMM4 : 0000000000000000000000000000000000000000000000000000000000000000
YMM5 : 0000000000000000000000000000000000000000000000000000000000000000
YMM6 : 0000000000000000000000000000000000000000000000000000000000000000
YMM7 : 0000000000000000000000000000000000000000000000000000000000000000
DR0 : 00000000
DR1 : 00000000
DR2 : 00000000
DR3 : 00000000
DR6 : 00000000
DR7 : 00000000
INT3 breakpoint at mbret4masm32.0040104F (0040104F)!
EXCEPTION_DEBUG_INFO:
dwFirstChance: 1
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: 0019FF82
NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 00000002 Inaccessible Address
First chance exception on 0019FF82 (C0000005, EXCEPTION_ACCESS_VIOLATION)!
Sorry, LiaoMi, I don't understand: what is not working? It doesn't assemble, or it crashes? Could you post a complete source please, so that I can check if there is a problem in my routine? Thanks :thup:
Quote from: jj2007 on March 17, 2020, 01:17:34 AM
Sorry, LiaoMi, I don't understand: what is not working? It doesn't assemble, or it crashes? Could you post a complete source please, so that I can check if there is a problem in my routine? Thanks :thup:
Your finished example does not display messages like yours, instead, the program prints the first line and exits :rolleyes:, after debugging, I showed the place where the crash occurs.
2 args: ebx=3 <-
Exit### MbRet 2ArgsNoLocals: stack is -4 bytes off, 1 push(es) too many ###
12345678: ebx after the invoke
2 args: ebx=2
12345678: ebx after the invoke
2 args: ebx=1
### MbRet 2ArgsNoLocals: stack is 4 bytes off, 1 pop(s) too many - check uses ###
0040107A: ebx after the invoke
CodeNoArgs - cannot work with @MbRet: ### MbRet 31: no stack frame found ###
### MbRet ThreeArgsLocal123: stack is -4 bytes off, 1 push(es) too many ###
hit any key for the stack corruption test:
2 args: ebx=2
### stack corruption? 2ArgsNoLocals: retaddr 12345678h is above the code segment ###
Quote from: jj2007 on March 06, 2020, 04:29:43 AM
What it cannot do so easily is find the start of the proc.
How to trace the call stack under x64 Windows? - https://stackoverflow.com/questions/18889376/how-to-trace-the-call-stack-under-x64-windows (https://stackoverflow.com/questions/18889376/how-to-trace-the-call-stack-under-x64-windows)
CaptureStackBackTrace function - https://docs.microsoft.com/en-us/windows/win32/debug/capturestackbacktrace (https://docs.microsoft.com/en-us/windows/win32/debug/capturestackbacktrace)
How to Log Stack Frames with Windows x64 - https://stackoverflow.com/questions/590160/how-to-log-stack-frames-with-windows-x64 (https://stackoverflow.com/questions/590160/how-to-log-stack-frames-with-windows-x64)
Fast capture stack trace on windows / 64-bit / mixed mode - https://exceptionshub.com/fast-capture-stack-trace-on-windows-64-bit-mixed-mode.html (https://exceptionshub.com/fast-capture-stack-trace-on-windows-64-bit-mixed-mode.html)
A Mixed-Mode Stackwalk with the IDebugClient Interface - https://www.codeproject.com/Articles/371137/A-Mixed-Mode-Stackwalk-with-the-IDebugClient-Inter (https://www.codeproject.com/Articles/371137/A-Mixed-Mode-Stackwalk-with-the-IDebugClient-Inter)
Walking the callstack - https://www.codeproject.com/Articles/11132/Walking-the-callstack-2 (https://www.codeproject.com/Articles/11132/Walking-the-callstack-2)
How To Get a Stack Trace on Windows - http://www.rioki.org/2017/01/09/windows_stacktrace.html (http://www.rioki.org/2017/01/09/windows_stacktrace.html)
(http://www.rioki.org/media/2017-01-09_stacktrace.png)
StackWalk function - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-stackwalk?redirectedfrom=MSDN (https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-stackwalk?redirectedfrom=MSDN)
StackWatcher - https://www.ivanlef0u.tuxfamily.org/?p=6 (https://www.ivanlef0u.tuxfamily.org/?p=6)