News:

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

option prologue/epilogue

Started by cobold, May 26, 2009, 08:13:30 AM

Previous topic - Next topic

cobold

As far as I understand ml generates prologue/epilogue calls for procedures:

                 ALIGN 4
00000294              RunTime proc ms:DWORD
                     LOCAL ttt:DWORD         ; days
                     LOCAL hhh:DWORD         ; hours
                     LOCAL mmm:DWORD         ; minutes
                     LOCAL sss:DWORD         ; seconds
                     LOCAL mss:DWORD

00000294   1   55      *    push   ebp
00000295   1   8B EC      *    mov   ebp, esp
00000297   1   83 C4 EC   *    add   esp, 0FFFFFFECh   
....
....
                     ret
000002F5   3   C9      *    leave   
000002F6   2   C2 0004      *    ret   00004h
000002F9              RunTime endp



If a procedure does not have any parameters and/or locals ml will not generate this code.
Why does one use OPTION PROLOGUE:NONE then?
What is the advantage, and when is it save to use OPTION PROLOGUE:NONE ??

ecube

you still have parameters, advanced asm coders just turn off  prologue/epilogue usually so they can align the code differently in hopes it'll speed things up. For instance


OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

align 16

Cmpi proc src:DWORD,dst:DWORD

    push ebx
    push esi
    push edi

    mov esi, [esp+16]               ; src
    mov edi, [esp+20]               ; dst
    sub eax, eax                    ; zero eax as index

  align 4
  @@:
    movzx edx, BYTE PTR [esi+eax]
    movzx ebx, BYTE PTR [edi+eax]
    movzx ecx, BYTE PTR [edx+Cmpi_tbl]
    add eax, 1
    cmp cl, [ebx+Cmpi_tbl]
    jne quit                        ; exit on 1st mismatch with
    test cl, cl                     ; non zero value in EAX
    jnz @B

    sub eax, eax                    ; set EAX to ZERO on match

  quit:
    pop edi
    pop esi
    pop ebx

    ret 8

Cmpi endp

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef


see how hutch changed the alignment twice, first it was align 16 then it was 4.

cobold

Quote from: E^cube on May 26, 2009, 08:23:12 AM

Cmpi proc src:DWORD,dst:DWORD

2 DWORD parameters, so:

Quote from: E^cube on May 26, 2009, 08:23:12 AM
    ret 8

because 2*4 bytes == size of parameters on stack, right?

What if you have any locals? Would that change the ret, f. ex if there were one local DWORD would it be ret 12 (8 two parameters + 4 one local?)

dedndave

it is much like a disassembled c program
locals are referenced with the ebp register

a proc with 2 dword locals...

SomeProc proc

        push    ebp
        sub     esp,8
        mov     ebp,esp
;
;local1 = [ebp]
;local2 = [ebp+4]
;
        add     esp,8
        pop     ebp
        ret

SomeProc endp

something like that
i always turn them off - if i want a stack frame, i do it myself

MichaelW

For procedures that have parameters, turning of the prologue/epilogue code saves a few clock cycles when the procedure executes, and avoids tying up the EBP register so it can readily be used for things other than accessing the stack frame (although it must be preserved if it is used).
eschew obfuscation

hutch--

cobold,

MASM is basically geared to use procedures with its PROC ENDP pair so that the assembler knows the start and finish of a procedure written this way. This is fine for procedures that use a normal stack frame but there are enough places where you benefit by writing a procedure without a stack frame but can still use a prototype and the INVOKE notation.

With the OPTION PROLOGUE/EPILOGUE operators you can selectively turn off the default procedure handling and write the entire code yourself while still having the procedure prototyped and called using INVOKE. When your proc is finished you can turn the default handling back on again. As Michael mentioned, writing a no stack frame procedure gives you a slightly lower call overhead and it makes EBP available if you need the extra register for the procedure.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Mirno

MASM by default will "create" the locals for your arguments, and assign them - even if you use "OPTION PROLOGUE:NONE", so you can still have the code in there, it will just be wrong!

You can also create your own macros, the default is somewhat akin to:

my_prologue MACRO szProcName, flags, cbParams, cbLocals, rgRegs, rgUserParams
  push ebp
  mov  ebp, esp

  IF cbLocals NE 0
    sub  esp, cbLocals
  ENDIF

  FOR usesreg, rgRegs
    push usesreg
  ENDM

EXITM <0>
ENDM


There is another topic on an "auto probing" prologue macro on the board here: http://www.masm32.com/board/index.php?topic=2011.0
I belive the epilogue is a similar structure macro-wise, but will have to deal with popping off the "uses" registers, and unwinding the stack rather than setting it up!

As hutch says, most of the time you'll either want the default, or none. I could imagine you may also want to write a prologue if you were dealing with classes or maybe an obfuscator of some sort.

