> Still not clear about
- ESP+0x10 is first or third or the last parameter?
Try writing
void __fastcall test(int x, int y, int z)
{
}
Then code
test(1, 2, 3)
Set a breakpoint and see where the values are.
It is often more productive to run an experiment than to ask a question or
read the documentation.
You can only trust the results if optimization is completely turned off.
Third: ECX and EDX for first 2 parameters.
First: this paper.
Last:
From MSDN:
The first two DWORD or smaller arguments are passed in ECX and EDX
registers; all other arguments are passed right to left.
Because args passed right to left, if there are 3 paras( p1, p2, p3),
should I consider ESP+0x10 is p3?
See previous comment. I don’t recall, but this is such a screamingly
obvious experiment I’m surprised you didn’t try it.
stacks grow down
Does it means the case:
ESP –> address 0x8000,0020
ESP+0x4 –> address 0x8000,001c
ESP+0x8 –> address 0x8000,0018
No. The stack *grows* down. It means exactly what it says when you write
ESP+4: the value 4 is added to the value in ESP to get the address. Note
that each “push” and the “call” each decrement ESP. So, before the last
parameter was pushed, the value of ESP was 0x8000014C. Note that these
offsets are correct ONLY if you set a breakpoint at the very first
instruction (NOT first-line-of-C-code) of the function.
p3 ESP+C 0x80000148
p2 ESP+8 0x80000144
p1 ESP+4 0x80000140
retaddr ESP 0x8000013C
- current stack pointer
Does ESP is the meaning of current stack
pointer?
Sorry for my confusing, in my opinion, stack pointer is a pointer which
point to the last (full or empty) element push to this stack.
ESP points to the current stack position. There is no concept of “full”
or “empty” here. Note that in general you will see, if any local
variables are used
sub ESP, nnn
where nnn represents the amount of space needed for the local variables,
“rounded” up to the nearest multiple of 4 (for Win32).
The current ESP now points to a location which is the top of the stack.
In production code, this means that all local variables you do not
initialize before their use inherit whatever garbage values that had been
left on the stack.
So my confusing is
there are
ESP saved edi
ESP+4 saved esi
ESP+8 saved ebx
ESP+12 return address
ESP+16 first parameter
ESP+20 second parameter
so many element in the stack, why the stack pointer still at the edi
element?
Because edi is the current top of the stack! There may be several
THOUSAND values below the second parameter, but “below” on a stack means
“at higher addresses”
Since you are so interested in this, write some test apps. Make sure all
optimizations are off, but “debug” mode is not selected (in app space,
this results in a lot of extra code for debugging prolog. Once you
understand what the basic behavior is, you can then do a debug build and
study what it does). Turn on the compiler option for listing, mixed
source, assembly, and code. Study the listing. The set a breakpoint at
the call to the function and, one instruction at a time, single-step, and
watch what each instruction does. You should bring up a memory display
window, and at the breakpoint set it to show the memory at esp, and a
register-display window, so you can watch the registers change.
Ok, here’s a trick question. Those of you who know the answer, keep quiet
until the OP answers.
int i = 0;
int data = { 9, 10, 11, 12};
func(i++, data[i]);
or
func(++i, data[i]);
or
func(i, data[i++]);
or
func(i, data[++i]);
or
func(data[i], i++);
or
func(data[i], ++i);
or
func(i, data[i++]);
or
func(i, data[++i]);
if the function is
void func(int a, int b)
{
// print a=%d, b=%d\n
}
Hint: the answer is not what you might expect it to be. You can probably
save a lot of time by reading the C language spec on parameters. After
you do some research and post the answer, we’ll tell you if you are
correct.
(And yes, I know this is a trick question, but when you are down looking
at the generated assembly code, understanding the implications becomes a
whole lot more important. So give the OP a chance, guys, before jumping
in)
8015C914 53 push ebx
8015C915 56 push esi
8015C916 57 push edi
8015C917 8B7C2414 mov edi,dword ptr [esp+14h]
If we consider esp+0x14 is a parameter,
but from this code,
It seems that ebx, esi, edi enter to the stack later than parameter of
function.
Does my guess right?
Yes. The sequence is
push rightmost parameter value
…
push leftmost paramter value
push return address and jump to function (call instruction)
function prologue
save frame pointer if necessary
set current frame pointer to stack pointer
save preservable registers this function will need to use
decrement stack pointer to make room for local variables
…body of functiom
function epilog
strip locals from stack
pop saved registers
pop frame pointer
pop return address to EIP
upon return from call
increment ESP to strip parameters from stack
Now, many of these actions are omitted if not required. For example, if
the function only uses transient registers for its computations (those
registers whose content does not need to be saved), the the register saves
are not made, and the restores are not done. If the function does not use
a frame pointer, it isn’t saved. If local variables are not needed, they
won’t be allocated or stripped. If __stdcall or __fastcall are used, the
action of popping the return address to EIP and incrementing esp to strip
the paramters is a single instruction: RET n, and therefore, there will be
no increment of the stack pointer after the call instruction.
Note that you can do all these experiments in user space to see what happens.
joe
NTDEV is sponsored by OSR
Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev
OSR is HIRING!! See http://www.osr.com/careers
For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars
To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer