The MASM Forum

General => The Campus => Topic started by: Lonewolff on May 01, 2018, 11:01:40 AM

Title: PUSH & POP question
Post by: Lonewolff on May 01, 2018, 11:01:40 AM
Hi guys,

Do you have to 'pop' every time you 'push' something on the stack?

Do 'call' and 'invoke' automatically do this for us?

Thanks in advance  :biggrin:
Title: Re: PUSH & POP question
Post by: hutch-- on May 01, 2018, 11:16:15 AM
Except in rare instances, yes. The stack for PUSH / POP work on a last on, first off basis so the POPs must be in reverse order to the PUSHs.

When a CALL is made, you need to have pushed the arguments onto the stack first. When you use the built in MACRO "invoke" it automates the push / call process. Note that any procedure you call that you wrote yourself must balance the stack on exit. This is done with the number after the RET (ret 16) and it must match the number of arguments x 4.
Title: Re: PUSH & POP question
Post by: jj2007 on May 01, 2018, 11:16:42 AM
YES and YES.

Quote from: hutch-- on May 01, 2018, 11:16:15 AMNote that any procedure you call that you wrote yourself must balance the stack on exit.
"Balanced" means normally that #push = #pop, but there are situations where you may want to use push push call instead of invoke, see screenshot below. I have recently started to use uppercase PUSH for these cases, so that searching for push+pop yields lowercase #push = #pop if the stack is balanced.

Note also that a program does not necessarily crash if the stack is not balanced; most procs have a stack frame, and if that gets released at the end, the stack is balanced. Great, but you have a bug...
Title: Re: PUSH & POP question
Post by: Lonewolff on May 01, 2018, 11:19:17 AM
Thanks guys.

So this would need four more pop's before pop'ping ESI?


; Display 'MessageBox'
push esi ; preserve esi

mov eax, 10h ; Error icon
push eax

lea esi, _EE ; Error title
mov ecx, esi
push ecx

lea esi, _E0 ; Error reason
mov ecx, esi
push ecx

xor eax, eax ; Window handle
push eax
call MessageBoxA

pop esi ; restore esi


Doing it all long handed for learning purposes (ie. not using 'offset' or 'invoke').

(Or a ret 16 if it was in a proc?)
Title: Re: PUSH & POP question
Post by: hutch-- on May 01, 2018, 11:26:15 AM
For the 1st and 4th argument, you can directly do a "push 0" in MASM.
Title: Re: PUSH & POP question
Post by: jj2007 on May 01, 2018, 11:26:28 AM
Quote from: Ascended on May 01, 2018, 11:19:17 AM
So this would need four more pop's before pop'ping ESI?
Nope. You used 4*push instead of invoke. Your stack is balanced.

If you want to dig deeper, here is a snippet (see above, "a program does not necessarily crash if the stack is not balanced"):include \masm32\include\masm32rt.inc ; console build!

.code
SayHi proc uses esi edi ebx pMessage
Local hWnd ; force a stack frame...
  and hWnd, 0 ; set window handle to zero
  push MB_OK
  push chr$("Hi")
  push pMessage ; chr$("Hello World")
  push hWnd
  call MessageBox ; invoke MessageBox, 0, str$(eax), chr$("Title"), MB_OK
  pop edx ; a desperate attempt to balance the stack
  pop edx
  pop edx
  pop edx
  ; int 3 ; uncomment to see what happens here
  ret
SayHi endp
 
start:
  mov ebx, 123
  mov esi, 456
  mov edi, 789
  print str$(ebx), 9, "ebx before", 13, 10
  print str$(esi), 9, "esi", 13, 10
  print str$(edi), 9, "edi", 13, 10
  invoke SayHi, chr$("Hello World")
  print str$(ebx), 9, "ebx after", 13, 10
  print str$(esi), 9, "esi", 13, 10
  inkey str$(edi), 9, "edi", 13, 10
  exit

end start
Title: Re: PUSH & POP question
Post by: Lonewolff on May 01, 2018, 12:11:43 PM
Thanks guys. Got it  :t

And Hutch, the fourth arg is a 10H, but I get what you are saying.

Thanks again  8)
Title: Re: PUSH & POP question
Post by: Lonewolff on May 01, 2018, 12:15:41 PM
Also, was I right in assuming that this is the equivalent of 'offset'?


push esi
lea esi, xxx
mov ecx, esi
pop esi


[edit]
Actually OllyDbg says that it is simply 'push offset address'

I though 'offset' must have been another macro, but it is an actual keyword. There ya go  :biggrin:
Title: Re: PUSH & POP question
Post by: jj2007 on May 01, 2018, 12:33:46 PM
lea esi, somevar is the same as mov esi, offset somevar if somevar is a global variable.

This is unnecessarily complicated:
lea esi, _EE ; Error title
mov ecx, esi
push ecx

lea esi, _E0 ; Error reason
mov ecx, esi
push ecx


Better:
mov esi, offset _EE ; Error title
push esi

mov esi, offset _E0 ; Error reason
push esi


