Balancing the Stack for API's called using EXTERN

Started by 00100b, January 03, 2005, 08:32:27 PM

Previous topic - Next topic

00100b

Hey all.  Do you think that it's about time that I started asking questions? ;)

From my studies so far, I've learned that one must pop from the stack what one pushes to the stack in order to keep the stack in balance and prevent unexpected results when later referencing the stack.

I'm going to use a very basic example from which to base my question.

Let's say that the following is used to declare an API prototype:


; API Prototype
EXTERN GetModuleHandleA@4:NEAR

; and the library that contains it.
INCLUDELIB c:\masm32\lib\kernel32.lib


and then used as such:


; Get the application's instance descriptor.
PUSH 0
CALL GetModuleHandleA@4
MOV hInstance, EAX


I had omitted the declaration of hInstance above, but to be thurough:


_DATA SEGMENT

    hInstance DWORD 0

_DATA ENDS



How would I pop the value 0 that I had pushed onto the stack, or is that handled by the API call (much in the same way that RET does) in that it automatically pops the number of bytes specified for the call (ie, the @4 part of the prototype)?

Thank you.

John

My understanding is that it depends. Chapter 12 of the MASM programmers guide covers this in part.

Accoring to what I've read if the routine you are calling uses the C calling convention then it will be your responsibility to balance the stack. The wsprintf API uses the C calling convention I believe. In your case GetModuleHandle will do it for you as you said. You can check this with a debugger by looking at esp before and after the call and insuring they remain the same.

00100b

Thanks John for the response.

May I ask a follow-up question?

From my understanding, POP can be used with memory and register operands.  How would I pop an immediate operand, or would I need to MOV the immediate value into either a variable or register before pushing it to the stack and then call the API using the above method?

Vortex

Hi Forby,

More than the 99% of API functions uses the stdcall parameter passing convention. All the value are passed from right to left and it's the job of the called stdcall API function to balance the stack.

You can push immediate values on the stack, no need of the mov instruction:
invoke GetModuleHandle,0
or
push 0
call GetModuleHandle


are both equivalent

00100b

Thank for the response Vortex,

From my search of the MSDN Library, wsprintf is the only one in the API Reference that states that it uses the _cdecl calling convention.  Is it safe to assume, that if the API doesn't specifically state that is uses this calling method, that it uses the _stdcall convention?

Also, for clarification, for those less-than 1%, I would use a memory or register operand instead of an immediate operand.  Correct?

MichaelW

Forby,

In answer to your first question, you could just use invoke to call any function that takes arguments, and assuming the function was prototyped correctly, MASM would generate code to balance the stack where necessary. For example, user32.inc for MASM32 prototypes wsprintf as:

wsprintfA PROTO C :DWORD,:VARARG
wsprintf equ <wsprintfA>

So:

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    .486                       ; create 32 bit code
    .model flat, stdcall       ; 32 bit memory model
    option casemap :none       ; case sensitive

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

    includelib \masm32\lib\masm32.lib
    includelib \masm32\lib\user32.lib
    includelib \masm32\lib\kernel32.lib

    include \masm32\macros\macros.asm

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

      P1Mins    equ 4FD4A0h

    .data
   
      buffer      db 30 dup(0)
      format      db "%X",0

    .code

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

start:

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    invoke GetModuleHandle, 0   
    invoke wsprintf, ADDR buffer, ADDR format, eax
    print ADDR buffer
    print chr$(13,10)
   
    print chr$(13,10,"Press enter to exit...",13,10)
    invoke StdIn, addr buffer, 1
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end start

Generates this code:

00401000                    start:
00401000 6A00                   push    0
00401002 E8F9000000             call    fn_00401100
00401007 50                     push    eax
00401008 681E304000             push    40301Eh
0040100D 6800304000             push    403000h
00401012 E8DD000000             call    fn_004010F4
00401017 83C40C                 add     esp,0Ch
...
004010F4                    fn_004010F4:
004010F4 FF251C204000           jmp     dword ptr [wsprintfA]
004010FA                    fn_004010FA:
004010FA FF2504204000           jmp     dword ptr [ExitProcess]
00401100                    fn_00401100:
00401100 FF2508204000           jmp     dword ptr [GetModuleHandleA]

eschew obfuscation

Vortex

Quote from: 00100b on January 03, 2005, 09:26:26 PM
From my search of the MSDN Library, wsprintf is the only one in the API Reference that states that it uses the _cdecl calling convention.

Hi Forby,

There is also the wvsprintf function using the _cdecl calling function.

00100b

Thank you all for your responses.

This was all part of my current exercise in trying to determine the pros and cons of using EXTERN/CALL versus PROTO/INVOKE.

I think that the PROTO/INVOKE has it.  Aside from readability, it appears to be a single method in which handles the most, if not all (I'm not that fluent yet  :wink) of my API/PROC calling needs.

Be patient with me.  I'll get there... eventually.

Mirno

00100b, the two will result in the same code.
It's worth knowing what happens when you assemble an invoke statement (it is converted by MASM to the push / call ( / clean-up if C calling convention)), but once you've learned it use invoke as it avoids errors as MASM will check the number of parameters and do clean-up where necessary.

Knowing what happens is useful though as it allows you to write ugly code when you need it.

Mirno

00100b

Thanks Mirno,

Yeap, that is the route that I'm taking.  What I was doing was writing a module using each method and seeing what the gains/pitfalls were.

Invoke is the route I think that I will use going forward.

It will probably be a while before I write anything but ugly code though  :lol