I am examining function calls with arguments, local variables on the stack and such...There is one thing I don't quite understand.

I'm making this assumption:
If the cpu make a call to a procedure, it first pushes the instruction pointer on the stack so it will know the correct adress to return to in the ret-opcode of the procedure.
( I'm thinking like this cause I started assembler with 6502 processor way back and it only had one stack 255 bytes big for system/user )

So I can't understand this:
If my assumption is true: If I push an argument and call a procedure , how can the procedure properly balance the stack BEFORE returning? I mean it has to adjust the stack pointer to point at the proper 'return-adress' , before the ret-opcode. Then it returns, but when back the argument that was pushed before calling is still not poped and the stack is unbalanced!?!

I'm thinking perhaps there one stack reserved for my program, and another stack for exclusive system-use upon which the return-adress is pushed automatically when calling? Is this so?
Posted on 2002-06-19 01:05:06 by david


I'm making this assumption:
If the cpu make a call to a procedure, it first pushes the instruction pointer on the stack so it will know the correct adress to return to in the ret-opcode of the procedure.
( I'm thinking like this cause I started assembler with 6502 processor way back and it only had one stack 255 bytes big for system/user )

So I can't understand this:
If my assumption is true: If I push an argument and call a procedure , how can the procedure properly balance the stack BEFORE returning? I mean it has to adjust the stack pointer to point at the proper 'return-adress' , before the ret-opcode. Then it returns, but when back the argument that was pushed before calling is still not poped and the stack is unbalanced!?!



Hello david!

Guess what? ret has the following form:

ret

where is an optional parameter, designating the number of bytes to be popped AFTER popping the return address. So ret pops off the return address, THEN pops of n bytes, where n happens to be the number of bytes passed to your proc.

Now isn't that cool???

:grin: :grin: :grin:
Posted on 2002-06-19 01:17:13 by AmkG
If the cpu make a call to a procedure, it first pushes the instruction pointer on the stack so it will know the correct adress to return to in the ret-opcode of the procedure.
( I'm thinking like this cause I started assembler with 6502 processor way back and it only had one stack 255 bytes big for system/user )
True.
If my assumption is true: If I push an argument and call a procedure , how can the procedure properly balance the stack BEFORE returning? I mean it has to adjust the stack pointer to point at the proper 'return-adress' , before the ret-opcode. Then it returns, but when back the argument that was pushed before calling is still not poped and the stack is unbalanced!?!
actually if you have a parameter passed to a procedure, the ret instruction will assemble into retn/retf value. For example in a typical win32 program whose function has 2 dword type parameters, ret will assemble into retn 8. This retn 8 will balance the stack so after the procedure is done it will continue to execute the code pointed by the EIP(which is still currently in the stack)... :)

The total size of the parameter/s will determine the size to be use on the value.
Posted on 2002-06-19 01:22:13 by stryker
Aha! That's genius ! Thanks! :)

However, I did a test prog in c++ to analyze the disassembly, I thought I would see a 'ret 4' in the end of the procedure but it's not there though, hmm, got any clue about the following:

I coded in the winmain:

myFunction(0xffeeddcc);

.............

void myFunction(ULONG ul)
{

MessageBoxA(NULL,"function","test",MB_OK);

}

and the resulting disassembly of this were:

(the function call)

push 0FFEEDDCC
call .000401240

.....................

(the function)

push ebp
mov ebp,esp
sub esp,008
push 000
push 000401210
push 000401218
push 000
call MessageBoxA
leave
retn ; <- Why isn't there any number?
Posted on 2002-06-19 01:41:28 by david
I've noticed that there isn't one if it's in Win32 C/C++ but if it's in Win32ASM you will see retn 4. C/C++ likes to use the add esp, value to balance the stack after a function call is made. So here's my findings:

1. Notice, like you said no retn value just retn.
2. The EIP value is probably on the top of the stack that's why you see this line mov eax, which is equal to mov, eax DWORD PTR meaning it accessed the second DWORD size value from the stack to get the parameter.
3. When retn was executed inside the function, the top of stack was popped into EIP.
4. leaving the top of the stack equal to the parameter passed. Which in this example was 0
5. add esp, 4 was there to balanced it.

So here's the actual rundown:

push 0 ;Second Topmost DWORD value of the stack. Our first param which is == MB_OK
push EIP ;Top of the stack(first)

... sub_401000
... call messagebox here
...
retn ;pop the top of the stack into EIP

execute the code pointed by EIP which is add esp, 4

add esp, 4 ;voila!!! a balanced stack

:)

P.S. Mine doesn't use frame based procedures that's why you don't see the use of the ebp register, your version is frame based but it still does the same thing. BTW I use IDA disassembler. Here's the code I used:
#include <windows.h>


void callMBox(DWORD style)
{
MessageBox(NULL, "Goodbye, cruel world!", "Note", style);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
callMBox(MB_OK);
return 0;
}
I'm really tired and sleepy(lame excuse :) ), so forgive me if I didn't make myslef clear. :alright:
Posted on 2002-06-19 02:33:50 by stryker
In c, the callee pops the stack for the arguments passed.
Your function takes no argument so 'ret' (= 'ret 0').
It passes arguments to MessageBoxA which takes care of poping the stack when returning.
Posted on 2002-06-19 03:01:54 by Alfhiger
Thanks! Yes that's very interesting! I have tried different things now. It seems even though you would compile as win32 app the compiler always use a C-style call as default, unless I defined my function-prototype as STDCALL which produced the 'ret 4' I wanted =)

(I used mingw-compiler and wdasm32 by the way.)

Another question , when do I use c-style call and when to use stdcall...?
Posted on 2002-06-19 03:22:09 by david
STDCALL is always used when interfacing with the Win32 API. EXCEPT for one routine that uses Cdecl: wsprintf (two if you consider that there are two versions: wsprintfA for ASCII and wsprintfW for Unicode).


Typically a procedure with variable number of arguments uses Cdecl but I think STDCALL can optionally handle var no. of args.
Posted on 2002-06-19 05:38:50 by AmkG
How would stdcall handle a variable number of arguments? Enlighten me ;)
Posted on 2002-06-19 07:53:46 by f0dder
You would need one parameter telling the proc how many parameters in total were passed to it, but you need that for C call convention anyway don't you.
Posted on 2002-06-19 10:42:04 by Eóin
And how is the count parameter going to be used in stdcall? There is no ret reg/mem instruction, and compilers don't generate self-modifying code.

