源码
- (void)test1 {
int a = 1;
NSLog(@"%p",&a);
}
& 取变量a地址 16进制输出a变量的值
(lldb) p &a
(int *) $2 = 0x00007ffee6c9c00c
(lldb) p/x a
(int) $12 = 0x00000001
那么 0x00007ffee6c9c00c 怎么来的
0x00000001 是怎么存储的
完整汇编
指针偏移`-[ViewController test1]:
0x108f61a00 <+0>: pushq %rbp
0x108f61a01 <+1>: movq %rsp, %rbp
0x108f61a04 <+4>: subq $0x20, %rsp
0x108f61a08 <+8>: leaq 0x2611(%rip), %rax ; @"%p"
0x108f61a0f <+15>: movq %rdi, -0x8(%rbp)
0x108f61a13 <+19>: movq %rsi, -0x10(%rbp)
-> `0x108f61a17 <+23>: movl $0x1, -0x14(%rbp)`
0x108f61a1e <+30>: movq %rax, %rdi
0x108f61a21 <+33>: leaq -0x14(%rbp), %rsi
0x108f61a25 <+37>: movb $0x0, %al
0x108f61a27 <+39>: callq 0x108f621c4 ; symbol stub for: NSLog
0x108f61a2c <+44>: addq $0x20, %rsp
0x108f61a30 <+48>: popq %rbp
0x108f61a31 <+49>: retq
找地址
找到 int a = 1;
0x108f61a17 <+23>: movl $0x1, -0x14(%rbp)
int a = 1;
分析
- 0x108f61a17 为当前pc指令
- 编译器 把源码 int a = 1 转化为 movl $0x1, -0x14(%rbp)指令
- 把0x1 存入 rbp的地址和 0x14 相减得到新地址。
- 查看 rbp 的地址
- 计算得出新地址
(unsigned long) rbp = 0x00007ffee6c9c020
(lldb) p/x 0x00007ffee6c9c020-0x14
(long) $1 = 0x00007ffee6c9c00c
和前面 &a得到的一样
看数据
寄存器读取数据
1、读取地址
2、读取长度
3、读取顺序
读取地址 就是上面的 0x00007ffee6c9c00c
读取长度 int 类型4字节 读取4字节
读取顺序 从高往低读4个字节
查看内存
(lldb) x 0x00007ffee6c9c00c
0x7ffee6c9c00c: 01 00 00 00 13 23 f6 08 01 00 00 00 90 6b 50 a4 .....#.......kP.
0x7ffee6c9c01c: e8 7f 00 00 50 c0 c9 e6 fe 7f 00 00 f7 19 f6 08 ....P...........
内存中一个地址对应一个字节
一个字节8bit 也就是 2个16进制
如果存储一个Int类型的数据 也就是4个字节 必然存在着一个如何将多个字节安排的问题, 所以就有了 内存存储模式 有大端模式 和 小端模式
小端模式简单来说就是 低低高高 也就是低地址存放低位字节 高地址存放高位字节
反之就是大端模式
int a = 1
也就是用 4个字节存放 00 00 00 01
其中 右边 00 属于高位字节 左边 01 属于低位字节
上图中
0x7ffee6c9c00c 存放 01 低字节存放地址地位
0x7ffee6c9c00d 存放 00
0x7ffee6c9c00e 存放 00
0x7ffee6c9c00f 存放 00 高字节存放地址高位
我们也可以用这种方式去检测当前环境是大端存储 还是小端存储
所以当前环境下属于 小端存储模式
看参数
运行X86_64 架构, Xcode会使用 AT&T汇编输出 经常使用 rdi rsi 寄存器表示函数参数
接着看下面2行汇编
0x108f61a0f <+15>: movq %rdi, -0x8(%rbp)
0x108f61a13 <+19>: movq %rsi, -0x10(%rbp)
分析
把rdi 的值放到 -0x8(%rbp) 位置
把rsi 的值放到 -0x10(%rbp)位置
Printing description of $rbp:
(unsigned long) rbp = 0x00007ffee6c9c020
(lldb) p/x 0x00007ffee6c9c020-0x8
(long) $3 = 0x00007ffee6c9c018
(lldb) p/x 0x00007ffee6c9c020-0x10
(long) $4 = 0x00007ffee6c9c010
(lldb)
那么 rdi rsi 到底是什么呢
源码 cpp
static void _I_ViewController_test1(ViewController * self, SEL _cmd) {
int a = 1;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_zm_558cwfjs099fbm2r8kxg8wt00000gt_T_ViewController_21e9d9_mi_0,&a);
}
发现 有 self 和 cmd 2个参数
参数顺序 从右往左, 参数压栈顺序与函数参数顺序相反
cmd 是第一个参数 self是第二个参数
压栈顺序是 先self 入栈 cmd 在入栈
查看rsi , 编译器用rsi表示第一个参数 也就是我们上面的参数 SEL _cmd
Printing description of $rsi:
(unsigned long) rsi = 0x0000000108f62313
(lldb) p (SEL)0x0000000108f62313
(SEL) $6 = "test1"
查看 self,编译器用 rdi表示 第二个参数 也就是我们的self
(lldb) po self
<ViewController: 0x7fe8a4506b90>
Printing description of $rdi:
<ViewController: 0x7fe8a4506b90>
看内存
- (void)test1 函数栈 完整的内存分布
(lldb) register read rsp
rsp = 0x00007ffee6c9c000
(lldb) register read rbp
rbp = 0x00007ffee6c9c020
(lldb) p/x 0x00007ffee6c9c020-0x8
(long) $8 = 0x00007ffee6c9c018
(lldb) p/x 0x00007ffee6c9c020-0x10
(long) $9 = 0x00007ffee6c9c010
(lldb) p &a
(int *) $10 = 0x00007ffee6c9c00c
(lldb)
图示:
分析
1、 基本数据类型的变量值 也就是1 直接存储栈空间
2、 记录2个地址信息
cmd 0x0000000108f62313
self 0x00007fe8a4506b90
看cmd 内存分布
cmd地址 0x0000000108f62313
test1 转 ascii 码 116 101 115 116 49 在转16进制 003174736574
看self内存分布
下面开始分析 runtime的 对象内存分布