汇编看函数栈构成

源码

- (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;

分析

  1. 0x108f61a17 为当前pc指令
  2. 编译器 把源码 int a = 1 转化为 movl $0x1, -0x14(%rbp)指令
  3. 把0x1 存入 rbp的地址和 0x14 相减得到新地址。
  4. 查看 rbp 的地址
    在这里插入图片描述
  5. 计算得出新地址
(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的 对象内存分布

请添加图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值