函数栈帧(用汇编来剖析)

这次讲解一下C++函数调用,学了这么久C语言,肯定听说过栈(数据结构啊,地址空间的栈啊之类的),函数调用就和栈密切相关。

因为地址空间内的栈是从高地址向低地址生长的,也就是说压栈顺序靠后的反而地址比较低,栈底的地址高于栈顶的地址,下面贴上一段测试代码

#include<stdio.h>                                                               

#include<stdlib.h>

void bug()
{
    printf("haha I ma a bug!!");
    exit(100);
}
int func(int x, int y)
{
    int *p = &x;
    p--;
    *p = (int)bug;
    printf("x:%d,y:%d\n", x, y);
    int c = 0xcccc;
    return c;
}

int main()
{

printf("I am main\n");
    int a = 0xaaaa;
    int b = 0xbbbb;
    func(a, b);
    printf("I should run here\n");
    return 0;
}

这段代码的运行结果,并没有执行main函数的第二个printf,而是跑到了bug函数中执行,这是因为我修改了函数栈帧中的返回地址部分

函数栈帧(用汇编来剖析)

本来是打算通过linux系统来看的,但是CentOS7的栈帧实现似乎有些不同,同样的代码在centos7上面跑不通。

下面是反汇编

int main()
{
00A118E0  push        ebp 
00A118E1  mov        ebp,esp 
00A118E3  sub        esp,0D8h 
00A118E9  push        ebx 
00A118EA  push        esi 
00A118EB  push        edi 
00A118EC  lea        edi,[ebp-0D8h] 
00A118F2  mov        ecx,36h 
00A118F7  mov        eax,0CCCCCCCCh 
00A118FC  rep stos    dword ptr es:[edi] 

printf("I am main\n");
00A118FE  push        offset string "I am main\n" (0A16CF0h) 
00A11903  call        _printf (0A1132Ah) 
00A11908  add        esp,4 
    int a = 0xaaaa;
00A1190B  mov        dword ptr [a],0AAAAh 
    int b = 0xbbbb;
00A11912  mov        dword ptr [b],0BBBBh 
    func(a, b);
00A11919  mov        eax,dword ptr [b] 
00A1191C  push        eax 
00A1191D  mov        ecx,dword ptr [a] 
00A11920  push        ecx 
00A11921  call        func (0A11366h) 
00A11926  add        esp,8 
    printf("I should run here\n");
00A11929  push        offset string "I should run here\n" (0A16CFCh) 
00A1192E  call        _printf (0A1132Ah) 
00A11933  add        esp,4 
    return 0;
00A11936  xor        eax,eax 
}

因为main函数本身真的是个函数!所以在执行我们编写的程序之前操作系统需要保存当前它运行的状态,就跟函数调用很类似

 1 00A118E0 push ebp    这句话就是把操作系统的状态压栈

2 00A118E1 mov ebp,esp    然后把栈底指针挪到新的位置

3 00A118E3 sub esp,0D8h   扩展新的栈帧,你总不能让新的栈底和栈顶挨在一起吧? 

过程图我会在讲到func函数的时候给出来,更容易理解,之后的push之类的就是为了保存现场和执行前准备

1 printf("I am main\n"); 2 00A118FE push offset string "I am main\n" (0A16CF0h) 3 00A11903 call _printf (0A1132Ah) 4 00A11908 add esp,4

这部分就是调用printf的系统调用,因为库函数更多是对操作系统调用的再一次调用(封装?的说法也可以),因为我不是很懂这部分,也就不详细解释其中_printf的系统调用究竟怎么工作了

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/de7d63afeafdd075c8461624b8ec8183.html