一、基础了解
对于系统工程师来说,汇编属于一种基本功,应长期关注。在一些性能分析、问题定位时,有时需要读汇编代码,在学习linux内核过程中,也需要读汇编代码。
所以先以x86为例,学习一个最简单的c语言程序对应的汇编。
基础知识介绍:
- 首先了解一下x86处理器拥有的寄存器
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<stdio.h>
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 <main>:
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 <test> 调用test函数
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 <test>:
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个八字节的数。