前端时间阅读了王爽老师的汇编语言,之后一直没有抽出时间进行整理,最近终于得空,进行一个小的梳理以及回顾
CPU数据的读写主要分三类
- 存储单元的地址(地址信息)
- 通过地址总线
- 器件的选择,读或写的命令(控制信息)
- 通过控制总线
- 读或写的数据(数据信息)
- 通过数据总线
Register | Conventional use | Low 32-bits | Low 16-bits | Low 8-bits |
---|---|---|---|---|
rax | 返回值 | eax | ax | al |
rdi | 第一个参数 | edi | di | dil |
rsi | 第二个参数 | esi | si | sil |
rdx | 第三个参数 | esx | dx | dl |
rcx | 第四个参数 | ecx | cx | cl |
r8 | 第五个参数 | r8d | r8w | r8b |
r9 | 第六个参数 | r9d | r9w | r9b |
r10 | 内部调用暂时寄存器 | r10d | r10w | r10b |
r11 | 内部调用暂时寄存器 | r11d | r11w | r11b |
rsp | 栈指针 | esp | sp | spl |
rbx | 临时变量 | ebx | bx | bl |
rbp | 临时变量 | ebp | bp | bpl |
r12 | 临时变量 | r12d | r12w | r12b |
r13 | 临时变量 | r13d | r13w | r13b |
r14 | 临时变量 | r14d | r14w | r14b |
r15 | 临时变量 | r15d | r15w | r15b |
rip | 调用寄存器 | |||
eflags | 标志位寄存器 |
对rax做个简单的探究
- (NSInteger)returnInt {
return 100;
}
断点在函数中,在lldb中输入finish
可见rax中存储的是100(返回值)
rdi rsi rdx rcx r8 r9分别负责存储函数的第一、二、三、四、五、六个参数
- (void)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six {
NSLog(@"test");
}
断点之后在lldb打印
可看到rdx rcx r8 r9分别存储了1,2,3,4
理论上rdi rsi分别存储第1、2个参数,所以看看rdi rsi分别存储了什么
可看到rdi里面存储的是ViewController的一个对象指针,这可以理解了,OC方法消息传递,第一个参数是对象,第二个参数是SEL
rip为指令寄存器,存储下一条要执行的指令的地址
在行数首行断点
打印rip的值
寻找argumentWithOne:two:three:four:five:six:
的内存地址
可看到range = [0x000000010c76ae80-0x000000010c76aec3)
,可看到0x000000010c76ae80为rip的值
-
RSP
- 指向当前栈的顶端(由于栈向下增长,push RSP会递减,pop会递增)
-
rbp
- 一直指向栈的顶端
前面有提到,x86共有6个寄存器用来存储参数,但是当参数超时6个的时候怎么处理呢?这边便需要用到一种常见的数据结构--栈
编写函数
- (void)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six seven:(int)seven eight:(int)eight nine:(int)nine ten:(int)ten {
NSLog(@"test");
}
在调用处加断点
[self argumentWithOne:1 two:2 three:3 four:4 five:5 six:6 seven:7 eight:8 nine:9 ten:10];
打开Debug->Debug Workflow->Always Show Disassembly,可看到如下
可看到前6个参数正常存储于寄存器,之后放入栈中
可看到入栈用的不是push操作,同时在前面可以看到
sub rsp, 0x60
这一条指令,这是一个优化,提前给参数创造出一片空间,用来存储参数,防止多个push操作影响性能,同时12个参数(10个入参+id+SEL)正好为12*8 = 0x60
可看到函数调用基本步骤
- 主函数将被调函数需要的参数压入寄存器或栈中
- 调用call,跳到被调函数的地址,同时将主函数的下一条指令入栈
- 被调函数设置rbp为栈顶
- 将0-5参数以及临时变量入栈
- 完成将返回值(如果有)存入rax
- 通过ret返回主函数
接下来进行验证:
- (void)viewDidLoad {
[super viewDidLoad];
[self argumentWithOne:1 two:2 three:3 four:4 five:5 six:6];
}
- (int)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six {
[self argumentWithOne:7 two:8 three:9 four:10 five:11 six:12 seven:13 eight:14 nine:15 ten:16];
NSLog(@"test");
return 10;
}
- (int)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six seven:(int)seven eight:(int)eight nine:(int)nine ten:(int)ten {
int a = 1; int b = 2;
a = 3; b = 9;
NSLog(@"%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", one, two, three, four, five, six, seven, eight, nine, ten, a, b);
return 10;
}
在两个函数中间分别断点,可见
AssamblyStudy`-[ViewController argumentWithOne:two:three:four:five:six:]:
0x10316cc70 <+0>: push rbp
0x10316cc71 <+1>: mov rbp, rsp
0x10316cc74 <+4>: sub rsp, 0x70
0x10316cc78 <+8>: mov eax, dword ptr [rbp + 0x18]
0x10316cc7b <+11>: mov r10d, dword ptr [rbp + 0x10]
0x10316cc7f <+15>: mov qword ptr [rbp - 0x8], rdi
0x10316cc83 <+19>: mov qword ptr [rbp - 0x10], rsi
0x10316cc87 <+23>: mov dword ptr [rbp - 0x14], edx
0x10316cc8a <+26>: mov dword ptr [rbp - 0x18], ecx
0x10316cc8d <+29>: mov dword ptr [rbp - 0x1c], r8d
0x10316cc91 <+33>: mov dword ptr [rbp - 0x20], r9d
-> 0x10316cc95 <+37>: mov rsi, qword ptr [rbp - 0x8]
0x10316cc99 <+41>: mov rdi, qword ptr [rip + 0x7740] ; "argumentWithOne:two:three:four:five:six:seven:eight:nine:ten:"
0x10316cca0 <+48>: mov qword ptr [rbp - 0x28], rdi
0x10316cca4 <+52>: mov rdi, rsi
0x10316cca7 <+55>: mov rsi, qword ptr [rbp - 0x28]
0x10316ccab <+59>: mov edx, 0x7
0x10316ccb0 <+64>: mov ecx, 0x8
0x10316ccb5 <+69>: mov r8d, 0x9
0x10316ccbb <+75>: mov r9d, 0xa
0x10316ccc1 <+81>: mov dword ptr [rsp], 0xb
0x10316ccc8 <+88>: mov dword ptr [rsp + 0x8], 0xc
0x10316ccd0 <+96>: mov dword ptr [rsp + 0x10], 0xd
0x10316ccd8 <+104>: mov dword ptr [rsp + 0x18], 0xe
0x10316cce0 <+112>: mov dword ptr [rsp + 0x20], 0xf
0x10316cce8 <+120>: mov dword ptr [rsp + 0x28], 0x10
0x10316ccf0 <+128>: mov dword ptr [rbp - 0x2c], eax
0x10316ccf3 <+131>: mov dword ptr [rbp - 0x30], r10d
0x10316ccf7 <+135>: call qword ptr [rip + 0x2303] ; (void *)0x00007fff20175280: objc_msgSend
0x10316ccfd <+141>: lea rsi, [rip + 0x2314] ; @"test"
0x10316cd04 <+148>: mov rdi, rsi
0x10316cd07 <+151>: mov dword ptr [rbp - 0x34], eax
0x10316cd0a <+154>: mov al, 0x0
0x10316cd0c <+156>: call 0x10316d354 ; symbol stub for: NSLog
0x10316cd11 <+161>: mov eax, 0xa
0x10316cd16 <+166>: add rsp, 0x70
0x10316cd1a <+170>: pop rbp
0x10316cd1b <+171>: ret
可以看到在主函数中给rsp开辟了0x70(0x10316cc74 <+4>: sub rsp, 0x70
)的空间,即为12个参数+2个临时变量的空间
AssamblyStudy`-[ViewController argumentWithOne:two:three:four:five:six:seven:eight:nine:ten:]:
0x10316cd20 <+0>: push rbp
0x10316cd21 <+1>: mov rbp, rsp
0x10316cd24 <+4>: push r15
0x10316cd26 <+6>: push r14
0x10316cd28 <+8>: push r13
0x10316cd2a <+10>: push r12
0x10316cd2c <+12>: push rbx
0x10316cd2d <+13>: sub rsp, 0x98
0x10316cd34 <+20>: mov eax, dword ptr [rbp + 0x38]
0x10316cd37 <+23>: mov r10d, dword ptr [rbp + 0x30]
0x10316cd3b <+27>: mov r11d, dword ptr [rbp + 0x28]
0x10316cd3f <+31>: mov ebx, dword ptr [rbp + 0x20]
0x10316cd42 <+34>: mov r14d, dword ptr [rbp + 0x18]
0x10316cd46 <+38>: mov r15d, dword ptr [rbp + 0x10]
0x10316cd4a <+42>: lea r12, [rip + 0x22e7] ; @"%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d"
0x10316cd51 <+49>: mov qword ptr [rbp - 0x30], rdi
0x10316cd55 <+53>: mov qword ptr [rbp - 0x38], rsi
0x10316cd59 <+57>: mov dword ptr [rbp - 0x3c], edx
0x10316cd5c <+60>: mov dword ptr [rbp - 0x40], ecx
0x10316cd5f <+63>: mov dword ptr [rbp - 0x44], r8d
0x10316cd63 <+67>: mov dword ptr [rbp - 0x48], r9d
0x10316cd67 <+71>: mov dword ptr [rbp - 0x4c], 0x1
0x10316cd6e <+78>: mov dword ptr [rbp - 0x50], 0x2
0x10316cd75 <+85>: mov dword ptr [rbp - 0x4c], 0x3
0x10316cd7c <+92>: mov dword ptr [rbp - 0x50], 0x9
-> 0x10316cd83 <+99>: mov esi, dword ptr [rbp - 0x3c]
0x10316cd86 <+102>: mov edx, dword ptr [rbp - 0x40]
0x10316cd89 <+105>: mov ecx, dword ptr [rbp - 0x44]
0x10316cd8c <+108>: mov r8d, dword ptr [rbp - 0x48]
0x10316cd90 <+112>: mov r9d, dword ptr [rbp + 0x10]
0x10316cd94 <+116>: mov r13d, dword ptr [rbp + 0x18]
0x10316cd98 <+120>: mov edi, dword ptr [rbp + 0x20]
0x10316cd9b <+123>: mov dword ptr [rbp - 0x54], eax
0x10316cd9e <+126>: mov eax, dword ptr [rbp + 0x28]
0x10316cda1 <+129>: mov dword ptr [rbp - 0x58], eax
0x10316cda4 <+132>: mov eax, dword ptr [rbp + 0x30]
0x10316cda7 <+135>: mov dword ptr [rbp - 0x5c], eax
0x10316cdaa <+138>: mov eax, dword ptr [rbp + 0x38]
0x10316cdad <+141>: mov dword ptr [rbp - 0x60], eax
0x10316cdb0 <+144>: mov eax, dword ptr [rbp - 0x4c]
0x10316cdb3 <+147>: mov dword ptr [rbp - 0x64], eax
0x10316cdb6 <+150>: mov eax, dword ptr [rbp - 0x50]
0x10316cdb9 <+153>: mov dword ptr [rbp - 0x68], edi
0x10316cdbc <+156>: mov rdi, r12
0x10316cdbf <+159>: mov dword ptr [rsp], r13d
0x10316cdc3 <+163>: mov r13d, dword ptr [rbp - 0x68]
0x10316cdc7 <+167>: mov dword ptr [rsp + 0x8], r13d
0x10316cdcc <+172>: mov r13d, dword ptr [rbp - 0x58]
0x10316cdd0 <+176>: mov dword ptr [rsp + 0x10], r13d
0x10316cdd5 <+181>: mov r13d, dword ptr [rbp - 0x5c]
0x10316cdd9 <+185>: mov dword ptr [rsp + 0x18], r13d
0x10316cdde <+190>: mov r13d, dword ptr [rbp - 0x60]
0x10316cde2 <+194>: mov dword ptr [rsp + 0x20], r13d
0x10316cde7 <+199>: mov r13d, dword ptr [rbp - 0x64]
0x10316cdeb <+203>: mov dword ptr [rsp + 0x28], r13d
0x10316cdf0 <+208>: mov dword ptr [rsp + 0x30], eax
0x10316cdf4 <+212>: mov al, 0x0
0x10316cdf6 <+214>: mov dword ptr [rbp - 0x6c], r10d
0x10316cdfa <+218>: mov dword ptr [rbp - 0x70], r11d
0x10316cdfe <+222>: mov dword ptr [rbp - 0x74], ebx
0x10316ce01 <+225>: mov dword ptr [rbp - 0x78], r14d
0x10316ce05 <+229>: mov dword ptr [rbp - 0x7c], r15d
0x10316ce09 <+233>: call 0x10316d354 ; symbol stub for: NSLog
0x10316ce0e <+238>: mov eax, 0xa
0x10316ce13 <+243>: add rsp, 0x98
0x10316ce1a <+250>: pop rbx
0x10316ce1b <+251>: pop r12
0x10316ce1d <+253>: pop r13
0x10316ce1f <+255>: pop r14
0x10316ce21 <+257>: pop r15
0x10316ce23 <+259>: pop rbp
0x10316ce24 <+260>: ret
可见
0x10316cd51 <+49>: mov qword ptr [rbp - 0x30], rdi
0x10316cd55 <+53>: mov qword ptr [rbp - 0x38], rsi
0x10316cd59 <+57>: mov dword ptr [rbp - 0x3c], edx
0x10316cd5c <+60>: mov dword ptr [rbp - 0x40], ecx
0x10316cd5f <+63>: mov dword ptr [rbp - 0x44], r8d
0x10316cd63 <+67>: mov dword ptr [rbp - 0x48], r9d
0x10316cd67 <+71>: mov dword ptr [rbp - 0x4c], 0x1
0x10316cd6e <+78>: mov dword ptr [rbp - 0x50], 0x2
这个函数一共12个参数,前6个在寄存器中,则[rbp+0x10]-[rbp+0x38]存放第7-12个参数(0xb-0x10),现在进行验证
(lldb) x/gx '$rbp + 0x10'
0x7ffeeca90ff0: 0x000000000000000b
(lldb) x/gx '$rbp + 0x18'
0x7ffeeca90ff8: 0x000000000000000c
(lldb) x/gx '$rbp + 0x20'
0x7ffeeca91000: 0x000000000000000d
(lldb) x/gx '$rbp + 0x28'
0x7ffeeca91008: 0x000000000000000e
(lldb) x/gx '$rbp + 0x30'
0x7ffeeca91010: 0x000000000000000f
(lldb) x/gx '$rbp + 0x38'
0x7ffeeca91018: 0x0000000000000010
同时,[rbp+0x8]应该是函数的返回地址
(lldb) x/gx '$rbp + 0x8'
0x7ffeeca90fe8: 0x000000010316ccfd
可见这个地址即为-[ViewController argumentWithOne:two:three:four:five:six:]
调用call的下一个地址
0x10316ccf7 <+135>: call qword ptr [rip + 0x2303] ; (void *)0x00007fff20175280: objc_msgSend
0x10316ccfd <+141>: lea rsi, [rip + 0x2314] ; @"test"
验证成功~
注:以上都在Mac的x86_64架构进行