系统:centos 7,64位。64位基本使用寄存器存储函数参数,寄存器不够才入栈。
32位使用栈帧来作为传递的参数的保存位置,而64位使用寄存器,分别用rdi, rsi, rdx, rcx, r8, r9作为第1-6个参数,rax作为返回值。
用gdb调试,测试代码如下:test.c
#include
int add(int a, int b, int c)
{
return (a + b + c);
}
int main()
{
int sum = 0;
int a = 1;
int b = 2;
int c = 3;
sum = add(a, b ,c);
sum = sum + 1;
return 0;
}
编译
在编译时加上-g选项,那么用objdump反汇编时可以把C代码和汇编代码穿插起来显示,这样C代码和汇编代码的对应关系看得更清楚。
gcc test.c -g
objdump -dS a.out
汇编后的代码如下
00000000004004e9 :
int main()
{
4004e9: push %rbp ; rbp入栈,即将调用main()的函数的栈底地址放入栈中
4004ea: mov %rsp, %rbp ; 将rsp的值给rbp,此时rbp指向新的栈底
4004ed: sub $0x10, %rsp ; rsp - 0x10,为main函数开辟空间
int sum = 0;
4004f1: c7 45 fc 00
}
gdb跟踪,在main函数设置断点
看当前的汇编
默认的汇编语言编码是ATT模式。如果看不顺的话,可以手动修改为intel风格,依个人喜好,我个人更偏向intel风格
set disassembly-flavor intel
刚开始用到了rbp,rsp。那先重点关注rbp,rsp,cs,rip这四个寄存器
通过如下命令查看
info registers rbp rsp cs rip
或者
print $rbp
通过画图来分析栈里面当前的数据
1,设置栈底和栈顶
00000000004004e9 :
int main()
{
4004e9: push %rbp ; rbp入栈,即将调用main()的函数的栈底地址放入栈中
4004ea: mov %rsp, %rbp ; 将rsp的值给rbp,此时rbp指向新的栈底
4004ed: sub $0x10, %rsp ; rsp - 0x10,为main函数开辟空间
过程如图
来看下旧rbp里面的值,通过x /nfu addr来查看内存里面的值,如下
发现旧rbp为0x0,这里不做扩展,继续
2,局部变量入栈
汇编语句的单步调试(si 4),向前4步
代码解释
00000000004004e9 :
int main()
{
...
4004f1: mov DWORD PTR [rbp-0x4], 0x0 ; sum入栈
4004f8: mov DWORD PTR [rbp-0x8], 0x1 ; a入栈
4004ff: mov DWORD PTR [rbp-0xc], 0x2 ; b入栈
400506: mov DWORD PTR [rbp-0x10], 0x3 ; c入栈
...
此时如下图
3,被调函数的参数储存在寄存器中
00000000004004e9 :
int main()
{
...
mov edx, DWORD PTR [rbp-0x10]
mov ecx, DWORD PTR [rbp-0xc]
mov eax, DWORD PTR [rbp-0x8]
...
向前3步(si 3)
寄存器 | 值 |
edx | c(3) |
ecx | b(2) |
eax | a(1) |
00000000004004e9 :
int main()
{
...
mov esi, ecx
mov edi, eax
...
寄存器 | 值 |
esi | ecx(b:2) |
edi | eax(a:1) |
4,跳转函数
此时rip寄存器的值为:
执行call语句后
call语句分解为:
push rip;
jump ip;
看各个寄存器的值
看rsp的值为:0x7fff,ffff,e4b8,看入栈的返回地址的值; rip 为0x4004cd
即0x40051f正好对应于call下面的语句
5,执行函数,将main函数的栈底压栈,并将rsp的值给rbp,此时rbp指向新的栈底
00000000004004cd :
int add(int a, int b, int c)
{
4004cd: push rbp
4004ce: mov rbp, rsp
...
}
6,将局部变量压栈
00000000004004cd :
int add(int a, int b, int c)
{
...
4004d1: mov DWORD PTR [rbp-0x4], edi
4004d4: mov DWORD PTR [rbp-0x8], esi
4004d7: mov DWORD PTR [rbp-0xc], edx
...
}
7, 开始计算
00000000004004cd :
int add(int a, int b, int c)
{
...
4004da: mov eax, DWORD PTR [rbp-0x8]; eax = b = 1
4004dd: mov edx, DWORD PTR [rbp-0x4]; edx = a = 2
4004e0: add edx, eax; edx = eax + edx = 3;
4004e2: mov eax, DWORD PTR [rbp-0xc]; eax = c = 3
4004e5: add eax, edx; eax = eax + edx = 6, 即返回值保存在寄存器eax中
...
}
8,恢复rbp
各个寄存器的值
00000000004004cd :
int add(int a, int b, int c)
{
...
4004e7: pop rbp
9,回到main函数
00000000004004cd :
int add(int a, int b, int c)
{
...
4004e8: ret
ret的作用是: pop rip
10,返回值赋值
00000000004004e9 :
int main()
{
...
sum = add(a, b, c)
...
mov DWORD PTR [rbp-0x4], eax ; 将eax的值赋值给sum
sum = sum + 1;
add DWORD PTR [rbp-0x4], 0x1 ; sum += 1
return 0;
mov eax, 0x0 ; 将返回值0通过eax传递
...
11,执行leave
执行前
执行后
rbp = 0
rsp = 0x7fff,ffff,e4d8
12,执行ret,回到_libc_start_main函数里
页面更新:2024-02-13
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号