常见的寄存器
- rax、rbx、rcx、rdx、rsi、rdi、rbp、rsp
- r8、r9、r10、r11、r12、r13、r14、r15
寄存器的具体用途
- rax、rdx常作为函数返回值使用
- rdi、rsi、rdx、rcx、r8、r9等寄存器常用于存放函数参数
- rsp、rbp用于栈操作
- rip作为指令指针,存储着CPU下一条要执行的指令的地址,一旦CPU读取一条指令,rip会自动指向下一条指令(存储下一条指令的地址)
寄存器访问和使用
- 首先AT&T汇编有一个特点就是寄存器的访问和使用前面要加
%
,而立即数(就是具体的数字)访问要加$
- 拿寄存器rax来举例子,如果我遇到rax,我只想使用其中的一半(或者为了兼容32位的),常常会将rax取出来一半,然后叫
eax
,同样如果我还是再取出一半的一半使用,那我们就用ax
,同理还有ah
,al
,都是为了拿出ax寄存器的一部分来使用
指令
- mov 指令
perl
movl $123, %eax # 立即数 → 寄存器
movl %eax, %ebx # 寄存器 → 寄存器
movl (%eax), %ebx # 内存 → 寄存器
movl %eax, (%ebx) # 寄存器 → 内存
movl $123, (%eax) # 立即数 → 内存
另外可以看到的mov后面的l是代表的什么意思呢?l代表操作的是32位的数据。 b是byte的意思,w是word就是双字节的意思,而l是long单词的缩写。而q是Quad缩写,意思是四个字。
bash
movb # 传送字节 (8位)
movw # 传送字 (16位)
movl # 传送双字 (32位)
movq # 传送四字 (64位)
2.add 加法指令
perl
addl $10, %eax # %eax = %eax + 10
addl %ebx, %eax # %eax = %eax + %ebx
addl (%ebx), %eax # %eax = %eax + [%ebx]
3.sub 减法指令
perl
subl $10, %eax # %eax = %eax - 10
subl %ebx, %eax # %eax = %eax - %ebx
4.乘法和除法
perl
imull %ebx, %eax # %eax = %eax * %ebx
idivl %ebx # %eax = %eax / %ebx (商)
5.自增自减
perl
incl %eax # %eax++
decl %eax # %eax--
- 位运算
perl
andl $0xFF, %eax # %eax = %eax & 0xFF
orl $0xFF, %eax # %eax = %eax | 0xFF
xorl %ebx, %eax # %eax = %eax ^ %ebx
notl %eax # %eax = ~%eax
7.移位指令
perl
shll $2, %eax # 逻辑左移 2 位
shrl $2, %eax # 逻辑右移 2 位
sarl $2, %eax # 算术右移 2 位
roll $2, %eax # 循环左移 2 位
rorl $2, %eax # 循环右移 2 位
8.比较指令
perl
cmpl $10, %eax # 比较 %eax 和 10
cmpl %ebx, %eax # 比较 %eax 和 %ebx
testl %eax, %eax # 测试 %eax 是否为零
9.条件跳转
bash
je label # 相等时跳转 (ZF=1)
jne label # 不相等时跳转 (ZF=0)
jl label # 小于时跳转 (SF≠OF)
jle label # 小于等于时跳转 (ZF=1 或 SF≠OF)
jg label # 大于时跳转 (ZF=0 且 SF=OF)
jge label # 大于等于时跳转 (SF=OF)
10.无条件的跳转
r
jmp label # 无条件跳转
call function # 调用函数
ret # 返回
11.push 和pop指令
perl
pushl %eax # 压栈
popl %eax # 出栈
pushl $123 # 压入立即数
寻址相关的指令
- 立即数寻址
perl
movl $123, %eax # 将立即数 123 加载到 %eax
movl $0xABCD, %ebx # 将十六进制立即数加载到 %ebx
movl $'A', %ecx # 将字符 'A' 的 ASCII 码加载到 %ecx
2.寄存器寻址
perl
movl %eax, %ebx # 将 %eax 的值复制到 %ebx
addl %ecx, %edx # %edx = %edx + %ecx
xorl %esi, %edi # %edi = %edi ^ %esi
3.直接寻址 (Direct Addressing)
perl
movl variable, %eax # 将变量 variable 的值加载到 %eax
movl %eax, variable # 将 %eax 的值存储到变量 variable
4.间接寻址
perl
movl (%eax), %ebx # 将 %eax 指向的内存地址的值加载到 %ebx
movl %ebx, (%eax) # 将 %ebx 的值存储到 %eax 指向的内存地址
5.基址偏移寻址
perl
movl 4(%eax), %ebx # %ebx = [%eax + 4]
movl -8(%eax), %ebx # %ebx = [%eax - 8]
movl 0x100(%eax), %ebx # %ebx = [%eax + 0x100]
示例
perl
movl 4(%eax), %ebx # %ebx = [%eax + 4]
movl -8(%eax), %ebx # %ebx = [%eax - 8]
movl 0x100(%eax), %ebx # %ebx = [%eax + 0x100]
6.基址变址寻址
perl
movl (%eax,%ebx), %ecx # %ecx = [%eax + %ebx]
movl (%esi,%edi), %eax # %eax = [%esi + %edi]
数组访问示例:
perl
movl $array, %eax # 数组基地址
movl $2, %ebx # 索引 (第3个元素)
movl (%eax,%ebx,4), %ecx # %ecx = array[2] (假设是int数组,每个元素4字节)
- 比例寻址
perl
movl (%eax,%ebx,1), %ecx # %ecx = [%eax + %ebx * 1]
movl (%eax,%ebx,2), %ecx # %ecx = [%eax + %ebx * 2]
movl (%eax,%ebx,4), %ecx # %ecx = [%eax + %ebx * 4]
movl (%eax,%ebx,8), %ecx # %ecx = [%eax + %ebx * 8]
不同数据类型的数组访问:
perl
# char 数组 (1字节)
movl $char_array, %eax
movl $5, %ebx
movb (%eax,%ebx,1), %cl # %cl = char_array[5]
# short 数组 (2字节)
movl $short_array, %eax
movl $3, %ebx
movw (%eax,%ebx,2), %cx # %cx = short_array[3]
# int 数组 (4字节)
movl $int_array, %eax
movl $2, %ebx
movl (%eax,%ebx,4), %ecx # %ecx = int_array[2]
# double 数组 (8字节)
movl $double_array, %eax
movl $1, %ebx
fldl (%eax,%ebx,8) # 加载 double_array[1]
- 基址变址偏移寻址
perl
movl 4(%eax,%ebx,4), %ecx # %ecx = [%eax + %ebx * 4 + 4]
movl -8(%esi,%edi,2), %eax # %eax = [%esi + %edi * 2 - 8]
总结:立即数 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 ( a , b ,立即数 1(a,b,立即数 </math>1(a,b,立即数2)表示的是 b * 立即数2 + a - 立即数1 ,然后获取的值作为地址,去找这个地址的值
AT&T中重要规律
- 内存地址格式为:0x4bdc(%rip),一般是全局变量,静态变量,字符串字面量,常量数组,全局区(数据段)
ini
// C 代码
int global_var = 42;
static int static_var = 100;
int main() {
// 这些变量会被编译为 0x4bdc(%rip) 格式的地址
int x = global_var;
int y = static_var;
}
-
内存地址格式为:-0x78(%rbp),一般是局部变量,栈空间
-
内存地址格式为:0x10(%rax),一般是堆空间,rax是通用寄存器,通常用存储指针值。
c
// C 代码
struct Person {
int id;
char name[20];
int age;
};
int main() {
struct Person *p = malloc(sizeof(struct Person));
// 这些访问会被编译为 0x10(%rax) 格式
p->id = 1; // 偏移 0
strcpy(p->name, "John"); // 偏移 4
p->age = 25; // 偏移 24 (0x18)
}