Even better (shorter, and doesn't need esi):
push offset _EE ; Error title
push offset _E0 ; Error reason


Even better (for readability):
push chr$("There was an error:")
push chr$("But I don't know the reason")


And once you have understood how it works under the hood. you can become professional and lazy:
MsgBox 0, "But I don't know the reason", "There was an error:", MB_YESNOCANCEL or MB_ICONWHATEVER
Title: Re: PUSH & POP question
Post by: hutch-- on May 01, 2018, 12:51:06 PM
Its unfortunate that ML64 has gone the way of NASM in not using OFFSET which is a specific technical term for a distance from a location which is used to locate either data or code within an assembler binary. In 32 bit MASM (ML.EXE) it is the preferred technique for addresses. LEA will do it but you are determining the address dynamically where OFFSET puts the value in at assembly time as an immediate.
Title: Re: PUSH & POP question
Post by: Lonewolff on May 01, 2018, 12:53:29 PM
Quote from: hutch-- on May 01, 2018, 12:51:06 PM
Its unfortunate that ML64 has gone the way of NASM in not using OFFSET which is a specific technical term for a distance from a location which is used to locate either data or code within an assembler binary. In 32 bit MASM (ML.EXE) it is the preferred technique for addresses. LEA will do it but you are determining the address dynamically where OFFSET puts the value in at assembly time as an immediate.

Oh, so in ML64 you have to use LEA then?
Title: Re: PUSH & POP question
Post by: hutch-- on May 01, 2018, 12:55:38 PM
In most instances, yes. I don't like it but that is how it works.
Title: Re: PUSH & POP question
Post by: Lonewolff on May 01, 2018, 12:57:09 PM
Bummer, good snippet to know though  :t
Title: Re: PUSH & POP question
Post by: jj2007 on May 01, 2018, 01:02:52 PM
Quote from: hutch-- on May 01, 2018, 12:51:06 PM
Its unfortunate that ML64 has gone the way of NASM in not using OFFSET

Are you sure?

  mov rsi, offset txMessage
  lea rdi, txMessage


0000000140001018   | 48 BE 02 10 00 40 01 00 00 00    | movabs rsi,140001002                    | 140001002:"Hello World"
0000000140001022   | 48 8D 3D D9 FF FF FF             | lea rdi,qword ptr ds:[140001002]        | 140001002:"Hello World"
Title: Re: PUSH & POP question
Post by: hutch-- on May 01, 2018, 06:18:54 PM
There are some places where you can use it and some places where it fails. I try for consistency and LEA is consistent across all of the uses I have found.
Title: Re: PUSH & POP question
Post by: sinsi on May 01, 2018, 07:20:32 PM
LEA uses a 32-bit signed offset from RIP, OFFSET uses the 64-bit address. Benefits of LEA are smaller code (see jj's example) and no relocation needed (helps with position-independent code).
Title: Re: PUSH & POP question
Post by: LordAdef on May 02, 2018, 07:56:09 AM
Every time I see you guys talking about masm 64 I get the shivers... It will take some time before I dig into 64 I guess

Why the hell did they make x64 with so many caveats?

Title: Re: PUSH & POP question
Post by: daydreamer on May 03, 2018, 05:40:05 AM
Quote from: hutch-- on May 01, 2018, 12:51:06 PM
Its unfortunate that ML64 has gone the way of NASM in not using OFFSET which is a specific technical term for a distance from a location which is used to locate either data or code within an assembler binary. In 32 bit MASM (ML.EXE) it is the preferred technique for addresses. LEA will do it but you are determining the address dynamically where OFFSET puts the value in at assembly time as an immediate.
Isnt there a way to make OFFSET macro in ml64 ?
Title: Re: PUSH & POP question
Post by: hutch-- on May 03, 2018, 02:13:09 PM
Answer is simple.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include64\masm64rt.inc

    .data
      testme dq 12345678

    .code

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

entry_point proc

    LOCAL var   :QWORD

    ; mov var, OFFSET testme  ; offset.asm(16) : error A2084:constant value too large

    lea rax, testme
    mov var, rax

    conout "Howdy, your new console template here.",lf,lf
    waitkey
    .exit

entry_point endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    end
Title: Re: PUSH & POP question
Post by: hutch-- on May 03, 2018, 02:17:02 PM
Alex,

I would not worry about Win64, if you saw the miseries of shifting from 16 bit segment:offset to Win32 flat memory model, this is no big deal to go to 64 bit. Its different but it does make sense.
Title: Re: PUSH & POP question
Post by: hutch-- on May 03, 2018, 02:28:27 PM
In 32 bit, OFFSET works fine.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    .data
      item dd 12345678

    .code

    start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    mov eax, OFFSET item

    print str$([eax]),13,10

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    end start
Title: Re: PUSH & POP question
Post by: jj2007 on May 03, 2018, 05:56:51 PM
Quote from: hutch-- on May 03, 2018, 02:28:27 PM
In 32 bit, OFFSET works fine.
...
    mov eax, OFFSET item

That works in 64-bit, too. Rarely useful but it works, even with ML64.
Title: Re: PUSH & POP question
Post by: hutch-- on May 03, 2018, 06:43:10 PM
The distinction is simple, ML64 treats OFFSET like a memory operand so while you can load it into a register you cannot load it into another memory operand. An OFFSET is an immediate, not a memory operand. LEA does the job but its 2 steps, not an immediate into either a memory operand or a register.