News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Procedures

Started by Bieb, January 12, 2005, 01:39:58 PM

Previous topic - Next topic

Bieb

How exactly do I make a procedure that accepts arguments?  Do I have to have it take them off the stack by itself, or can the process be automated with MASM?  If I have to do it myself, how do I have to balance the stack frame?

donkey

Most procedures you use are STDCALL, that means the proc balances the stack. An automated stack frame will modify the RET opcode to include the number of bytes to remove from the stack,for example if you have 2 DWORD parameters it will change it to RET 8. So no, you do not have to worry about balancing the stack in STDCALL, it is handled in the stack frame. C calling convention requires that the caller balance the stack, in that case you are responsible but specific to MASM, it will also insert the necessary ADD ESP,xxx into your code so you need not worry about it either.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

#2
Just to explain a bit about stack frames,which seem to be a hot topic these days, this is the general way they are built...

CALL:
1. Push the parameters onto the stack
2. push the return address
3. jump to the function

PROC:
1. Save the value of EBP (stack base pointer usually PUSH it)
2. Save any registers in the USES directive
3. Move ESP into EBP (the top of the entry stack is now seen as the base)
4. SUB the number of local bytes from ESP (make room for the local data)

5. Execute the proc...

6. Move EBP into ESP (reset the top of the stack)
7. Restore the USES registers
8. Restore EBP (usually POP it)
9. Adjust RET to remove the parameters in bytes from the stack (ie RET 8)
10. POP the return address
11. Jump to the return address
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Bieb

Could I get a quick code example?

donkey

In MASM syntax...

MyProc PROTO :DWORD,:DWORD,:DWORD

invoke MyProc, 1,2,3

MyProc PROC uses EDI ESI EBX Param1:DWORD, Param2:DWORD,Param3:DWORD


RET
MyProc ENDP
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Bieb

And then I can just use Param1-Param3 like I would any other memory location?

donkey

Yup, they assemble as an offset from EBP but you can use them as normal memory labels, mov data in and out perform math etc... The only exception is that you cannot directly get the offset of the memory location as it is not known at compile time, you must use LEA to caclulate it like this...

lea eax, Param1
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Vortex

Hi Bied,

Here is an example demonstrating how to code procedures at low level:


    .386
    .model flat, stdcall
    option casemap :none

    include \masm32\include\windows.inc
    include \masm32\include\kernel32.inc

    public StdOut@4
    .code

StdOut@4:
    push ebp
    mov ebp,esp
    add esp,-12    ;3 dwords
;   LOCAL hOutPut  :DWORD
;   LOCAL bWritten :DWORD
;   LOCAL sl       :DWORD

    invoke GetStdHandle,STD_OUTPUT_HANDLE
    mov [ebp-4], eax ; hOutPut

    invoke lstrlen,[ebp+8] ; lpszText
    mov [ebp-12], eax

    lea eax,[ebp-8] ; bWritten
    invoke WriteFile,[ebp-4],[ebp+8],[ebp-12],eax,NULL ; [ebp-12] = sl
    mov eax,[ebp-8]
    leave
    ret 4

end


Main application:


.386
.model flat, stdcall
option casemap :none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

StdOut PROTO :DWORD

.data

Message db 'Hello my friend!',0

.code

start:

invoke StdOut,ADDR Message 
invoke ExitProcess,0
END start



[attachment deleted by admin]

MichaelW

Hi donkey,

In your listing of the operations under PROC, for MASM the order should be:
1. Save the value of EBP (stack base pointer usually PUSH it)
2. Move ESP into EBP (the top of the entry stack is now seen as the base)
3. SUB the number of local bytes from ESP (make room for the local data)
4. Save any registers in the USES directive
...

MyProc proc uses eax ebx ecx edx param1:DWORD, param2:DWORD
    LOCAL local1:DWORD, local2:DWORD, local3:DWORD
    mov   eax, param1
    mov   eax, param2
    mov   eax, local1
    mov   eax, local2
    mov   eax, local3
    ret
MyProc endp

00401000 55                     push    ebp
00401001 8BEC                   mov     ebp,esp
00401003 83C4F4                 add     esp,0FFFFFFF4h
00401006 50                     push    eax
00401007 53                     push    ebx
00401008 51                     push    ecx
00401009 52                     push    edx
0040100A 8B4508                 mov     eax,[ebp+8]
0040100D 8B450C                 mov     eax,[ebp+0Ch]
00401010 8B45FC                 mov     eax,[ebp-4]
00401013 8B45F8                 mov     eax,[ebp-8]
00401016 8B45F4                 mov     eax,[ebp-0Ch]
00401019 5A                     pop     edx
0040101A 59                     pop     ecx
0040101B 5B                     pop     ebx
0040101C 58                     pop     eax
0040101D C9                     leave
0040101E C20800                 ret     8

eschew obfuscation

donkey

#9
Hi MichaelW,

Depends on which assembler you use :),

GoAsm...

0040105C   . 55             PUSH EBP
0040105D   . 57             PUSH EDI
0040105E   . 56             PUSH ESI
0040105F   . 53             PUSH EBX
00401060   . 89E5           MOV EBP,ESP
00401062   . 83EC 04        SUB ESP,4
00401065   . 8B45 18        MOV EAX,DWORD PTR SS:[EBP+18]

00401068   . 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
0040106A   . 50             PUSH EAX                                 ; |Title
0040106B   . 55             PUSH EBP                                 ; |Text
0040106C   . 830424 FC      ADD DWORD PTR SS:[ESP],-4                ; |
00401070   . 6A 00          PUSH 0                                   ; |hOwner = NULL
00401072   . E8 A72F0000    CALL <JMP.&USER32.MessageBoxA>           ; \MessageBoxA
; ...
0040108E   . 89EC           MOV ESP,EBP
00401090   . 5B             POP EBX
00401091   . 5E             POP ESI
00401092   . 5F             POP EDI
00401093   . 5D             POP EBP
00401094   . C2 1000        RETN 10


Note that at offset 0040106B it pushes an offset to a local but never uses LEA, the source line was...

invoke MessageBox,NULL,offset testlocal,eax,0

Should the USES stuff be pushed as in MASM it would be more difficult to handle a "registerless" invoke, by doing them prior to the mov it makes the math exceedingly easy.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable