函数调用栈
1、程序源代码:
[xuanmiao@localhost Practice]$ cat main.c
#include <stdio.h>
int plus(int a, int b)
{
int c = a+b;
return c;
}
int main()
{
int a = 1, b = 2;
int c = 0;
c = plus(a,b);
return 0;
}
2、编译
[xuanmiao@localhost Practice]$ gcc main.c -o main -g
[xuanmiao@localhost Practice]$ ls
main main.c
3、生成汇编代码(此处只取main和plus函数部分)
汇编代码(只取main和plus函数)
00000000004004ed <plus>:
#include <stdio.h>
int plus(int a, int b)
{
4004ed: 55 push %rbp
4004ee: 48 89 e5 mov %rsp,%rbp
4004f1: 89 7d ec mov %edi,-0x14(%rbp)
4004f4: 89 75 e8 mov %esi,-0x18(%rbp)
int c = a+b;
4004f7: 8b 45 e8 mov -0x18(%rbp),%eax
4004fa: 8b 55 ec mov -0x14(%rbp),%edx
4004fd: 01 d0 add %edx,%eax
4004ff: 89 45 fc mov %eax,-0x4(%rbp)
return c;
400502: 8b 45 fc mov -0x4(%rbp),%eax
}
400505: 5d pop %rbp
400506: c3 retq
0000000000400507 <main>:
int main()
{
400507: 55 push %rbp
400508: 48 89 e5 mov %rsp,%rbp
40050b: 48 83 ec 10 sub $0x10,%rsp
int a = 1, b = 2;
40050f: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp)
400516: c7 45 f8 02 00 00 00 movl $0x2,-0x8(%rbp)
int c = 0;
40051d: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp)
c = plus(a,b);
400524: 8b 55 f8 mov -0x8(%rbp),%edx
400527: 8b 45 fc mov -0x4(%rbp),%eax
40052a: 89 d6 mov %edx,%esi
40052c: 89 c7 mov %eax,%edi
40052e: e8 ba ff ff ff callq 4004ed <plus>
400533: 89 45 f4 mov %eax,-0xc(%rbp)
return 0;
400536: b8 00 00 00 00 mov $0x0,%eax
}
40053b: c9 leaveq
40053c: c3 retq
40053d: 0f 1f 00 nopl (%rax)
4、进入gdb
[xuanmiao@localhost Practice]$ gdb main
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/xuanmiao/Practice/main...done.
(gdb) list
2
3 int plus(int a, int b)
4 {
5 int c = a+b;
6 return c;
7 }
8
9 int main()
10 {
11 int a = 1, b = 2;
(gdb)
12 int c = 0;
13 c = plus(a,b);
14 return 0;
15 }
(gdb)
5、断到main函数
(gdb) b main
Breakpoint 1 at 0x40050f: file main.c, line 11.
(gdb) run
Starting program: /home/xuanmiao/Practice/main
Breakpoint 1, main () at main.c:11
11 int a = 1, b = 2;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-317.el7.x86_64
6、wi命令进入调试页面
7、bt查看调用栈
(gdb) bt
#0 main () at main.c:11
(gdb) bt
#0 main () at main.c:13
(gdb) s
plus (a=1, b=2) at main.c:5
(gdb) bt
#0 plus (a=1, b=2) at main.c:5
#1 0x0000000000400533 in main () at main.c:13
这里有两层调用栈:
-
#0
表示栈的最内层,也就是当前正在执行的函数。在这里,它是plus
函数,接收两个参数a
和b
,它们的值分别是 1 和 2。这个函数调用发生在源文件main.c
的第 5 行。 -
#1
表示调用栈的上一层,这里是main
函数。main
函数是程序的入口点,它调用了plus
函数。这个调用发生在main.c
文件的第 13 行。
8、main函数和plus函数 汇编代码的解释
int main()
{
400507: 55 push %rbp (将%rbp压栈:400507是指令在内存中的地址,55表示压栈指令,rbp是基址寄存器 )
400508: 48 89 e5 mov %rsp,%rbp (将栈指针寄存器 %rsp 的值赋给基指针寄存器 %rbp)
40050b: 48 83 ec 10 sub $0x10,%rsp ( 分配 16字节栈空间,%rsp = %rsp-16)
int a = 1, b = 2;
40050f: c7 45 fc 01 00 00 00 movl $0x1,-0x4(%rbp) ( -0x4(%rbp) 是 a,赋值为1 )
400516: c7 45 f8 02 00 00 00 movl $0x2,-0x8(%rbp)( -0x8(%rbp) 是 b,赋值为 2 )
int c = 0;
40051d: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp) ( -0xc(%rbp) 是 c,赋值为 0 )
c = plus(a,b);
400524: 8b 55 f8 mov -0x8(%rbp),%edx ( 将b的值赋值到%edx寄存器中 )
400527: 8b 45 fc mov -0x4(%rbp),%eax ( 将a的值赋值到%eax寄存器中 )
40052a: 89 d6 mov %edx,%esi ( plus函数传递参数b )
40052c: 89 c7 mov %eax,%edi ( plus函数传递参数a )
40052e: e8 ba ff ff ff callq 4004ed <plus> ( 调用plus函数 )
400533: 89 45 f4 mov %eax,-0xc(%rbp) ( 将plus返回值赋给c )
return 0;
400536: b8 00 00 00 00 mov $0x0,%eax ( 将main返回值0放到寄存器eax )
}
int plus(int a, int b)
{
4004ed: 55 push %rbp (将%rbp压栈 )
4004ee: 48 89 e5 mov %rsp,%rbp (将栈指针寄存器 %rsp 的值赋给基指针寄存器 %rbp)
4004f1: 89 7d ec mov %edi,-0x14(%rbp) (将edi中参数a的值赋给 -0x14(%rbp))
4004f4: 89 75 e8 mov %esi,-0x18(%rbp) (将esi中参数b的值赋给 -0x18(%rbp))
int c = a+b;
4004f7: 8b 45 e8 mov -0x18(%rbp),%eax ( 将b赋值给%eax )
4004fa: 8b 55 ec mov -0x14(%rbp),%edx ( 将a赋值给%edx )
4004fd: 01 d0 add %edx,%eax ( 累加 )
4004ff: 89 45 fc mov %eax,-0x4(%rbp) ( 将计算值放到-0x4(%rbp) )
return c;
400502: 8b 45 fc mov -0x4(%rbp),%eax ( 将-0x4(%rbp) 赋值给%eax ,函数的返回值通常存储在 %eax 寄存器)
}
400505: 5d pop %rbp (出栈,pop %rbp 指令来恢复原始的栈指针)
400506: c3 retq (retq 指令会从栈中弹出调用者的指令指针(即程序计数器 %rip 的值),并将控制流转移到这个地址,继续执行调用者的代码。)
1、函数调用时栈是如何变化的?-云社区-华为云 (huaweicloud.com)
2、函数的调用过程(栈帧) - 滴巴戈 - 博客园 (cnblogs.com)
3、C函数调用过程原理及函数栈帧分析 - stardsd - 博客园 (cnblogs.com)
4、栈、函数调用与系统调用 - 小夕nike - 博客园 (cnblogs.com)
5、函数调用栈简单认识(美化版) - alexlance - 博客园 (cnblogs.com)
6、C/C++函数调用过程中栈的变化 - cuglzf - 博客园 (cnblogs.com)
7、深入浅出CPU眼中的函数调用&栈溢出攻击 - CuriosityWang - 博客园 (cnblogs.com)
8、函数调用栈与缓冲区溢出 - chzhyang - 博客园 (cnblogs.com)
9、函数调用栈的一些简单认识 - alexlance - 博客园 (cnblogs.com)
10、函数调用栈 - 小坤学习园 - 博客园 (cnblogs.com)
11、linux c 程序函数调用栈帧分析 - tjyuanxi - 博客园 (cnblogs.com)
12、C语言中函数调用方式与栈原理 - 影随风动91 - 博客园 (cnblogs.com)
13、C语言的函数调用过程(栈帧的创建与销毁) - 龙跃十二 - 博客园 (cnblogs.com)
14、Linux 中的各种栈 - BabyMelvin - 博客园 (cnblogs.com)