汇编调试常用手段LLDB
- 读取寄存器的值:
register read rax/格式(x/f/d )
register write rax 10
register read //所有寄存器
- 读取内存地址:x/5xg
x 内存地址
x/3xw 3-显示3组数据
x-是16进制 /f是浮点/d是十进制
w(word)4个字节/ g(giant)-8个字节/b(byte)-1个字节/h(half)-两个字节
- 打印函数调用栈
bt
- 执行步骤
si
finish
- 打印函数调用栈
bt
从编码到启动APP
- 小端 高地址存储高字节,低地址存储低字节
- 大端 高地址存储低字节,低地址存储高字节
内存大概分布图
- 全局变量,放在数据段
- 全局变量的地址在编译的那一刻就确认了(偏移地址固定)
关于String的思考
- 一个String变量占用多少内存? // 内存占用16个字节
- 下面两个String变量,底层存储有什么不同?
var str1 = "0123456789" //字节放在内存中
//0x3736353433323130 0xea0000000000003839
//前15个字节解析:最高存储15个字节
//最后1个字节解析:e标识,a字符串长度
//类似于OC中的tagger pointer
var str2 = "0123456789ABCDEF" //放在常量区
//0xd000000000000010 0x800000010000a790
//前8个字节解析:d标识,10字符串长度
//后8个字节解析:字符串的真实地址 + 0x7fffffffffffffe0
//callq String.init
//%rsi存放这字符串的长度0x10
//%rdi存放这字符串的真实地址
//cmp 0xf,%rsi //比较字符串长度
//movabsq $0x7fffffffffffffe0, %rdx
//addq %rdx, %rdi // 0x7fffffffffffffe0 + rdi
//字符串的真实地址 + 0x7fffffffffffffe0 = 0x800000010000a790(字符串后8个字节)
//字符串的真实地址 = 0x800000010000a790 - 0x7fffffffffffffe0
//字符串的真实地址 = 0x000000010000a790 + 0x20 (小技巧)
//字符串的真实地址 = 0x10000a7b0
- 如果对String进行拼接操作,String变量的存储会发生什么变化?
str1.append("ABCDE")
//如果长度没有超过15个字节,直接放在内存中
str1.append("F")
//0xd000000000000010 0x800000000000a790
//字符串真实地址:0x800000000000a790 - 0x7fffffffffffffe0 = 0x000000000000a790 + 0x20
str2.append("G") //堆空间地址值
//0xf000000000000011 0x0000000103014c60
//字符串真实地址:0x0000000103014c60 - 0x7fffffffffffffe0 = 0x0000000103014c60 + 0x20
//重新指向新的内存,前32个字节存放堆空间的一些信息,后面的才是字符串的真实内容
- ASCII码表:https://www.ascii-code.com/
总结:
//字符串长度 <= 0xF,字符串内容直接存放在str1变量的内存中
var str1 = "0123456789"
//字符串长度 >= 0xF,字符串内容存放在__TEXT.csstring中(常量区)
//字符串的地址值信息存放在str变量的后8个字节中
var str2 = "0123456789ABCDEF"
//又会字符串长度 <= 0xF,所以字符串内容依然存放在str1变量的内存中
str1.append("ABCDE")
//开辟堆空间
str1.append("F")
//开辟堆空间
str2.append("G")
dyld_stub_binder(符号绑定)
- 符号的延迟绑定通过dyld_stub_binder完成
- jmpq *0xb31(%rip)格式的汇编指令(占用6个字节)
关于Array的思考
public struct Array<Element>
var arr = [1, 2, 3, 4]
- 一个Array变量占用多少内存?//8个字节,指向堆空间
- 数组中的数据存放在哪里? //对应堆空间前32个字节存放堆空间的一些信息,后面的才是字符串的真实内容