hi,

parameters are passed to the stack so i did

push eax
push edx
call myproc


myproc proc near


where are my parameters ? i know that they are at ebp+XX but whats the XX ? how can i find it while coding ?

myproc endp

in ida\when coding are local variables referecened by ebp-XX ?

thanks in advance.
Posted on 2002-05-06 07:16:09 by The Keeper
The call instruction pushes Instruction Pointer (and Code Segment for far calls) onto stack and loads Instruction Pointer with the address of proc-name.

so i think you can get the parameters through the esi register, or using pop instruction
Posted on 2002-05-06 07:30:40 by yoursguideline


<---,
<---,
Posted on 2002-05-06 07:36:27 by bdjames
push eax
push edx
call myproc


myproc PROC

mov edx,
mov eax,

myproc ENDP

is the return address for the CALL instruction. The stack moves backward in memory. ( PUSH EAX ) is the same as ( SUB ESP,4 / MOV ,EAX ). ( CALL XXX ) is the same as ( SUB ESP,4 / MOV ,IP+ ), but remember you don't have direct access to IP register - IP+ means the IP after the CALL instruction. Trace through your code in Ollydbg.
Posted on 2002-05-06 08:29:27 by bitRAKE
Or the easy way...



myproc proc p0:dword, p1:dword
mov eax, [p0]
mov edx, [p1]
ret
myproc endp

...
invoked myproc, 1000h, eax
Posted on 2002-05-06 09:01:08 by f0dder
I don't want to waste the precious EBP, nor to waste time with ENTER/LEAVE (and equivalents).. so I reference all through ESP.
F0dder "criticized" me because, having my own assembler (compiler), things are easy for me (i.e. automatic), while advicing around to use ESP directly, with normal assemblers, makes things a bit too complex.

When I have to provide a source, for contract reasons, from time to time I still code using standard assemblers (mostly NASM, which hopefully will become FASM soon), but I still use this technique neverthless.
I don't think it's complex.. to diminish confusion, I use to do things like this:



MOV EAX,[ESP+4*(x+y)]


Where "x" is the "parameter" I want to reference (e.g. x=0 is the return IP, x=1 is the first parameter, etc..), and "y" is a comfortable way to deal with the eventual extra stack depth.. for example:


MOV EAX,[ESP+4*(1+0)]
MOV EBX,[ESP+4*(2+0)]
MOV ECX,[ESP+4*(3+0)]

With the above I will move to EAX the 1st param, to EBX the 2nd param, and to ECX the third param.

Another example, this time dealing with stack extra depth troubles:


MOV EAX,[ESP+4*(1+0)] ;I reference the 1st param, but the stack depth is normal anyway (i.e. 0)

PUSH EDX ;depth = 1
MOV EBX,[ESP+4*(2+1)] ;I reference the 2nd param. Notice also that now y=1, easy
POP EDX ;depth returns to 0

PUSH EDX ;depth = 1
PUSH ESI ; " = 2
PUSH EDI ; " = 3
PUSH EBP ; " = 4
PUSH EDX ; " = 5
MOV ECX,[ESP+4*(3+5)] ;3 is the param, 5 is the current stack depth
POP EDX ;depth = 4
POP EBP ; " = 3
POP EDI ; " = 2
POP ESI ; " = 1
POP EDX ; " = 0

or some intricated direct parameter to parameter copying:
PUSH P32 0 ;depth = 1
PUSH P32 .Length ;depth = 2
PUSH U32 [ESP+4*(2+2)] ;2 param + 2 depth, which then becomes 3
PUSH P32 [ESP+4*(1+3)] ;1 param + 3 depth, which then becomes 4
PUSH U32 [InHandle] ;depth = 5
CALL [KERNEL32_ReadFile]


Sorry for my English.. I hope the above is clear and simple as it seems to me.

No compiler can beat a skilled assembly programmer, and never will, *as long as* you're ready to write some very "unreadable" code, and use all optimizations techniques available.
It makes no sense to privilege readability in a assembly program, and often the best optimizations leave readability much behind.

