

这里注意:addq $0x1, %rax 的意思是,把1与%rax相加,然后结果存储到%rax寄存器中




这里有个注意点:
// 这里小括号一般代表的是内存地址
//整个代码的意思:把内存地址%rbp-0x1中存储的内容取出来赋值给寄存器%rax
movq -0x18(%rbp),%rax
//这句汇编的意思是把 %rbp - 0x18这个地址的值赋值给%rax
//%rax寄存器中存储的是地址值
leaq -0x18(%rbp), %rax

jum指令:cpu跳转到对应内存地址,去执行代码,jum还支持跳转到寄存器中存储的地址值去
jum 0x4001002 //cpu跳转到0x4001002内存地址,去执行汇编指令
//intel
jum rdx
//AT&T
jum *%rdx
jum和callq的区别:
//call后面一般会放函数地址
call 0x100001666
测试一段汇编代码:

[图片上传中...(image.png-c742aa-1678592313947-0)]

汇编显示:callq 0x100002fe0
首先会跳转到 0x100002fe0 这个内存地址
执行下lldb命令 si(单步执行汇编指令)命令 会跳到0x100002fe0这个内存地址,所以0x100002fe0就是函数地址,所以callq后面跟着基本就是函数地址,这里和jum区别是,callq一般都会和retq配合使用

后面单步执行si,直接回车就好了,因为之前执行过si ,一直执行到最后一步retq,然后再执行一次si



这里注意下:
rax “r”开头的寄存器都是64位8个字节的寄存器
eax “e”开头的是32位4字节的寄存器
64位寄存器是可以兼容32位寄存器的:如下图
64位寄存器%rax,会拿出低地址的4个字节来存储32寄存器的4个字节,当作%eax来使用,同理拿出低2个字节来当作16位寄存器来使用%ax,%ax寄存器又可以拆分成低%ah与%al 两种8位寄存器

小结:
r开头:64bit,8字节
e开头:43bit,4字节
ax, bx, cx : 16bit, 2字节
ah al:8bit,1字节(带h或者l的是1个字节)
bh bl
因为共用寄存器存储空间,把值放到寄存器的不同位,所以会间接的改变rax,eax的寄存器值
movq $0xa, %rax //把值0xa存储到%rax寄存器中
movl $0xa, %eax
lldb常用指令:

(lldb) register read rax. //读寄存器
rax = 0x000000008327a7fb
(lldb) register read rax
rax = 0x000000008327a7fb
(lldb) register write rax 0x1 //写寄存器
(lldb) register read rax
rax = 0x0000000000000001
(lldb)
(lldb) register read //打印所有寄存器
General Purpose Registers:
rax = 0x0000000000000001
rbx = 0x00000001000d8060
rcx = 0x000000008327a7fb
rdx = 0x000000008347a80b
rdi = 0x00000000000007fb
rsi = 0x0000600001700280
rbp = 0x00007ff7bfeff420
rsp = 0x00007ff7bfeff300
r8 = 0x0000000000000280
r9 = 0x0000000000000040
r10 = 0x00000000000007fb
r11 = 0x00000000000000ff
r12 = 0x00000001000a03a0 dyld`_NSConcreteStackBlock
r13 = 0x00007ff7bfeff4d8
r14 = 0x0000000100002990 TestSwift`main at main.swift
r15 = 0x000000010008c010 dyld`dyld4::sConfigBuffer
rip = 0x0000000100002def TestSwift`main + 1119 at main.swift:115:1
rflags = 0x0000000000000206
cs = 0x000000000000002b
fs = 0x0000000000000000
gs = 0x0000000000000000
(lldb) x 0x00000001000d8060 //打印内存地址
0x1000d8060: 58 dd 08 00 01 00 00 00 10 c0 08 00 01 00 00 00 X...............
0x1000d8070: 10 80 0d 00 01 00 00 00 f0 8b 0d 00 01 00 00 00 ................
(lldb) x/3xw 0x00000001000d8060 //3xw的意思是,“3”打印三组内存读取内容,“x”按照16进制打印,“w“意思是4个字节位一组
0x1000d8060: 0x0008dd58 0x00000001 0x0008c010
0x1000d8060: 58 dd 08 00 小端模式,低字节位放到低地址位,高字节位放到高内存位,刚好可以和前面0x0008dd58对应起来

n 单步执行,遇到函数会跳过
s 遇到函数会进入
n和s都是针对源码级别的调试,所以如果实在汇编代码的情况下执行n或者s,或自动跳转到源码对应的汇编代码位置,不会按照汇编指令挨个挨条执行
如下图:在执行到test()函数,执行“s”命令

会直接跳转到源码test函数中第一句源码对应的汇编代码,并不是从test函数第一行汇编代码开始执行
func test() {
var a = 3
var b = a+1
}
test()

movq $0x3, -0x8(%rbp) 刚好对应 var a = 3 这句源码
- 相对应的si和ni就对应汇编代码的单条执行
- finish和放开断点的意思相同,如果是在函数内部,执行finish会直接执行到当前函数最后一行
- 汇编中打断点和源码中打断点式分开的,都会记录

//接下来看一段汇编代码,$是立即数的全称,立即数就是常数
movq $0x3, -0x8(%rbp) //将3 赋值到%rbp+-0x8这个内存空间中,%rbp是寄存器中存储的值,然后减去0x8,movq中q的意思是用8个字节来存储值,movb中b的意思是用1个字节来存储,一般用于枚举变量的成员值
//如果想查看寄存器中存储的值可以使用命令:
(lldb) register read rbp
rbp = 0x0000000100002ff4
//xorl 位或运算
xorl %ecx,%ecx

注意:这里手动计算下rip-0x8 ,rip是 0x00007ff7bfeff2f0 减去 0x8 为0x7FF7BFEFF2E8
我们查找内存中 0x7FF7BFEFF2E8 地址对应的内存看下,确实把3存储到了 0x7FF7BFEFF2E8 地址

rip寄存器存储的是指令的地址
cpu要执行的下一条指令地址就存储在rip中

这里+8的意思是,相对于函数起始地址,偏移增加的地址量
网友评论