The MASM Forum

General => The Campus => Topic started by: buraks on January 26, 2020, 11:00:47 AM

Title: About the stack
Post by: buraks on January 26, 2020, 11:00:47 AM
Hello,

There is a caller in an exe:


   push par1
   push par2
   call myFunction


And myFunction is in a DLL:


myFunction proc param1:dword, param2:dword
   invoke MessageBox........

   pop ebp
   mov eax, 1
   retn
myFunction endp


why do we need the last 3 lines in myFunction instead of just "ret"?
That is:


myFunction proc param1:dword, param2:dword
   invoke MessageBox.........

   ret
myFunction endp


Title: Re: About the stack
Post by: jj2007 on January 26, 2020, 02:56:05 PM
You don't need them, a simple "ret" would do the job:

include \masm32\include\masm32rt.inc ; plain Masm32 for the fans of pure assembler

.code
myFunction proc param1:dword, param2:dword
   invoke MessageBox, 0, param1, param2, MB_OK

   ret ; comment out to see the difference

   pop ebp
   mov eax, 1
   retn 2*DWORD
myFunction endp

start:
print str$(esp), " stack", 13, 10
push chr$("Title")
push chr$("Text")
call myFunction
inkey str$(esp), " stack", 13, 10
exit

end start


"ret" is a built-in macro, "retn" is an instruction; to understand what really happens, use a debugger like Olly (http://www.ollydbg.de/version2.html)
Title: Re: About the stack
Post by: aw27 on January 26, 2020, 06:19:51 PM
AFAIK, RETN means return near and is obsolete because in the flat model all calls are near (or far, you decide).
The Intel manuals from this century do not even mention RETN, they mention RET and it has the opcode C3.

What MASM does is a trick, when it sees RET, it fills up the Epilogue for you.

You can prevent MASM from being a smart ass by prepend this before the procedure(s):

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

(https://www.dropbox.com/s/1twpa49pqxgupw6/retfunction.png?dl=1)

Title: Re: About the stack
Post by: jj2007 on January 26, 2020, 08:23:39 PM
Here is a more detailed version showing what happens under the hood:
include \masm32\include\masm32rt.inc ; plain Masm32 for the fans of pure assembler

.code
myFunction1 proc param1:dword, param2:dword
   print param1
   print str$(param2), 13, 10

   nops 2
   ret ; the ret macro
myFunction1 endp
  nop

myFunction2 proc param1:dword, param2:dword
   print param1
   print str$(param2), 13, 10

   nops 2
   pop ebp ; the version that UAsm will use for the ret macro
   mov eax, 1
   retn 2*DWORD
myFunction2 endp
  nop

myFunction3 proc param1:dword, param2:dword
   print param1
   print str$(param2), 13, 10

   nops 2
   leave ; the version that MASM will use for the ret macro
   retn 2*DWORD
myFunction3 endp
  nop

start:
  print str$(esp), " stack", 13, 10
  int 3
  invoke myFunction1, chr$("calling "), 1
  invoke myFunction2, chr$("calling "), 2
  invoke myFunction3, chr$("calling "), 3
  inkey str$(esp), " stack", 13, 10
  exit

end start


The nops serve to make the disassembly look clearer. "print" is a Masm32 macro that uses Stdout:
Command                             Comments
push ebp                            ; NewFile.myFunction1(guessed Arg1,Arg2)
mov ebp, esp
push dword ptr [ebp+8]              ; ÚArg1 => [Arg1]
call StdOut                         ; ÀNewFile.StdOut
push offset ??001F                  ; ÚArg2 = NewFile.??001F
push dword ptr [ebp+0C]             ; ³Arg1 => [Arg2]
call dwtoa                          ; ÀNewFile.dwtoa
push offset ??001F                  ; ÚArg1 = NewFile.??001F
call StdOut                         ; ÀNewFile.StdOut
push offset ??0026                  ; ÚArg1 = ASCII CR,LF
call StdOut                         ; ÀNewFile.StdOut
nop
nop
pop ebp                             ; voilà, this is
retn 8                              ; the ret MACRO!
nop                                 ;
push ebp                            ; NewFile.myFunction2(guessed Arg1,Arg2)
mov ebp, esp
push dword ptr [ebp+8]              ; ÚArg1 => [ARG.1]
call StdOut                         ; ÀNewFile.StdOut
push offset ??002D                  ; ÚArg2 = NewFile.??002D
push dword ptr [ebp+0C]             ; ³Arg1 => [ARG.2]
call dwtoa                          ; ÀNewFile.dwtoa
push offset ??002D                  ; ÚArg1 = NewFile.??002D
call StdOut                         ; ÀNewFile.StdOut
push offset ??0034                  ; ÚArg1 = ASCII CR,LF
call StdOut                         ; ÀNewFile.StdOut
nop
nop
pop ebp                             ; looks like the
mov eax, 1                          ; ret MACRO, but you
retn 8                              ; added a return value
nop
push ebp                            ; NewFile.myFunction3(guessed Arg1,Arg2)
mov ebp, esp
push dword ptr [ebp+8]              ; ÚArg1 => [ARG.1]
call StdOut                         ; ÀNewFile.StdOut
push offset ??003B                  ; ÚArg2 = NewFile.??003B
push dword ptr [ebp+0C]             ; ³Arg1 => [ARG.2]
call dwtoa                          ; ÀNewFile.dwtoa
push offset ??003B                  ; ÚArg1 = NewFile.??003B
call StdOut                         ; ÀNewFile.StdOut
push offset ??0042                  ; ÚArg1 = ASCII CR,LF
call StdOut                         ; ÀNewFile.StdOut
nop
nop
leave                               ; the shortest version
retn 8
nop


Normally, procedures ("functions") first create a stack frame using
push ebp                            ; NewFile.myFunction2(guessed Arg1,Arg2)
mov ebp, esp

... then do their job, and after that quit using either...
pop ebp
retn DWORD*(number of args)

... or ...
leave
retn DWORD*(number of args)

It gets slightly more complicated with local variables:
myFunction1 proc arg1, arg2
Local v1, v2, v3

  Command                             Comments
  push ebp                            ; NewFile.myFunction1(guessed Arg1,Arg2)
  mov ebp, esp
  add esp, -0C                        ; create space for 3 local DWORDs
...
  mov esp, ebp                        ; the ret MACRO again...
  pop ebp
  retn 8


This would be an equivalent but shorter return:
  leave
  retn 8


As you can when comparing the screenshot from the Intel manual to the disassembly, it is the C2 0800 encoding, i.e. near return and pop imm16.

Debuggers like Olly use retn to distinguish the instruction from the ret macro used by MASM and compatible assemblers like UAsm and AsmC.

Likewise, with MASM & friends you can use retn to override the macro creation as in your code:

   pop ebp
   mov eax, 1
   retn

So ret means "insert the macro, e.g. leave plus retn 8", retn means "use only the ret instruction"
Title: Re: About the stack
Post by: aw27 on January 26, 2020, 09:21:43 PM
Holly crap  :badgrin:, Intel says RET is an instruction but some people insist it is a macro.

Yeah, everything is a macro and we are all Charlie.  :icon_idea:

(https://www.dropbox.com/s/5opjpmta7pmosk1/retfunction2.png?dl=1)

Title: Re: About the stack
Post by: hutch-- on January 26, 2020, 09:30:44 PM
 :biggrin:

If in doubt, trust the opcode.  :tongue:
Title: Re: About the stack
Post by: jj2007 on January 26, 2020, 09:59:29 PM
Quote from: AW on January 26, 2020, 09:21:43 PM
Holly crap  :badgrin:, Intel says RET is an instruction but some people insist it is a macro.

The Microsoft Macro Assembler (MASM) inserts one, two, three or more instructions when it sees ret. Same for UAsm and AsmC. Commonly such behaviour is expected from a macro :cool:

In the words of an assembler developer:
Quote from: johnsa on October 16, 2017, 08:45:12 PMRET is what triggers epilogue generation

I am no expert for other assemblers (this is a MASM forum, including the UAsm and AsmC clones), but it seems that FASM does the same (answer by vid (https://board.flatassembler.net/topic.php?p=43515)):
Quotethere is no "ret" isntruction, i believe you mean "retn". "ret" is pseudo-opcode (macro in FASM) to return

It seems even more confusing in FASM (https://board.flatassembler.net/topic.php?p=89384):
QuoteHave you spelled RET as "ret" (i.e. all lowercase)? It is important because the proc macro defines the ret macro but not RET nor any other combination of lower and upper case.

Google "nasm" "ret macro" to see how NASM behaves (messy as well, apparently RET is the macro, ret is the instruction; FASM does it the other way round :rolleyes:).

Another disassembly of a simple MASM ret - quitting from somealgo proc uses esi edi ebx arg1, arg2, arg3:
pop ebx
pop edi
pop esi
mov esp, ebp
pop ebp
retn 0C


The conclusion:
- use ret (the macro) to let the assembler (MASM, UAsm, AsmC) decide which instructions are needed to quit the proc
- use retn (the instruction) if you want to do it manually, and you know what you are doing.
Title: Re: About the stack
Post by: aw27 on January 26, 2020, 10:39:14 PM
The only conclusions are:
1) RETN is not an Intel instruction. It is a clue for some  debuggers and assemblers to insert opcode C3 straight away.
2) RET does not trigger anything, it is an Intel Instruction. Assemblers watch it to insert the Epilogue, in the same sense that you watch the Traffic Lights to move on when they turn green.

Anyway, I think you got the idea by now.
Title: Re: About the stack
Post by: felipe on January 27, 2020, 05:22:05 AM
I found this a little weird. I have always used "ret" and i have review some code that i wrote in 32 and 64 bits, that uses it, with dissasemblers and debuggers just now and i didn't see any extra line added to my code, even if i don't used "option epilogue:none". So any idea on this? I have always used masm (masm, ml, ml64).  :icon_idea:
Title: Re: About the stack
Post by: daydreamer on January 27, 2020, 05:46:04 AM
if you use qeditor,there is lots of old windows help,in help meny,from the simplest macro usage to the advanced macro making,even mnemonic/opcodes dated for older cpus,but mostly still in use
Title: Re: About the stack
Post by: jj2007 on January 27, 2020, 06:17:07 AM
Quote from: felipe on January 27, 2020, 05:22:05 AMi didn't see any extra line added to my code

Post an example, Felipe.
Title: Re: About the stack
Post by: felipe on January 27, 2020, 06:58:03 AM
As an example you can check this lousy program (just to see the "ret" in action in some procedures in the program, ignore the errors mentioned in the post, i mean that's another topic  :rolleyes:):
http://masm32.com/board/index.php?topic=6235.0 (http://masm32.com/board/index.php?topic=6235.0)

Checking the code in the post, with the dissasembly with ida or using a debugger like x32dbg will show you the same code pertaining to the "ret" instruction.  :icon_idea:

I don't want to put images of that here, you can try if you want and it's possible to you.

I have olly too, but i have not tried with it yet to see how this "rets" show up. I will do it soon as i can.  :thup:
Title: Re: About the stack
Post by: avcaballero on January 27, 2020, 07:34:25 AM
I have not done the test. Maybe all of this go from here. If I'm not wrong, you can define a macro with the name of an instruction that replaces it. In the case of "RET" this can be useful to exit from procedures using "PROC", for example to exit with arguments, etc. Perhaps the clean "RET" can be in a procedure of the type

myproc:
...
ret

Title: Re: About the stack
Post by: felipe on January 27, 2020, 08:05:16 AM
I have done the same with olly and nothing is changed. As you can see in the link i posted above, as example this piece of code:


send_messag1    proc                                                    ; Makes the first message box.
                push    MB_OKCANCEL
                lea     ebx,titl
                push    ebx
                lea     ebx,content1
                push    ebx
                push    0
                call    MessageBox
                mov     value_retorn,eax                                ; We store here the button pushed.
                ret
send_messag1    endp


Looks in x32dbg as this:
Quote
00401029 | 6A 01                         | push 1                                                  |
0040102B | 8D 1D 00 30 40 00             | lea ebx,dword ptr ds:[403000]                           | 00403000:"System message"
00401031 | 53                            | push ebx                                                |
00401032 | 8D 1D 0F 30 40 00             | lea ebx,dword ptr ds:[40300F]                           | 0040300F:"Log out now?"
00401038 | 53                            | push ebx                                                |
00401039 | 6A 00                         | push 0                                                  |
0040103B | E8 5E 00 00 00                | call <logout.MessageBoxA>                               |
00401040 | A3 6C 30 40 00                | mov dword ptr ds:[40306C],eax                           |
00401045 | C3                            | ret                                                     |

And as i said, with ida and olly i see the same, there are no lines aggregated to the "original" code. So any ideas?  :icon_idea:
Title: Re: About the stack
Post by: felipe on January 27, 2020, 08:10:23 AM
Oh i think i got it now, probably this "masm" behavior (don't care about other assemblers for now) of adding code when you use "ret" only happens when you use the "proc" directive, passing parameters with it, like in this code:


myFunction1 proc param1:dword, param2:dword


Correct me if i'm wrong or ignore me if i'm right... :tongue:
Title: Re: About the stack
Post by: avcaballero on January 27, 2020, 08:32:19 AM
Yes, that's right. You cannot exit from a procedure that has local variables or paramethers in the same way from other that has nothing of this. Though you only use "ret" for every case.

Title: Re: About the stack
Post by: jj2007 on January 27, 2020, 09:47:21 AM
Quote from: felipe on January 27, 2020, 08:10:23 AMOh i think i got it now, probably this "masm" behavior (don't care about other assemblers for now) of adding code when you use "ret" only happens when you use the "proc" directive, passing parameters with it

Yes, that's correct. For a simple somestuff proc without arguments and without local variables, the assembler uses retn and nothing else. There is no need for a stack frame in such a proc.
Title: Re: About the stack
Post by: felipe on January 27, 2020, 11:37:02 AM
Thanks for confirming this, i really appreciate it.  :thumbsup:
Title: Re: About the stack
Post by: hutch-- on January 27, 2020, 12:10:06 PM
The action with the stack in 32 bit is to ensure that has the same value on exit as it has before the proc was called. This is called "balancing the stack". One of the easiest ways to test this is and display code that can output "str$(esp)". If the displayed value is the same both before and after the proc that is called, then the stack is balanced, if not you have something to fix with the code.
Title: Re: About the stack
Post by: jj2007 on January 27, 2020, 05:13:48 PM
Related: Another good method is to use a global variable, as demonstrated below:
include \masm32\include\masm32rt.inc

.data?
globalEsp dd ?

.code
TheAlgo proc uses esi edi ebx _text, _title
Local tLen
  mov globalEsp, esp
  mov esi, _text
  mov edi, _title
  mov tLen, len(esi)
  add tLen, len(edi)
  push eax ; push is a very useful instruction, but there should be a corresponding pop
  print str$(tLen), " = len of text plus len of title", 13, 10
  pushw 123 ; oops, this pushes a word - serious trouble
  cmp globalEsp, esp
  .if !Zero?
mov eax, globalEsp
sub eax, esp
mov esp, globalEsp
push eax
sar eax, 2
.if !Sign?
print str$(eax), " = number of push instructions in excess", 13, 10
.else
print str$(eax), " = number of pop instructions in excess", 13, 10
.endif
pop eax
invoke MessageBox, 0, str$(eax), chr$("Stack is some bytes off:"), MB_OK
  .endif
  ret
TheAlgo endp
codesize TheAlgo

start:
  invoke TheAlgo, chr$("This is the text"), chr$("The title:")
  inkey "hit any key"
  exit

end start


Attached the De Luxe version - by activating the useCsb=0 line, no extra code will be generated by the CheckStackBalance macro:
include \masm32\include\masm32rt.inc
include CheckStackBalance.inc ; the macro
; useCsb=0 ; uncomment this line to see the effect

.code
TheAlgo proc uses esi edi ebx _text, _title
Local tLen
  CheckStackBalance ; after the locals
  mov esi, _text
  mov edi, _title
  mov tLen, len(esi)
  add tLen, len(edi)
  push eax ; push is a very useful instruction, but there should be a corresponding pop
  print str$(tLen), " = len of text plus len of title", 13, 10
  pushw 123 ; oops, this pushes a word - serious trouble!
  CheckStackBalance ; before the ret
  ret
TheAlgo endp
codesize TheAlgo ; ** TheAlgo is 207 bytes long **

start:
  mov esi, 123456789
  invoke TheAlgo, chr$("This is the text"), chr$("The title:")
  inkey str$(esi), " is the value of esi"
  exit

end start


Note that for useCsb=0 the code does not crash, and you will not see any warning - stack frames are a wonderful feature! But check the "value of esi" line :cool:

P.S.: Just for fun, I've added a codesize macro - to be used after the endp. Watch your output window when you build the code either with useCsb=0 or useCsb=1 (default). No extra code generated :thumbsup:
Title: Re: About the stack
Post by: jj2007 on February 01, 2020, 03:54:51 PM
The CheckStackBalance macro is now available, see this post (http://masm32.com/board/index.php?topic=6483.msg91146#msg91146)
Title: Re: About the stack
Post by: daydreamer on February 01, 2020, 07:58:08 PM
JJ,wasnt you who had cool SSE push/pop macro?
Title: Re: About the stack
Post by: jj2007 on February 01, 2020, 09:28:25 PM
Quote from: daydreamer on February 01, 2020, 07:58:08 PM
JJ,wasnt you who had cool SSE push/pop macro?

What do you mean? I have dedicated macros to keep XMM0...XMM3 safe, but not for public use.
fxsave and fxrstor are your friends - roll your own ;-)