(Hey f0dder! ;) )

Cdecl doesn't require a count argument (implicit or explicit). The various printf and scanf functions have a "control" argument (the format string) that determines how many arguments are accessed. But you could also have a "terminator" argument. I believe some Unix exec functions use NULL as an argument list terminator.
Posted on 2002-06-19 14:15:35 by tenkey
HOWTO: Dynamic Functions :)
[size=9].686

.MODEL FLAT, STDCALL
OPTION CASEMAP:NONE

INCLUDE \masm32\include\windows.inc
INCLUDE \masm32\include\kernel32.inc
INCLUDELIB \masm32\lib\kernel32.lib
INCLUDE \masm32\include\user32.inc
INCLUDELIB \masm32\lib\user32.lib
INCLUDE \masm32\include\masm32.inc
INCLUDELIB \masm32\lib\masm32.lib

.DATA

buffer DB 20 DUP(?)

.CODE

xparam:

mov edx, DWORD PTR [esp+4]
mov ecx, 2
inc edx

@@:
add eax, DWORD PTR [esp+ecx*4]
inc ecx
cmp ecx, edx
jbe @B

retn

START:

push 1
push 2
push 3
push 4
[color=red]push 4 ;Our Count - Tells xparam how many actual parameters were passed.[/color]
call xparam
invoke dwtoa, eax, OFFSET buffer
invoke MessageBox, 0, OFFSET buffer, 0, 0
mov eax, DWORD PTR [esp]
[color=#3366FF]inc eax[/color]
mov ecx, 4
mul ecx
add esp, eax
pop eax
invoke dwtoa, eax, OFFSET buffer
invoke MessageBox, 0, OFFSET buffer, 0, 0
invoke ExitProcess, NULL

END START[/size]
As you can see xparam can handle dynamic parameters and still the stack is balanced.

the second dwtoa-messagebox combo was there to verify that we have balanced the stack properly - this will print out garbage. If you don't believe me, try removing inc eax and I bet the second messagebox will print out 1 which was our last parameter passed to xparam.

On this example the output is 10 == 4 + 3 + 2 +1 ;)


of course, this is somewhat like a C calling convention but at least it shows how to create dynamic functions.
Posted on 2002-06-19 15:22:39 by stryker
That's right...xparam is a cdecl function -- not a stdcall function. The variable number of arguments is still handled by the cdecl code, not the stdcall code.
Posted on 2002-06-19 17:02:32 by tenkey
Well.... according to \masm32\Help\masm32.hlp:


Calling Conventions

C SYSCALL STDCALL BASIC FORTRAN PASCAL
+-------+-------+-------+-------+-------+-------+
Leading Underscore | X | | X | | | |
|-------+-------+-------+-------+-------+-------|
Capitalize All | | | | X | X | X |
|-------+-------+-------+-------+-------+-------|

Arguments Left to Right | | | | X | X | X |
|-------+-------+-------+-------+-------+-------|
Arguments Right to Left | X | X | X | | | |
|-------+-------+-------+-------+-------+-------|
Caller Stack Cleanup | X | | * | | | |
|-------+-------+-------+-------+-------+-------|
BP Saved | | | | X | X | X |

|-------+-------+-------+-------+-------+-------|
:VARARG Allowed | X | X | X | | | |
+-------+-------+-------+-------+-------+-------+

* The STDCALL language type uses caller stack cleanup if the :VARARG
parameter is used. Otherwise, the called routine must clean up the
stack.

The language type (langtype) determines the naming and calling conventions
used by the assembler. This allows you to share code and data with

modules written with other languages. Set the language type with the
.MODEL or OPTION LANGTYPE: directives or with the /G<x> command-line
option. Several directives allow you to specify a langtype
parameter to temporarily override the language type.

You can use the /H command-line option to limit the length of names
sent to the object file. Use this option to work with languages that
limit the maximum length of identifiers.
-o-


I just assumed that ':VARARG' means the thing has a variable number of arguments, but maybe it means something else...
Posted on 2002-06-20 02:55:32 by AmkG
And how about this?

push 1
push 2
push 3
push 4
call some_proc

some_proc:
mov eax,
shl eax,2
mov word ptr ,ax
ret 4
cleanup_size = $ - 2
Posted on 2002-06-20 11:49:50 by marcinbu