开发中经常使用指针访问变量修改变量值, 那么编译器是怎么翻译指针的 或者说指针在汇编层面到底是什么
指针的分析离不开这两个指令,简单看下mov指令
GNU汇编器输出AT&T汇编时为 mov 指令添加了一个维度,在其中必须声明要传送的数据元素的长度
因此,指令就变成了如下:
movx
其中 x 可以是下面的字符:
1,q用于64位的4字值
2,l用于32位的长字值
3,w用于16位的字值
4,b用于8位的字节值
- (void)asm_point { int a = 6; }
GUN汇编器输出AT&T汇编 和 Intel 汇编的语法顺序是相反
YangASM`-[ViewController asm_point]: 0x10ed88ee0 <+0>: pushq %rbp 0x10ed88ee1 <+1>: movq %rsp, %rbp 0x10ed88ee4 <+4>: movq %rdi, -0x8(%rbp) 0x10ed88ee8 <+8>: movq %rsi, -0x10(%rbp) 0x10ed88eec <+12>: movl $0x6, -0x14(%rbp) -> 0x10ed88ef3 <+19>: popq %rbp 0x10ed88ef4 <+20>: retq
(lldb) p &a (int *) $4 = 0x00007ffee0e7503c (lldb) register read rbp rbp = 0x00007ffee0e75050 (lldb) p/x 0x00007ffee0e75050-0x8 (long) $5 = 0x00007ffee0e75048 (lldb) p/x 0x00007ffee0e75050-0x10 (long) $6 = 0x00007ffee0e75040 (lldb) p/x 0x00007ffee0e75050-0x14 (long) $7 = 0x00007ffee0e7503c (lldb)
```c YangASM`-[ViewController asm_point]: // rbp = 0x00007ffee0e75050 // 开启asm_point 函数的函数栈 0x10ed88ee0 <+0>: pushq %rbp 0x10ed88ee1 <+1>: movq %rsp, %rbp // 上面我们得到 -0x8(%rbp) p/x 0x00007ffee0e75050-0x8 = 0x00007ffee0e75048 // 从 movq 可以看出来 需要霸占内存8个字节的区间 // 那么这条指令就是 rdi的值存放到 从0x00007ffee0e75048开始 往下的8个字节内 0x10ed88ee4 <+4>: movq %rdi, -0x8(%rbp) // rsi的值存放到 从0x00007ffee0e75040开始 往后的8个字节内 0x10ed88ee8 <+8>: movq %rsi, -0x10(%rbp) // int a = 6 // 把6存放到 从0x00007ffee0e7503c往下4个字节的区间内 // movl 看到需要霸占4个字节区间 int类型 4个字节 0x10ed88eec <+12>: movl $0x6, -0x14(%rbp) // 回收asm_point函数的函数栈空间 -> 0x10ed88ef3 <+19>: popq %rbp 0x10ed88ef4 <+20>: retq
上面的汇编有movl 和 movq
也就是说 在 AT&T汇编中 mov指令都是以movx的形式出现
上面的汇编中 movq %rdi, -0x8(%rbp) 还有内括号 还有 - 号
分别含义如下:
movl %ebx, %edi
ebx寄存器中的值 加载到edi寄存器中
movl %ebx, (%edi)
edi加上内括号 就是把 ebx寄存器中的值传递给 edi寄存器中包含的内存地址
movl %ebx, 4(%edi)
把edx寄存器中的值存放到edi寄存器指向的位置之后的4个字节的内存位置中
也可以把它存放到相反的方向
movl %ebx, -4(%edi)
把edx寄存器中的值存放到edi寄存器指向的位置之前的4个字节的内存位置中
上面的汇编代码 绘制出内存布局如下
leq 后面跟地址 直接把地址 给寄存器
mov 后面跟地址 把地址上的数据 给寄存器
- (void)asm_point { int a = 6; int *p = a; }
YangASM`-[ViewController asm_point]: 0x10da42ee0 <+0>: pushq %rbp 0x10da42ee1 <+1>: movq %rsp, %rbp // 安排 rdi rsi 0x10da42ee4 <+4>: movq %rdi, -0x8(%rbp) 0x10da42ee8 <+8>: movq %rsi, -0x10(%rbp) // int a = 6 6存入 -0x14(%rbp) 0x10da42eec <+12>: movl $0x6, -0x14(%rbp) // 讲-0x14(%rbp)这个地址 赋给rax 0x10da42ef3 <+19>: leaq -0x14(%rbp), %rax // rax里面的值 存入-0x20(%rbp) // rax里面的值 是一个地址 -0x14(%rbp) 0x10da42ef7 <+23>: movq %rax, -0x20(%rbp) -> 0x10da42efb <+27>: popq %rbp 0x10da42efc <+28>: retq
了解完 leq 和 mov 我们看指针修改
- (void)asm_point { int a = 6; int *p = &a; *p = 12; }
YangASM`-[ViewController asm_point]: 0x10f5e4ed0 <+0>: pushq %rbp 0x10f5e4ed1 <+1>: movq %rsp, %rbp 0x10f5e4ed4 <+4>: movq %rdi, -0x8(%rbp) 0x10f5e4ed8 <+8>: movq %rsi, -0x10(%rbp) 0x10f5e4edc <+12>: movl $0x6, -0x14(%rbp) 0x10f5e4ee3 <+19>: leaq -0x14(%rbp), %rax 0x10f5e4ee7 <+23>: movq %rax, -0x20(%rbp) 0x10f5e4eeb <+27>: movq -0x20(%rbp), %rax 0x10f5e4eef <+31>: movl $0xc, (%rax) -> 0x10f5e4ef5 <+37>: popq %rbp 0x10f5e4ef6 <+38>: retq
(lldb) register read rbp rbp = 0x00007ffee0619050 (lldb) p/x 0x00007ffee0619050-0x8 (long) $5 = 0x00007ffee0619048 (lldb) p/x 0x00007ffee0619050-0x10 (long) $6 = 0x00007ffee0619040 (lldb) p/x 0x00007ffee0619050-0x14 (long) $7 = 0x00007ffee061903c (lldb) p/x 0x00007ffee0619050-0x20 (long) $8 = 0x00007ffee0619030 (lldb)
图一
// 安排 rsi rdi 0x10f5e4ed4 <+4>: movq %rdi, -0x8(%rbp) 0x10f5e4ed8 <+8>: movq %rsi, -0x10(%rbp)
图二
// int a = 6 6存入 -0x14(%rbp) 0x10f5e4edc <+12>: movl $0x6, -0x14(%rbp)
图三
0x10f5e4ee7 <+23>: movq %rax, -0x20(%rbp)
图四
0x10f5e4ee7 <+23>: movq %rax, -0x20(%rbp) 0x10f5e4eef <+31>: movl $0xc, (%rax)
YangASM`-[ViewController asm_point]: 0x10f5e4ed0 <+0>: pushq %rbp 0x10f5e4ed1 <+1>: movq %rsp, %rbp // 安排 rdi rsi 0x10f5e4ed4 <+4>: movq %rdi, -0x8(%rbp) 0x10f5e4ed8 <+8>: movq %rsi, -0x10(%rbp) // int a = 6 6存入 -0x14(%rbp) 0x10f5e4edc <+12>: movl $0x6, -0x14(%rbp) // 讲-0x14(%rbp)这个地址 赋给rax 0x10f5e4ee3 <+19>: leaq -0x14(%rbp), %rax // rax里面的值 存入-0x20(%rbp) 0x10f5e4ee7 <+23>: movq %rax, -0x20(%rbp) // 讲-0x20(%rbp)的值赋给rax rax 现在指向-0x20(%rbp) // 这个地方很容易让人疑问 上面刚刚把rax 存入内存 怎么有从内存取出来了? // 上面的存入内存是因为源码的 int *p = &a 汇编要完成 p指向一个地址 // 这里是因为代码中 出现了 *p = 12 // *p = 12 其实是2行汇编代码 首先取到 *p 另一行是赋12 // 这里是为了后面 取(rax)提供方便 0x10f5e4eeb <+27>: movq -0x20(%rbp), %rax // (%rax) rax加一个内括号 就是操作这块内存区域里面的值 // movl 说明是4个字节 // 讲 这块区域的值改成 12 0xc 0x10f5e4eef <+31>: movl $0xc, (%rax) -> 0x10f5e4ef5 <+37>: popq %rbp 0x10f5e4ef6 <+38>: retq