Could somebody please tell me what is the purpose of the two push ecx
instructions below? I can't understand what they are supposed to be doing?
I realise the push epb
is saving the stack base pointer and then mov epb, esp
is assigning the stack pointer to the previous stack base pointer.
int main(){
01301190 push ebp
01301191 mov ebp,esp
01301193 push ecx ;here??
01301194 mov dword ptr [h],0CCCCCCCCh
int h = my_func(1,3);
int my_func(int a, int b){
01301160 push ebp
01301161 mov ebp,esp
01301163 push ecx ;here??
01301164 mov dword ptr [m],0CCCCCCCCh
int m = 0;
0130116B mov dword ptr [m],0
m = a*b;
01301172 mov eax,dword ptr [a]
01301175 imul eax,dword ptr [b]
01301179 mov dword ptr [m],eax
return m;
0130117C mov eax,dword ptr [m]
}
0130117F mov esp,ebp
}
01301181 pop ebp
01301182 ret
It pushes the value of ecx on the stack. ecx is a x86 CPU register, which can hold a value of a certain amount of bits (32 or 64 (called rcx then) on modern x86 CPU's). The call stack is divided into stack frames.
It first moves the 4 bytes located at memory location (%esp) into the specified register or memory location, and then increments ESP by 4. Save this answer.
eax is the 32-bit, "int" size register. It was added in 1985 during the transition to 32-bit processors with the 80386 CPU. I'm in the habit of using this register size, since they also work in 32 bit mode, although I'm trying to use the longer rax registers for everything. ax is the 16-bit, "short" size register.
The pop instruction removes the 4-byte data element from the top of the hardware-supported stack into the specified operand (i.e. register or memory location). It first moves the 4 bytes located at memory location [SP] into the specified register or memory location, and then increments SP by 4.
The push ecx
allocates 4 bytes on the stack for the local variable (m
and h
). The actual content of ecx
does not matter - the allocated slot is immediately overwritten by 0CCCCCCCCh
(this magic value is used by Visual C++ in debug builds to mark uninitialized variables).
Visual C++ often uses push ecx
and pop ecx
as alternatives to sub esp, 4
and add esp, 4
. Why? There are several reasons:
push ecx
and pop ecx
are single-byte instructions, while add
and sub
are three byte each. The difference may not be huge, but all the saved bytes in all functions may add up to something substantial.
ecx
is considered to be spoiled by a function call, so it's safe to trash it with pop ecx
after function calls. So you often see code like:
push arg1 ; push an argument
call __func1 ; call the function
pop ecx ; restore the stack
For push
, there's no real reason to use ecx
specifically - any of the basic registers would do. I guess it was just picked for symmetry, or to not be confused with real saving of a non-volatile register like esi
or edi
.
It would be extremely instructive to see the definitions of a
, b
, and m
in the assembly code above. I'm guessing they are ebp+8
, ebp+12
, and ebp-4
. If that is true, the compiler is not generating any special code for the debugger, it is just generating the code in the most straight-forward method. The location created on the stack by pushing ecx
is the memory location created for the local variable m
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With