一、基础了解
对于系统工程师来说,汇编属于一种基本功,应长期关注。在一些性能分析、问题定位时,有时需要读汇编代码,在学习linux内核过程中,也需要读汇编代码。
所以先以x86为例,学习一个最简单的c语言程序对应的汇编。
基础知识介绍:
x86_64寄存器如下所示:
0-63 |
0-31 |
0-15 |
8-15 |
0-7 |
使用惯例 |
%rax |
%eax |
%ax |
%ah |
%al |
保存返回值 |
%rbx |
%ebx |
%bx |
%bh |
%bl |
被调用者保存 |
%rcx |
%ecx |
%cx |
%ch |
%cl |
第4个参数 |
%rdx |
%edx |
%dx |
%dh |
%dl |
第3个参数 |
%rsi |
%esi |
%si |
无 |
%sil |
第2个参数 |
%rdi |
%edi |
%di |
无 |
%dil |
第1个参数 |
%rbp |
%ebp |
%bp |
无 |
%bpl |
被调用者保存 |
%rsp |
%esp |
%sp |
无 |
%spl |
栈指针 |
%r8 |
%r8d |
%r8w |
无 |
%r8b |
第5个参数 |
%r9 |
%r9d |
%r9w |
无 |
%r9b |
第6个参数 |
%r10 |
%r10d |
%r10w |
无 |
%r10b |
调用者保存 |
%r11 |
%r11d |
%r11w |
无 |
%r11b |
调用者保存 |
%r12 |
%r12d |
%r12w |
无 |
%r12b |
被调用者保存 |
%r13 |
%r13d |
%r13w |
无 |
%r13b |
被调用者保存 |
%r14 |
%r14d |
%r14w |
无 |
%r14b |
被调用者保存 |
%r15 |
%r15d |
%r15w |
无 |
%r15b |
被调用者保存 |
2.函数传参
函数调用参数传递时,%rdi,%rsi,%rdx,%rcx,%r8,%r9用于存储函数调用第1,2,3,4,5,6个参数,如果函数有那么多参数的话,如果超过6个参数,超出来的参数保存在栈中。
3.栈的伸缩
%rsp用于保存栈顶指针,由于栈的伸长方向一般是从高地址像低地址扩展,当需要扩充栈的时候,rsp值就减小,如 sub $0x60,%rsp , 当栈需要收缩的时候,rsp值就增大
二、c程序与汇编对应
C语言如下:
#include
char test(int a)
{
int b = 2;
b = b + a;
return a;
}
int main()
{
int i ,buf[20];
i++;
i = test(i);
return 0;
}
编译上面的简单c语言,使用objdump -d 反编译可执行文件后得到汇编如下,
00000000004004e6
4004e6: 55 push %rbp rbp压栈 rsp - 8
4004e7: 48 89 e5 mov %rsp,%rbp 重新开始一段rbp
4004ea: 48 83 ec 60 sub $0x60,%rsp 扩栈用于存局部变量
4004ee: 83 45 fc 01 addl $0x1,-0x4(%rbp) 执行 i++
4004f2: 8b 45 fc mov -0x4(%rbp),%eax
4004f5: 89 c7 mov %eax,%edi edi传递第参数i
4004f7: e8 d1 ff ff ff callq 4004cd
4004fc: 0f be c0 movsbl %al,%eax
4004ff: 89 45 fc mov %eax,-0x4(%rbp) 返回值放到i中
400502: b8 00 00 00 00 mov $0x0,%eax return 0准备
400507: c9 leaveq
/* leaveq指令将rbp寄存器的内容复制到rsp寄存器中,以释放分配给该过程的所有堆栈空间。然后,它从堆栈恢复rbp寄存器的旧值。*/
400508: c3 retq
400509: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
test函数汇编如下:
00000000004004cd
4004cd: 55 push %rbp rbp压栈,同时rsp - 8
4004ce: 48 89 e5 mov %rsp,%rbp 重新开始一段rbp
4004d1: 89 7d ec mov %edi,-0x14(%rbp)
4004d4: c7 45 fc 02 00 00 00 movl $0x2,-0x4(%rbp)
4004db: 8b 45 ec mov -0x14(%rbp),%eax
4004de: 01 45 fc add %eax,-0x4(%rbp) 传入的参数值 + 2
4004e1: 8b 45 ec mov -0x14(%rbp),%eax eax用于存放返回值
4004e4: 5d pop %rbp 出栈rbp rsp + 8
4004e5: c3 retq
三、gdb调试体会程序
使用gdb调试可执行程序 gdb ./a.out -tui
设置实时显示寄存器信息layout regs
设置以后的程序自动反汇编set disassemble-next-line on
设置断点后进行单步调试
si 、ni 单步调试汇编级 s 、n 调试c语言级
查看内存指令x使用:
x/
x/ 后可加参数,n、f、u是可选的参数。
x/nfu n代表显示内存的长度,f 表示显示的格式,如果地址是字符串,则f 为s ,u表示显示的地址的字节数,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。
如x/8g addr 表示从addr开始显示8个八字节的数。