Thanks,

Mirno

jj2007

Many people believe that coding without stack frame is more efficient. It depends... but without doubt, it becomes difficult to keep track of the stack. And of course, the frame makes the code longer: 29 without, 30 with frame. That small difference may be surprising at first sight, but Olly reveals the following:

No frame:
004010D3  Ú$ 60               pushad                           ; NoFrame.004010D3(guessed Arg1,Arg2)
004010D4  ³. 8B7C24 24        mov edi, dword ptr [arg.1]
004010D8  ³. 8B7424 28        mov esi, dword ptr [arg.2]
...
004010EC  ³. 61               popad
004010ED  À. C2 0800          retn 8


Frame:
004010F0  Ú$ 55               push ebp                         ; NoFrame.004010F0(guessed Arg1,Arg2)
004010F1  ³. 8BEC             mov ebp, esp
004010F3  ³. 60               pushad
004010F4  ³. 8B7D 08          mov edi, dword ptr [arg.1]
004010F7  ³. 8B75 0C          mov esi, dword ptr [arg.2]
...
00401109  ³. 61               popad
0040110A  ³. C9               leave
0040110B  À. C2 0800          retn 8


Compare the two versions of mov edi, dword ptr [arg.1]... while Olly treats both correctly as arg.1, they are different:

NoFrame:
mov edi, [esp+24h]

Frame:
mov edi, [ebp+8h]

The instructions relative to esp are one byte longer. For three arguments, code size is already identical.

Whether it is slower or faster, no idea: For the test code below, I could not get stable timings for a P4, but they were close - sometimes the proc with stack frame was a bit faster, sometimes a bit slower.

include \masm32\include\masm32rt.inc

MyProcNoFrame PROTO: DWORD, :DWORD
MyProcHasFrame PROTO: DWORD, :DWORD

.data?
buffer db 12 dup(?) ; destination

.code
hw db "Hello World", 0 ; source

start:
invoke MyProcNoFrame, offset buffer, offset hw
.if eax==sizeof hw
print offset buffer, 13, 10
.else
print "ERROR", 13, 10
.endif

invoke MyProcHasFrame, offset buffer, offset hw
.if eax==sizeof hw
print offset buffer
.else
print "ERROR"
.endif

print chr$(13, 10, 10, "Code sizes:", 13, 10)
mov eax, offset MyProcNoFrame_END-MyProcNoFrame
print str$(eax), 9, "MyProcNoFrame", 13, 10
mov eax, offset MyProcHasFrame_END-MyProcHasFrame
print str$(eax), 9, "MyProcHasFrame", 13, 10

getkey
exit

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

MyProcNoFrame proc dest:DWORD, src:DWORD
  pushad ; push 8 registers, 32 bytes (eax, ecx, edx, ebx, esp, ebp, esi, edi)
  mov edi, [esp+32+4] ; dest from stack (4 bytes each)
  mov esi, [esp+32+8] ; src from stack
  .Repeat
  lodsb
  stosb
  .Until al==0
  mov ecx, esi ; current src pos
  sub ecx, [esp+32+8] ; minus original src pos
  ; dec ecx ; uncomment to test the ERROR
  mov [esp+32-4], ecx ; move ecx into stack, will be returned as eax for further use
  popad
  ret 2*4 ; correct the stack manually for two arguments
MyProcNoFrame endp
MyProcNoFrame_END:

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

MyProcHasFrame proc dest:DWORD, src:DWORD
  pushad ; push 8 registers, 32 bytes (eax, ecx, edx, ebx, esp, ebp, esi, edi)
  mov edi, dest ; dest from stack as arg (3 bytes each)
  mov esi, src ; src from stack as arg
  .Repeat
  lodsb
  stosb
  .Until al==0
  mov ecx, esi ; current src pos
  sub ecx, src ; minus original src pos
  ; dec ecx ; uncomment to test the ERROR
  mov [esp+32-4], ecx ; move ecx into stack, will be returned as eax for further use
  popad
  ret ; do not correct the stack manually
MyProcHasFrame endp
MyProcHasFrame_END:

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef

end start

Jimg

Quote from: cobold on May 26, 2009, 08:13:30 AM
As far as I understand ml generates prologue/epilogue calls for procedures:

                 ALIGN 4
00000294              RunTime proc ms:DWORD
                     LOCAL ttt:DWORD         ; days
                     LOCAL hhh:DWORD         ; hours
                     LOCAL mmm:DWORD         ; minutes
                     LOCAL sss:DWORD         ; seconds
                     LOCAL mss:DWORD

00000294   1   55      *    push   ebp
00000295   1   8B EC      *    mov   ebp, esp
00000297   1   83 C4 EC   *    add   esp, 0FFFFFFECh   
....
....
                     ret
000002F5   3   C9      *    leave   
000002F6   2   C2 0004      *    ret   00004h
000002F9              RunTime endp



