为了防止出现不必要的代码影响汇编语言的查看,所以程序中不使用任何库函数,以保持汇编代码的简洁。
这里所使用的汇编是VC的MASM。
默认函数调用方式__cdecl
int add(int a, int b) {
return a + b;
}
int main() {
int a = 1, b = 2;
return add(a,b);
}
对应汇编代码:
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.10.25019.0
; 伪指令,如指定处理器指令,存储模型等
TITLE D:\C&C++\CppLearn\CLang\FunctionStack.c
.686P
.XMM
include listing.inc
.model flat
; 导入必要的静态库,如C语言运行时
INCLUDELIB MSVCRTD
INCLUDELIB OLDNAMES
; 所有函数过程的声明
PUBLIC _add
PUBLIC _main
EXTRN __RTC_CheckEsp:PROC
EXTRN __RTC_InitBase:PROC
EXTRN __RTC_Shutdown:PROC
; COMDAT rtc$TMZ
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
rtc$IMZ ENDS
_TEXT SEGMENT
; 局部变量的偏移地址
_b$ = -20 ; size = 4
_a$ = -8 ; size = 4
; main函数过程定义
_main PROC ; COMDAT
; 函数调用前必要的初始化,如寄存器入栈以保存CPU运行现场
push ebp ; 保存原来的基栈指针
mov ebp, esp ; 将原来的栈顶指针作为新的基栈指针
sub esp, 216 ; 000000d8H,为堆栈分配内存
push ebx
push esi
push edi
; 对堆栈进行初始化
lea edi, DWORD PTR [ebp-216]; 获取堆栈地址存入edi寄存器中
mov ecx, 54 ; 00000036H
mov eax, -858993460 ; ccccccccH
rep stosd ; rep指令表示重复执行后面的指令,重复次数为ecx中的值
; stosd表示使用eax中的值对es:[edi]指向的地址进行初始化,单位为dword
; 所以36H * 4 = d8H
; int a = 1, b = 2; 变量赋值
mov DWORD PTR _a$[ebp], 1
mov DWORD PTR _b$[ebp], 2
; return add(a,b);
; 可以看出函数调用的入栈顺序:从右到左
mov eax, DWORD PTR _b$[ebp]
push eax ;函数参数入栈
mov ecx, DWORD PTR _a$[ebp]
push ecx ;函数参数入栈
call _add ;调用add函数
add esp, 8 ;直接还原栈顶指针,不需要出栈数据
; 函数执行完成后,寄存器出栈,恢复执行现场
pop edi
pop esi
pop ebx
add esp, 216 ; 000000d8H,回收堆栈内存
cmp ebp, esp ; 比较基栈指针与栈顶指针是否相等
call __RTC_CheckEsp ; 堆栈检测,防止栈溢出
mov esp, ebp ; 恢复栈顶指针
pop ebp ; 恢复基栈指针
ret 0
_main ENDP
_TEXT ENDS
_TEXT SEGMENT
; 局部变量的偏移地址
_a$ = 8