I wanted to figure out how objects work by looking at the assembly output of the program. I have a class called Numbers
containing three ints
.
class Numbers {
public:
int n1;
int n2;
int n3;
};
Inside the main function, I create an instance named obj
and set each of the variables to a number.
int main() {
Numbers obj;
obj.n1 = 1;
obj.n2 = 2;
obj.n3 = 3;
}
The follwing code is the assembly generated:
int main() {
00935240 push ebp
00935241 mov ebp,esp
00935243 sub esp,0D8h
00935249 push ebx
0093524A push esi
0093524B push edi
0093524C lea edi,[ebp-0D8h]
00935252 mov ecx,36h
00935257 mov eax,0CCCCCCCCh
0093525C rep stos dword ptr es:[edi]
0093525E mov eax,dword ptr ds:[0093F000h]
00935263 xor eax,ebp
00935265 mov dword ptr [ebp-4],eax
Numbers obj;
obj.n1 = 1;
00935268 mov dword ptr [obj],1 ; === Here ===
obj.n2 = 2;
0093526F mov dword ptr [ebp-10h],2 ; === Here ===
obj.n3 = 3;
00935276 mov dword ptr [ebp-0Ch],3 ; === Here ===
return 0;
0093527D xor eax,eax
}
I thought that the base pointer pointed to the top of the stack frame, and since the function is main, pointed to the start of the program. How could it be subtracting from the base pointer when the stack pointer is what is pointing to the current address? Also, why is it accessing the variables out of order. It changes n1
, then is subtracts 16 bytes to get to the address of n2
, and then 12 bytes to get to n3
. Is there any reason for it doing this?
I am using Visual Studio 2013, with MASM as an assembler.
Between a wait for a slow Apache Pluto startup and a wait for my boss to finish her call, I like to wander these wasteland of the questions tagged with assembly.
So it is in this bored mood that I write another, useless, answer to this already satisfied OP.
;PROLOG
push ebp ;Save the caller frame pointer
mov ebp, esp ;Make our frame pointer
;ALLOCATE SPACE
sub esp, 0D8h ;Reserve 216 bytes on the stack
;Why 216? I dunno, maybe this makes the compiler
;source code easy to write/read/mantain
;SAVE CALLER REGS
push ebx
push esi
push edi ;Save caller register that we must not clobber
;INIT ALLOCATED SPACE
lea edi, [ebp-0D8h] ;EDI point to the start (the lower limit) of
;our reserved space (EDI = EBP-0d8h)
mov ecx, 36h ;ECX is the number of DWORD to write,
;36h*4 = 0d8h = 216 bytes
mov eax, 0CCCCCCCCh ;EAX is the DWORD to write, 0cccccccch comes
;from the fact that: 1) 0cch is the opcode for
;int 03h which is by convention the debug exception
;2) it is easy to spot 3) it is an invalid address to
;deference. This way an uninitialized var will misbehave
;when used (not for arithmetic). This is for debug purpose.
rep stos dword ptr es:[edi] ;Write ECX times EAX from ES:EDI upward (N.B. UPWARD)
;SET UP THE CANARY
mov eax, dword ptr ds:[0093F000h] ;Take a value which is safe in memory and cannot be
;overwritten by stack overflow (those guys, grrrr...)
xor eax, ebp ;Compute a function of the frame pointer and the canary
;This can make the canary unique on every invocation.
;The function is a xor
mov dword ptr [ebp-4],eax ;The canary is at the very beginning (ending?) or our
;allocated space. It is just below the frame pointer.
;Set the object fields
mov dword ptr [obj], 1 ;I believe this obj is [ebp-14h]
mov dword ptr [ebp-10h], 2 ;Remember that [ebp-10h] is after [ebp-14h], just like
mov dword ptr [ebp-0Ch], 3 ;-10 is after (i.e. bigger than) -14.
;Return the value 0
xor eax,eax ;EAX have to hold the returned value at the end of the
;function, V XOR V = 0 for all V