If a procedure does not have any parameters and/or locals ml will not generate this code.
Why does one use OPTION PROLOGUE:NONE then?
What is the advantage, and when is it save to use OPTION PROLOGUE:NONE ??

The answer to your question, is there is no advantage when the proc doesn't have any parameters/locals.
The real interesting part of the prologue/epilog code is to modify the stock version and add functionality.
For example, I occasionally use a prologue that stores the name of the procedure in a variable so I can print out tracing information.  You can't get the name of the procedure any other way without hard coding it in the every procedure.
like this-
; Thanks to Drizz
PrologueDef32 macro procname, flags, parambytes, localbytes, reglist, userparams
if (parambytes+localbytes) ne 0 or @InStr(,<userparms>,<FORCEFRAME>)
    push ebp
    mov ebp,esp
endif
if localbytes ne 0
    add esp,-localbytes
endif
for reg,reglist
    push reg
endm
    xprocname textequ <&procname>
exitm %localbytes
endm

EpilogueDef32 macro procname, flags, parambytes, localbytes, reglist, userparams
for reg,reglist
    pop reg
endm
if (parambytes+localbytes ne 0) or (@InStr(,<userparms>,<FORCEFRAME>))
    leave ;; mov esp,ebp / pop ebp
endif
if (flags and 10000b) ;; if flags bit 5 is set then caller cleans stack
    ret
else
    ret parambytes
endif
xprocname textequ <>
endm
OPTION PROLOGUE:PrologueDef32
OPTION EPILOGUE:EpilogueDef32

now every proc has its name available as xprocname

dedndave

that's cool Jim
i wonder if there is a way to do something similar for API function call names - lol
would be great for an error handler
i think the text for the names is already in the exe

UtillMasm


UtillMasm

\masm32\bin\ml.exe /c /coff /Fotest.obj /nologo test.asm
\masm32\bin\link.exe /subsystem:console /out:test.exe /nologo test.obj

###

c:\china>\RadASM2212\MASM\Projects\Prologue\test.exe
Hello World
Hello World

Code sizes:
29      MyProcNoFrame
30      MyProcHasFrame

c:\china>

sinsi

Just remember that if you use ""OPTION PROLOGUE:NONE" to use "OPTION PROLOGUE:PrologueDef" when you are finished, else tracking down problems is a lot of fun.
Light travels faster than sound, that's why some people seem bright until you hear them.

jj2007

Quote from: Jimg on May 26, 2009, 02:23:30 PM
now every proc has its name available as xprocname

Jim,
Can you give an example of its usage? I have tried, see code below, but without success. It assembles fine, but what I get is...

TITLE_IN_PROC: xprocname
TITLE_IN_MACRO: MyTest

... so the proc is being interpreted before the macro. Strangely enough, the order is reversed with JWasm, but still no success.

include \masm32\include\masm32rt.inc

PrologueDef32 macro procname, flags, parambytes, localbytes, reglist, userparams
if (parambytes+localbytes) ne 0 or @InStr(,<userparms>,<FORCEFRAME>)
    push ebp
    mov ebp,esp
endif
if localbytes ne 0
    add esp,-localbytes
endif
for reg,reglist
    push reg
endm
    xprocname textequ <&procname>
    % echo TITLE_IN_MACRO: xprocname
exitm %localbytes
endm

EpilogueDef32 macro procname, flags, parambytes, localbytes, reglist, userparams
for reg,reglist
    pop reg
endm
if (parambytes+localbytes ne 0) or (@InStr(,<userparms>,<FORCEFRAME>))
    leave ;; mov esp,ebp / pop ebp
endif
if (flags and 10000b) ;; if flags bit 5 is set then caller cleans stack
    ret
else
    ret parambytes
endif
xprocname textequ <>
endm

MyTest PROTO:DWORD,:DWORD,:DWORD,:DWORD

.code

OPTION PROLOGUE:PrologueDef32
OPTION EPILOGUE:EpilogueDef32

start:
invoke MyTest, 0, 0, 0, chr$("Hello...")
exit

MyTest proc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
  xTitle CATSTR <chr$(">, <xprocname>,<")>
    % echo TITLE_IN_PROC: xprocname
  invoke MessageBox, 0, lParam, xTitle, MB_YESNOCANCEL
  ret
MyTest endp

end start


EDIT: It's a can of worms. Here is one version that works with JWasm only.
Note that the apparently absolutely useless echo xprocname is the only means to get it working ::)


xTitle MACRO arg
  echo xprocname
  xT CATSTR <chr$(">, xprocname,<")>
  EXITM xT
ENDM
...
MyTest proc hwnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
  invoke MessageBox, 0, lParam, xTitle(), MB_YESNOCANCEL
  ret
MyTest endp

UtillMasm