Just as example, CALL,RET -> JMP optimizations, etc.. something I rarely see in the asm sources that coders publish.. or knowing that e.g. the PentiumIV has a CALL/RET buffer that makes those optimizations detrimental, etc..
Too many people believe that just rewriting a C algo in asm makes it faster.. that's extremely wrong and misleading.
Posted on 2002-05-06 09:06:39 by Maverick
This method of accessing parameters on stack is very interesting.
And you can possibly do it a bit easier using some macroinstructions like (here in fasm format):



stdp = 0 ; stack depth variable

macro push arg
{ push arg
stdp = stdp+1 }

macro pop arg
{ pop arg
stdp = stdp-1 }

param equ esp+4*stdp+4*

; example of use:
push eax
mov eax,[param 2]
; ...
pop eax
Posted on 2002-05-06 09:34:37 by Tomasz Grysztar
Cool.. can't wait to see a FASM version that saves the reloc table as well, so I can finally switch from NASM to FASM ;) (by coincidence I wrote you an email about this 2 hours ago)
Posted on 2002-05-06 10:00:13 by Maverick
That's really cool, Privalov! I have spend hours hacking together a similar stack interface layer in MASM, and it really sucks because you have to use the macro names instead of instruction names. FASM is really maturing. If you documented the output code/structures some, I'm sure Maverick could code the output format he desires. I was able to hack a couple of macro ops into the assembler after reading the code for a while.
Posted on 2002-05-06 10:18:40 by bitRAKE
As an addition, the following macros could be added:

STKALLOC

which just SUB ESP,x and updates the stack depth accordingly, and:

STKFREE

which does the opposite.

STKALIGN x

Should I say more?

These are among techniques I use much. It has been a bit annoying when I had to use my own way the standard assemblers, though. I always tryed to tweak (I even modifyed/customized NASM source in a couple of points) the assemblers to make them as much similar to my "standards" as possible.

---

FASM is gonna kick ***.. finally I have a substitute for NASM.. and I'm sure you bitRAKE will not miss MASM for the 2nd time.. this time being FASM so much supported, unlike NASM. ;)

About my specific need, I don't need anything fancy.. I already have written years ago OBJ, RDOFF and RDOFF2 converters to my own executable format (i.e. with the headers in the source).. it will take me 0 time (actually I've already coded that) to port a BIN (FASM already outputs it) plus RELOC (that's what I need) output to my own executable format.

Then, for me, FASM will already be perfect. :)

I will spread the word as much as possible.. then I just hope more people start using it.. on this board and elsewhere.

Privalov: don't wanna fill you with work, pal.. :P but, just to let you know, a friend of mine is very excited about FASM too, but, being he a Linux coder, he'd like (sometime in the future) it to produce Linux executables as well. No rush, though, you've already done and are doing a lot. ;)
Posted on 2002-05-06 17:10:32 by Maverick
For me the best thing about FASM is that it is an assembler for
assembly programmers and you know that when you use it.
Posted on 2002-05-06 17:29:25 by bitRAKE
In fact, and that's just why I can't stand nearer than 10000 Kms from MASM.
Posted on 2002-05-06 17:55:55 by Maverick
Maverick,

Using only the stack pointer makes sense if you don't use LOCAL values and the proc you are writing is small enough and called often enough to worry about the extra 2 instructions from setting up a stack frame.

It is probably worth keeping in mind that if you are passing a limited number of parameters, you can pass them in registers and forget about the stack altogether, you then have no more than the CALL/RET overhead.

In time past I have passed values in GLOBAL variables in an environment that had very limited stack space and it works fine and certainly had no stack overhead at all.

Glad to see FASM coming along well.

Regards,

hutch@movsd.com
Posted on 2002-05-06 18:17:44 by hutch--
Hutch,
Although I understand your point of view, using only ESP has never limited my freedom with local variables and buffers.
Posted on 2002-05-06 18:25:48 by Maverick
The only time when a stack frame is needed is when you are using the features of the frame. Like if you don't know what ESP is going to be, but usually there is a work-around. Sometimes the frame is used to recover from errors. The base fact of the matter: almost no MASM PROCS exist that need the frame. It is true that the syntax has all the pretty accessories that Hutch-- likes to knock - making MASM mask the stack ugliness for the newbies. This wouldn't be so bad if the assembler was smarter about the matter. Different code has different requirements and expectations of the assembler.
Posted on 2002-05-06 21:10:38 by bitRAKE
Have I missed something here, MASM like most assemblers can write anything you like, the reason why most people write non critical code in a standard PROC in MASM is to use the INVOKE syntax for the sheer convenience.

