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:
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.
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...
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?)
For the 1st and 4th argument, you can directly do a "push 0" in MASM.
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
Thanks guys. Got it :t
And Hutch, the fourth arg is a 10H, but I get what you are saying.
Thanks again 8)
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:
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
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.
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?
In most instances, yes. I don't like it but that is how it works.
Bummer, good snippet to know though :t
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"
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.
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).
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?
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 ?
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
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.
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
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.
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.