C语言的汇编表示
函数
代码如下:
1 | int plus(int x, int y) { |
下断点的时候要在函数调用前,否则会直接跳过函数执行过程。
反汇编:

在执行CALL指令后首先会为函数分配一个堆栈空间(为的是EBP寻址)。具体过程是,先保存当前ESP的值到EBP,然后提升ESP,这里提升了0XCC个字节。由于函数执行过程中可能会用到寄存器,所以将EBX、ESI、EDI三个寄存器压入栈中。
LEA EDI,[EBP-0C]一直到REP STOS DWORD PTR ES:[EDI]这些指令是为了防止缓冲区溢出,具体过程是,从新保存的EBP一直往上0XC个字节为缓冲区,将EAX寄存器填充为0XCCCCCCCC,循环3次写入到缓冲区中
至于为什么缓冲区被分配为0XC个字节,因为我用的编译器做过优化,3个变量一共0XC字节,如果直接返回结果return x+y,编译器会生成这条指令:XOR EXC,EXC,将ECX寄存器置0,也就是不需要缓冲区。
然后通过EBP寻址开始执行函数命令,EBP+8是因为除了CALL指令压入栈的地址外,还有EBP本来的地址,将运算结果写入到EAX寄存器。
最后按反顺序依次还原堆栈空间,弹出三个寄存器的值,将ESP和EBP还原,函数返回后将ESP+8,调用结束。
堆栈图如下:

变量
全局变量
全局变量全部存放在静态存储区(默认值为0),在编译时就确定了地址和长度,在程序开始执行时给全局变量分配存储区,程序行完毕就释放。
局部变量
局部变量储存在堆栈中,只有在函数执行时才会随机分配地址,这个地址取决于函数调用时的堆栈指针寄存器的值
1 | push ebp |
参数是在调用函数前被压栈的,局部变量储存在栈空间的缓冲区中,如上面的堆栈图一样,在ESP提升之后,将EBP往上的一段空间作为缓冲区,并填充CC
MOV DWORD PTR [EBP-8],EAX这句,将EAX寄存器的值写入到EBP-8,这个EBP-8就是上面局部变量result的地址(从EBP开始往上是缓冲区,用于储存局部变量)
在调用结束时ESP和EBP会恢复成以前的值,这也是为什么在C语言中局部变量不能直接使用的原因(值是未定义的)
返回值
大多数C/C++编译器会把返回值写入到EAX寄存器中
例如:
MOV EAX,DWORD PTR [EBP-8]
返回后使用返回值
1 | push 2 |
以上代码中使用的外平栈,在调用完毕后再将ESP+8,然后将EAX的值写入到局部变量中, [EBP-8]就是main函数局部变量的地址
