同样一段c++代码生成的汇编指令可能会不一样。有多种原因,例如编译器、调用约定或者底层平台。
今天要分析的是cdecl在x86机器上用visual c++ 2005上的编译结果。
首先需要设置一下项目配置以得到从源代码生成的汇编代码。
项目属性->配置属性->c/c++->输出文件->汇编输出 = Assembly With Source Code (/FAs)。
要被编译的源文件是:
Code
#include "stdafx.h"
struct Point3D
{
int X;
int Y;
int Z;
Point3D(int x, int y, int z):X(x),Y(y),Z(z)
{}
};
Point3D AddPoint3D(Point3D p1, Point3D p2)
{
Point3D p(p1);
p.X += p2.X;
p.Y += p2.Y;
p.Z += p2.Z;
return p;
}
void main()
{
Point3D p1(1,2,3);
Point3D p2(4,5,6);
Point3D p3 = AddPoint3D(p1,p2);
}
#include "stdafx.h"
struct Point3D
{
int X;
int Y;
int Z;
Point3D(int x, int y, int z):X(x),Y(y),Z(z)
{}
};
Point3D AddPoint3D(Point3D p1, Point3D p2)
{
Point3D p(p1);
p.X += p2.X;
p.Y += p2.Y;
p.Z += p2.Z;
return p;
}
void main()
{
Point3D p1(1,2,3);
Point3D p2(4,5,6);
Point3D p3 = AddPoint3D(p1,p2);
}
生成的汇编代码是:
Code
; Listing generated by Microsoft (R) Optimizing Compiler Version 14.00.50727.762
TITLE d:\project\CheckAsm\CheckAsm\CheckAsm.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB MSVCRTD
INCLUDELIB OLDNAMES
PUBLIC ?AddPoint3D@@YA?AUPoint3D@@U1@0@Z ; AddPoint3D
EXTRN @_RTC_CheckStackVars@8:PROC
EXTRN __RTC_Shutdown:PROC
EXTRN __RTC_InitBase:PROC
; COMDAT rtc$TMZ
; File d:\project\checkasm\checkasm\checkasm.cpp
rtc$TMZ SEGMENT
__RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown
rtc$TMZ ENDS
; COMDAT rtc$IMZ
rtc$IMZ SEGMENT
__RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase
; Function compile flags: /Odtp /RTCsu /ZI
rtc$IMZ ENDS
; COMDAT ?AddPoint3D@@YA?AUPoint3D@@U1@0@Z
;上面一堆先不管
_TEXT SEGMENT;定义PE文件中的Text段
_p$ = -16 ; size = 12;
___$ReturnUdt$ = 8 ; size = 4
_p1$ = 12 ; size = 12
_p2$ = 24 ; size = 12
;上面4个符号是AddPoint3D这个方法中的局部变量、返回值和参数的偏移量(相对于ebp)
?AddPoint3D@@YA?AUPoint3D@@U1@0@Z PROC ; AddPoint3D, COMDAT AddPoint3D方法的定义开始,由于name mangling,名称变为了AddPoint3D@@YA?AUPoint3D@@U1@0@Z
; 15 : {
push ebp;保存caller的ebp到堆栈
mov ebp, esp;设置本方法的ebp。ebp在方法调用的开始阶段被设置,结束阶段被还原,调用过程中保持不变。ebp表示为本次调用所分配的堆栈桢的起始地址
sub esp, 212 ; 000000d4H esp减少一定的数量,表示为本次调用在栈上分配了一定的空间。让我们把>ebp && <= 现在的esp 的这段内存称之为locals
push ebx;保存现场的3个push
push esi
push edi
lea edi, DWORD PTR [ebp-212];把edi设置成刚才sub esp后esp的值,也就是locals的最低地址
mov ecx, 53 ; 00000035H 53 * 4 = 212, you see whats going on here?
mov eax, -858993460 ; ccccccccH 设置eax=cccccccch
rep stosd ;这是一个串操作指令,其意义是
;while(ecx)
;{
; *(int*)edi = eax;
; edi += sizeof(int);//这里就是4
; ecx--;
;}
;本指令和它上面的三条指令完成了把locals这段内存初始化成一片ccccccccccccccccccccc.
;当此指令执行完毕,edi == ebp
; 16 : Point3D p(p1);
mov eax, DWORD PTR _p1$[ebp]
mov DWORD PTR _p$[ebp], eax
mov ecx, DWORD PTR _p1$[ebp+4]
mov DWORD PTR _p$[ebp+4], ecx
mov edx, DWORD PTR _p1$[ebp+8]
mov DWORD PTR _p$[ebp+8], edx
;复制构造局部变量p。_p1$[ebp]的意思就是[ebp + _p1$],_p1$是在前面定义过的一个偏移量
; 17 : p.X += p2.X;
mov eax, DWORD PTR _p$[ebp]
add eax, DWORD PTR _p2$[ebp]
mov DWORD PTR _p$[ebp], eax
; 18 : p.Y += p2.Y;
mov eax, DWORD PTR _p$[ebp+4]
add eax, DWORD PTR _p2$[ebp+4]
mov DWORD PTR _p$[ebp+4], eax
; 19 : p.Z += p2.Z;
mov eax, DWORD PTR _p$[ebp+8]
add eax, DWORD PTR _p2$[ebp+8]
mov DWORD PTR _p$[ebp+8], eax
;这些很好理解
; 20 :
; 21 : return p;
mov eax, DWORD PTR ___$ReturnUdt$[ebp]
;ReturnUdt中的Udt表示user defined type
;这条指令的意思是,调用方在从ebp开始偏移量是___$ReturnUdt$的dword中保存的返回值应当存放的地址,把这个地址加载到eax中
;此时eax保存的是一个地址,这个地址实际上call中局部变量p3的地址
mov ecx, DWORD PTR _p$[ebp]
mov DWORD PTR [eax], ecx
mov edx, DWORD PTR _p$[ebp+4]
mov DWORD PTR [eax+4], edx
mov ecx, DWORD PTR _p$[ebp+8]
mov DWORD PTR [eax+8], ecx
;从局部变量p复制到call的局部变量p3中
mov eax, DWORD PTR ___$ReturnUdt$[ebp]
;在eax中保存返回值的地址
; 22 : }
;这一段{
push edx
mov ecx, ebp
push eax
lea edx, DWORD PTR $LN5@AddPoint3D
call @_RTC_CheckStackVars@8
pop eax
pop edx
;这一段}可以忽略掉,并不是我们程序逻辑的一部分
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0;函数返回,返回值的保存地址存放在eax中
npad 2
$LN5@AddPoint3D:
DD 1
DD $LN4@AddPoint3D
$LN4@AddPoint3D:
DD -16 ; fffffff0H
DD 12 ; 0000000cH
DD $LN3@AddPoint3D
$LN3@AddPoint3D:
DB 112 ; 00000070H
DB 0
?AddPoint3D@@YA?AUPoint3D@@U1@0@Z ENDP ; AddPoint3D
_TEXT ENDS
PUBLIC ??0Point3D@@QAE@HHH@Z ; Point3D::Point3D
PUBLIC _main
EXTRN __RTC_CheckEsp:PROC
; Function compile flags: /Odtp /RTCsu /ZI
; COMDAT _main
_TEXT SEGMENT
_p3$ = -56 ; size = 12
_p2$ = -36 ; size = 12
_p1$ = -16 ; size = 12
_main PROC ; COMDAT
; 25 : {
;主函数的调用过程
;参见上面的注释{
push ebp
mov ebp, esp
sub esp, 252 ; 000000fcH
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-252]
mov ecx, 63 ; 0000003fH
mov eax, -858993460 ; ccccccccH
rep stosd
;参见上面的注释}
; 26 : Point3D p1(1,2,3);
push 3
push 2
push 1
lea ecx, DWORD PTR _p1$[ebp];ecx传递this指针,thiscall
call ??0Point3D@@QAE@HHH@Z ; Point3D::Point3D
; 27 : Point3D p2(4,5,6);
push 6
push 5
push 4
lea ecx, DWORD PTR _p2$[ebp]
call ??0Point3D@@QAE@HHH@Z ; Point3D::Point3D
; 28 : Point3D p3 = AddPoint3D(p1,p2);
sub esp, 12 ; 0000000cH 分配实参p1的空间并按值传递
mov eax, esp
mov ecx, DWORD PTR _p2$[ebp]
mov DWORD PTR [eax], ecx
mov edx, DWORD PTR _p2$[ebp+4]
mov DWORD PTR [eax+4], edx
mov ecx, DWORD PTR _p2$[ebp+8]
mov DWORD PTR [eax+8], ecx
sub esp, 12 ; 0000000cH 分配实参p2的空间并按值传递
mov edx, esp
mov eax, DWORD PTR _p1$[ebp]
mov DWORD PTR [edx], eax
mov ecx, DWORD PTR _p1$[ebp+4]
mov DWORD PTR [edx+4], ecx
mov eax, DWORD PTR _p1$[ebp+8]
mov DWORD PTR [edx+8], eax
lea ecx, DWORD PTR _p3$[ebp]
push ecx ;注意这个地方,p3的地址被保存在这里,也就是返回值的地址
call ?AddPoint3D@@YA?AUPoint3D@@U1@0@Z ; AddPoint3D
add esp, 28 ; 0000001cH 调用方清理参数堆栈28=12+12+4
; 29 : }
xor eax, eax
push edx
mov ecx, ebp
push eax
lea edx, DWORD PTR $LN7@main
call @_RTC_CheckStackVars@8
pop eax
pop edx
pop edi
pop esi
pop ebx
add esp, 252 ; 000000fcH
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
npad 3
$LN7@main:
DD 3
DD $LN6@main
$LN6@main:
DD -16 ; fffffff0H
DD 12 ; 0000000cH
DD $LN3@main
DD -36 ; ffffffdcH
DD 12 ; 0000000cH
DD $LN4@main
DD -56 ; ffffffc8H
DD 12 ; 0000000cH
DD $LN5@main
$LN5@main:
DB 112 ; 00000070H
DB 51 ; 00000033H
DB 0
$LN4@main:
DB 112 ; 00000070H
DB 50 ; 00000032H
DB 0
$LN3@main:
DB 112 ; 00000070H
DB 49 ; 00000031H
DB 0
_main ENDP
; Function compile flags: /Odtp /RTCsu /ZI
_TEXT ENDS
; COMDAT ??0Point3D@@QAE@HHH@Z
_TEXT SEGMENT
_this$ = -8 ; size = 4
_x$ = 8 ; size = 4
_y$ = 12 ; size = 4
_z$ = 16 ; size = 4
??0Point3D@@QAE@HHH@Z PROC ; Point3D::Point3D, COMDAT
; _this$ = ecx
; 11 : {}
push ebp
mov ebp, esp
sub esp, 204 ; 000000ccH
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-204]
mov ecx, 51 ; 00000033H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _x$[ebp]
mov DWORD PTR [eax], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _y$[ebp]
mov DWORD PTR [eax+4], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _z$[ebp]
mov DWORD PTR [eax+8], ecx
mov eax, DWORD PTR _this$[ebp]
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 12 ; 0000000cH
??0Point3D@@QAE@HHH@Z ENDP ; Point3D::Point3D
_TEXT ENDS
END
; Listing generated by Microsoft (R) Optimizing Compiler Version 14.00.50727.762
TITLE d:\project\CheckAsm\CheckAsm\CheckAsm.cpp
.686P
.XMM
include listing.inc
.model flat
INCLUDELIB MSVCRTD
INCLUDELIB OLDNAMES
PUBLIC ?AddPoint3D@@YA?AUPoint3D@@U1@0@Z ; AddPoint3D
EXTRN @_RTC_CheckStackVars@8:PROC
EXTRN __RTC_Shutdown:PROC
EXTRN __RTC_InitBase:PROC
; COMDAT rtc$TMZ
; File d:\project\checkasm\checkasm\checkasm.cpp
rtc$TMZ SEGMENT
__RTC_Shutdown.rtc$TMZ DD FLAT:__RTC_Shutdown
rtc$TMZ ENDS
; COMDAT rtc$IMZ
rtc$IMZ SEGMENT
__RTC_InitBase.rtc$IMZ DD FLAT:__RTC_InitBase
; Function compile flags: /Odtp /RTCsu /ZI
rtc$IMZ ENDS
; COMDAT ?AddPoint3D@@YA?AUPoint3D@@U1@0@Z
;上面一堆先不管
_TEXT SEGMENT;定义PE文件中的Text段
_p$ = -16 ; size = 12;
___$ReturnUdt$ = 8 ; size = 4
_p1$ = 12 ; size = 12
_p2$ = 24 ; size = 12
;上面4个符号是AddPoint3D这个方法中的局部变量、返回值和参数的偏移量(相对于ebp)
?AddPoint3D@@YA?AUPoint3D@@U1@0@Z PROC ; AddPoint3D, COMDAT AddPoint3D方法的定义开始,由于name mangling,名称变为了AddPoint3D@@YA?AUPoint3D@@U1@0@Z
; 15 : {
push ebp;保存caller的ebp到堆栈
mov ebp, esp;设置本方法的ebp。ebp在方法调用的开始阶段被设置,结束阶段被还原,调用过程中保持不变。ebp表示为本次调用所分配的堆栈桢的起始地址
sub esp, 212 ; 000000d4H esp减少一定的数量,表示为本次调用在栈上分配了一定的空间。让我们把>ebp && <= 现在的esp 的这段内存称之为locals
push ebx;保存现场的3个push
push esi
push edi
lea edi, DWORD PTR [ebp-212];把edi设置成刚才sub esp后esp的值,也就是locals的最低地址
mov ecx, 53 ; 00000035H 53 * 4 = 212, you see whats going on here?
mov eax, -858993460 ; ccccccccH 设置eax=cccccccch
rep stosd ;这是一个串操作指令,其意义是
;while(ecx)
;{
; *(int*)edi = eax;
; edi += sizeof(int);//这里就是4
; ecx--;
;}
;本指令和它上面的三条指令完成了把locals这段内存初始化成一片ccccccccccccccccccccc.
;当此指令执行完毕,edi == ebp
; 16 : Point3D p(p1);
mov eax, DWORD PTR _p1$[ebp]
mov DWORD PTR _p$[ebp], eax
mov ecx, DWORD PTR _p1$[ebp+4]
mov DWORD PTR _p$[ebp+4], ecx
mov edx, DWORD PTR _p1$[ebp+8]
mov DWORD PTR _p$[ebp+8], edx
;复制构造局部变量p。_p1$[ebp]的意思就是[ebp + _p1$],_p1$是在前面定义过的一个偏移量
; 17 : p.X += p2.X;
mov eax, DWORD PTR _p$[ebp]
add eax, DWORD PTR _p2$[ebp]
mov DWORD PTR _p$[ebp], eax
; 18 : p.Y += p2.Y;
mov eax, DWORD PTR _p$[ebp+4]
add eax, DWORD PTR _p2$[ebp+4]
mov DWORD PTR _p$[ebp+4], eax
; 19 : p.Z += p2.Z;
mov eax, DWORD PTR _p$[ebp+8]
add eax, DWORD PTR _p2$[ebp+8]
mov DWORD PTR _p$[ebp+8], eax
;这些很好理解
; 20 :
; 21 : return p;
mov eax, DWORD PTR ___$ReturnUdt$[ebp]
;ReturnUdt中的Udt表示user defined type
;这条指令的意思是,调用方在从ebp开始偏移量是___$ReturnUdt$的dword中保存的返回值应当存放的地址,把这个地址加载到eax中
;此时eax保存的是一个地址,这个地址实际上call中局部变量p3的地址
mov ecx, DWORD PTR _p$[ebp]
mov DWORD PTR [eax], ecx
mov edx, DWORD PTR _p$[ebp+4]
mov DWORD PTR [eax+4], edx
mov ecx, DWORD PTR _p$[ebp+8]
mov DWORD PTR [eax+8], ecx
;从局部变量p复制到call的局部变量p3中
mov eax, DWORD PTR ___$ReturnUdt$[ebp]
;在eax中保存返回值的地址
; 22 : }
;这一段{
push edx
mov ecx, ebp
push eax
lea edx, DWORD PTR $LN5@AddPoint3D
call @_RTC_CheckStackVars@8
pop eax
pop edx
;这一段}可以忽略掉,并不是我们程序逻辑的一部分
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0;函数返回,返回值的保存地址存放在eax中
npad 2
$LN5@AddPoint3D:
DD 1
DD $LN4@AddPoint3D
$LN4@AddPoint3D:
DD -16 ; fffffff0H
DD 12 ; 0000000cH
DD $LN3@AddPoint3D
$LN3@AddPoint3D:
DB 112 ; 00000070H
DB 0
?AddPoint3D@@YA?AUPoint3D@@U1@0@Z ENDP ; AddPoint3D
_TEXT ENDS
PUBLIC ??0Point3D@@QAE@HHH@Z ; Point3D::Point3D
PUBLIC _main
EXTRN __RTC_CheckEsp:PROC
; Function compile flags: /Odtp /RTCsu /ZI
; COMDAT _main
_TEXT SEGMENT
_p3$ = -56 ; size = 12
_p2$ = -36 ; size = 12
_p1$ = -16 ; size = 12
_main PROC ; COMDAT
; 25 : {
;主函数的调用过程
;参见上面的注释{
push ebp
mov ebp, esp
sub esp, 252 ; 000000fcH
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-252]
mov ecx, 63 ; 0000003fH
mov eax, -858993460 ; ccccccccH
rep stosd
;参见上面的注释}
; 26 : Point3D p1(1,2,3);
push 3
push 2
push 1
lea ecx, DWORD PTR _p1$[ebp];ecx传递this指针,thiscall
call ??0Point3D@@QAE@HHH@Z ; Point3D::Point3D
; 27 : Point3D p2(4,5,6);
push 6
push 5
push 4
lea ecx, DWORD PTR _p2$[ebp]
call ??0Point3D@@QAE@HHH@Z ; Point3D::Point3D
; 28 : Point3D p3 = AddPoint3D(p1,p2);
sub esp, 12 ; 0000000cH 分配实参p1的空间并按值传递
mov eax, esp
mov ecx, DWORD PTR _p2$[ebp]
mov DWORD PTR [eax], ecx
mov edx, DWORD PTR _p2$[ebp+4]
mov DWORD PTR [eax+4], edx
mov ecx, DWORD PTR _p2$[ebp+8]
mov DWORD PTR [eax+8], ecx
sub esp, 12 ; 0000000cH 分配实参p2的空间并按值传递
mov edx, esp
mov eax, DWORD PTR _p1$[ebp]
mov DWORD PTR [edx], eax
mov ecx, DWORD PTR _p1$[ebp+4]
mov DWORD PTR [edx+4], ecx
mov eax, DWORD PTR _p1$[ebp+8]
mov DWORD PTR [edx+8], eax
lea ecx, DWORD PTR _p3$[ebp]
push ecx ;注意这个地方,p3的地址被保存在这里,也就是返回值的地址
call ?AddPoint3D@@YA?AUPoint3D@@U1@0@Z ; AddPoint3D
add esp, 28 ; 0000001cH 调用方清理参数堆栈28=12+12+4
; 29 : }
xor eax, eax
push edx
mov ecx, ebp
push eax
lea edx, DWORD PTR $LN7@main
call @_RTC_CheckStackVars@8
pop eax
pop edx
pop edi
pop esi
pop ebx
add esp, 252 ; 000000fcH
cmp ebp, esp
call __RTC_CheckEsp
mov esp, ebp
pop ebp
ret 0
npad 3
$LN7@main:
DD 3
DD $LN6@main
$LN6@main:
DD -16 ; fffffff0H
DD 12 ; 0000000cH
DD $LN3@main
DD -36 ; ffffffdcH
DD 12 ; 0000000cH
DD $LN4@main
DD -56 ; ffffffc8H
DD 12 ; 0000000cH
DD $LN5@main
$LN5@main:
DB 112 ; 00000070H
DB 51 ; 00000033H
DB 0
$LN4@main:
DB 112 ; 00000070H
DB 50 ; 00000032H
DB 0
$LN3@main:
DB 112 ; 00000070H
DB 49 ; 00000031H
DB 0
_main ENDP
; Function compile flags: /Odtp /RTCsu /ZI
_TEXT ENDS
; COMDAT ??0Point3D@@QAE@HHH@Z
_TEXT SEGMENT
_this$ = -8 ; size = 4
_x$ = 8 ; size = 4
_y$ = 12 ; size = 4
_z$ = 16 ; size = 4
??0Point3D@@QAE@HHH@Z PROC ; Point3D::Point3D, COMDAT
; _this$ = ecx
; 11 : {}
push ebp
mov ebp, esp
sub esp, 204 ; 000000ccH
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-204]
mov ecx, 51 ; 00000033H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _x$[ebp]
mov DWORD PTR [eax], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _y$[ebp]
mov DWORD PTR [eax+4], ecx
mov eax, DWORD PTR _this$[ebp]
mov ecx, DWORD PTR _z$[ebp]
mov DWORD PTR [eax+8], ecx
mov eax, DWORD PTR _this$[ebp]
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 12 ; 0000000cH
??0Point3D@@QAE@HHH@Z ENDP ; Point3D::Point3D
_TEXT ENDS
END
That's it.