为什么当我访问一个由三个整数组成的对象时,它会从基指针而不是堆栈指针中减去?
Why when I access an object consisting of three integers, does it subtract from the base pointer, and not the stack pointer?
我想通过查看程序的汇编输出来弄清楚对象是如何工作的。我有一个名为 Numbers
的 class,其中包含三个 ints
.
class Numbers {
public:
int n1;
int n2;
int n3;
};
在主函数中,我创建了一个名为 obj
的实例,并将每个变量设置为一个数字。
int main() {
Numbers obj;
obj.n1 = 1;
obj.n2 = 2;
obj.n3 = 3;
}
下面的代码是生成的程序集:
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
}
我以为基指针指向栈帧的顶部,而且由于函数是main,指向程序的开始。当堆栈指针指向当前地址时,它怎么能从基指针中减去呢?另外,为什么它会乱序访问变量。它改变 n1
,然后减去 16 个字节得到 n2
的地址,然后减去 12 个字节得到 n3
。它这样做有什么理由吗?
我正在使用 Visual Studio 2013,使用 MASM 作为汇编程序。
ebp寄存器通常指向栈上当前函数的栈帧的开始(通常包含指向前一帧的指针)。
在汇编输出中,先保存最后一个栈帧指针,然后在ebp中保存当前栈指针的地址。这是当前函数堆栈帧的开始。然后,从 esp 中减去一些字节以在堆栈上为局部变量保留 space。
变量顺序正确;变量地址低于当前 ebp 地址(堆栈从高地址向低地址增长)。
I thought that the base pointer pointed to the top of the stack frame,
and since the function is main, the start of the program.
main()
并不是第一个调用的函数;之前调用了很多libc启动函数(例如全局对象初始化)
在等待缓慢的 Apache Pluto 启动和等待我的老板完成她的电话之间,我喜欢在这些标有 assembly.
的问题荒野中徘徊
所以我就是怀着这种无聊的心情,再写一个没用的答案给这个已经满意的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
我想通过查看程序的汇编输出来弄清楚对象是如何工作的。我有一个名为 Numbers
的 class,其中包含三个 ints
.
class Numbers {
public:
int n1;
int n2;
int n3;
};
在主函数中,我创建了一个名为 obj
的实例,并将每个变量设置为一个数字。
int main() {
Numbers obj;
obj.n1 = 1;
obj.n2 = 2;
obj.n3 = 3;
}
下面的代码是生成的程序集:
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
}
我以为基指针指向栈帧的顶部,而且由于函数是main,指向程序的开始。当堆栈指针指向当前地址时,它怎么能从基指针中减去呢?另外,为什么它会乱序访问变量。它改变 n1
,然后减去 16 个字节得到 n2
的地址,然后减去 12 个字节得到 n3
。它这样做有什么理由吗?
我正在使用 Visual Studio 2013,使用 MASM 作为汇编程序。
ebp寄存器通常指向栈上当前函数的栈帧的开始(通常包含指向前一帧的指针)。
在汇编输出中,先保存最后一个栈帧指针,然后在ebp中保存当前栈指针的地址。这是当前函数堆栈帧的开始。然后,从 esp 中减去一些字节以在堆栈上为局部变量保留 space。
变量顺序正确;变量地址低于当前 ebp 地址(堆栈从高地址向低地址增长)。
I thought that the base pointer pointed to the top of the stack frame, and since the function is main, the start of the program.
main()
并不是第一个调用的函数;之前调用了很多libc启动函数(例如全局对象初始化)
在等待缓慢的 Apache Pluto 启动和等待我的老板完成她的电话之间,我喜欢在这些标有 assembly.
的问题荒野中徘徊
所以我就是怀着这种无聊的心情,再写一个没用的答案给这个已经满意的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