Having coded nearly every variation of how to call a proc in MASM, there is still no faster way than a bare CALL/RET to a label and the only alternative is to inline the code if the CALL/RET syntax is to slow for what you are after.

If you need to use the stack to pass parameters, you are probably not doing it the fastest way anyway, registers, structures and global scope variable can all be used without the stack so if its speed you want, streamlining the stack frame is like tuning a T model ford, PHUN but who cares. :)

Regards,

hutch@movsd.com
Posted on 2002-05-07 05:59:40 by hutch--
Hutch--, assuming we want to maintain that convenience that presently exists, what is the most optimized calling convention that can be used? Or, rather I should say calling convention strategy because one method isn't correct for all uses. (Place this thought before my previous post. :))
Posted on 2002-05-07 07:02:07 by bitRAKE
Steve,
The stack is not useful only for parameters..
Personally I tend to avoid to allocate from the HEAP as I avoid a kick on the balls. Pretty much. :grin:
Posted on 2002-05-07 08:03:28 by Maverick
Ricky,

I guess we would agree that there are many different ways of doing things when it comes to using blocks of code and it can be packaged in many different ways. It seems to be on a needs basis, if its routine code particularly in complex logic, the easier the calling technique, the more complex the code can be written without errors.

On the other end in the middle of a speed critical algo, any calling method may be too slow and inlining the block of code is the only viable way of getting the performance. I would think that the distinction is to either inline or not inline, the rest are variations of CALL/RET.

If you need to manually code a proc to gain some adevantage from it, I think fair enough but unless there is an advantage from it, I wonder if its worth the effort.

Maverick,

While I understand the desire to avoid allocating memory that you don't need, how do you handle things like crunching ten meg of data at a time without allocated memory ? I imagine the only way is to use one of the dynamic strategies that are available through the operating system assuming windows coding.

Regards,

hutch@movsd.com
Posted on 2002-05-07 09:54:26 by hutch--
I have my own HEAP manager, with (manual, not automatic) garbage collection and defragmentation (a simple system based on a unlock that returns a handle, so then the buffer can be swapped to disk or moved, etc..).

But anytime I can allocate from the stack, I do it. It's quicker, and you don't bother asking yourself if you're fragmenting the precious heap (or one of them).

I don't get your example on crunching (do you mean compression?), but nothing prevents to reserve 10 or 100 MB of stack space.
There's no problem whatsoever. I do this in my startup code:


MOV U32 [FS:4],0x7FFFFFFF
MOV U32 [FS:8],0x00000000

This allows me to freely switch stacks whenever I wish it - without having Win32 killing my program at the first stack limit check (it would otherwise, when you go outside the PE-specified stack limits).

So I'm free to have e.g. a 10, 100 or 1000 MB stack space, if I need it.

There're other more advanced tricks I cannot disclose (I say this not to claim anything but just to mean that if I bother is because the gain is even higher), but the above alone already seems very useful to me anyway, even alone.

To make you an example, in a recent routine I wrote I needed to allocate two buffers, one after the other.
I had to do it in that sequence because the size of the second depends on the operations performed on the first.
At the end of the routine, the first buffer must be freed, and the second will remain resident.
This leaves a hole. I could fix it by unlocking the second buffer and reallocating it.. but all is much more simple and logical by choosing the first buffer to reside on the stack.
The only side effect is that allocation and freeing is faster.. so there are only gains by doing things this way. And I don't leave a hole in the heap.
Sure, a small program running under virtual memory and 2GB of address space will not have big problems with such holes.. but I don't design for Win32 specifically, I've my own OS in which I like to do things the right way - not the Win32 way - so the day that we'll move to a real OS I won't have to rewrite all of my code.

Said that, I repeat, doing things that way is faster and uses less code, so.. holes or not holes.
Posted on 2002-05-07 17:31:14 by Maverick