汇编-----函数的本质
2018-05-07 本文已影响0人
那时J花开
通过汇编代码我们可以探索函数的本质
- 1: 如果函数内部没有调用其他函数, 那么sp不会进行偏移, 编译器会通过mov bp - N, 0x11的方式向栈中存入数据. 这是编译器所进行的优化.这个时候这个栈中的区域被称为 红色区域 不超过128个字节
- 如果局部变量超过128个字节, 那么还是需要 sub sp, N来开辟栈空间
int sum(int a, int b, int c) {
int x = 10;
// test();
return a + b + c + x;
}
可以看到, 如果函数内部没有调用其他函数, 并且所有参数的总字节数没有超过128个字节时 CPU并不会开辟新的空间,而是使用红色区域去存储变量
safe005`sum:
0x103d90700 <+0>: pushq %rbp
0x103d90701 <+1>: movq %rsp, %rbp
0x103d90704 <+4>: movl %edi, -0x4(%rbp)
0x103d90707 <+7>: movl %esi, -0x8(%rbp)
0x103d9070a <+10>: movl %edx, -0xc(%rbp)
-> 0x103d9070d <+13>: movl $0xa, -0x10(%rbp)
0x103d90714 <+20>: movl -0x4(%rbp), %edx
0x103d90717 <+23>: addl -0x8(%rbp), %edx
0x103d9071a <+26>: addl -0xc(%rbp), %edx
0x103d9071d <+29>: addl -0x10(%rbp), %edx
0x103d90720 <+32>: movl %edx, %eax
0x103d90722 <+34>: popq %rbp
0x103d90723 <+35>: retq
- 2: 如果函数内部调用其他函数, 则必会通过 sub sp, N 来开辟栈空间
void test() {
}
int sum(int a, int b, int c) {
int x = 10;
test();
return a + b + c + x;
}
以下是上边函数的汇编代码, 可以看到, 如果函数内部调用了其他函数, 则sp必会上移,开辟栈空间, 这时候 下一条指令的地址在 栈空间上一位, bp在上2位
0x10417b6f0 <+0>: pushq %rbp
0x10417b6f1 <+1>: movq %rsp, %rbp
0x10417b6f4 <+4>: subq $0x10, %rsp
0x10417b6f8 <+8>: movl %edi, -0x4(%rbp)
0x10417b6fb <+11>: movl %esi, -0x8(%rbp)
0x10417b6fe <+14>: movl %edx, -0xc(%rbp)
-> 0x10417b701 <+17>: movl $0xa, -0x10(%rbp)
0x10417b708 <+24>: callq 0x10417b6e0 ; test at main.m:12
0x10417b70d <+29>: movl -0x4(%rbp), %edx
0x10417b710 <+32>: addl -0x8(%rbp), %edx
0x10417b713 <+35>: addl -0xc(%rbp), %edx
0x10417b716 <+38>: addl -0x10(%rbp), %edx
0x10417b719 <+41>: movl %edx, %eax
0x10417b71b <+43>: addq $0x10, %rsp
0x10417b71f <+47>: popq %rbp
0x10417b720 <+48>: retq
栈帧
- 栈帧指的是 sp和bp之间的空间 8086CPU中栈帧很明显就可以看到, 不过64位的CPU中 由于寄存器得到充分的运用, 所以栈帧不是每次函数调用都可以出现的
全局变量
![](https://img.haomeiwen.com/i10987991/b293eb8d4df705bd.png)
- 如上图所示, 全局变量相对于app来说, 其偏移地址一直是固定不变的, 而基地址是基于整个app在内存中的物理地址来随时改变的
if 判断
int a = 2;
if (a == 1) {
NSLog(@"a == 1");
} else if (a == 2) {
NSLog(@"a == 2");
} else if (a == 3) {
NSLog(@"a == 3");
} else if (a == 4) {
NSLog(@"a == 4");
}
0x1014926bb <+27>: movl $0x2, -0x14(%rbp) ; 赋值
-> 0x1014926c2 <+34>: cmpl $0x1, -0x14(%rbp) ; cmp = cpmpare 比较
0x1014926c6 <+38>: movq %rax, -0x20(%rbp)
0x1014926ca <+42>: jne 0x1014926e6 ; <+70> at main.m:21 ; jne: jump not equl 不等的时候跳转到0x1014926e6指令
0x1014926d0 <+48>: leaq 0x1989(%rip), %rax ; @"a == 1"
0x1014926d7 <+55>: movq %rax, %rdi
0x1014926da <+58>: movb $0x0, %al
0x1014926dc <+60>: callq 0x101492a14 ; symbol stub for: NSLog
0x1014926e1 <+65>: jmp 0x101492750 ; <+176> at main.m:29
0x1014926e6 <+70>: cmpl $0x2, -0x14(%rbp)
0x1014926ea <+74>: jne 0x101492706 ; <+102> at main.m:23
0x1014926f0 <+80>: leaq 0x1989(%rip), %rax ; @"a == 2"
0x1014926f7 <+87>: movq %rax, %rdi
0x1014926fa <+90>: movb $0x0, %al
0x1014926fc <+92>: callq 0x101492a14 ; symbol stub for: NSLog
0x101492701 <+97>: jmp 0x10149274b ; <+171> at main.m
0x101492706 <+102>: cmpl $0x3, -0x14(%rbp)
0x10149270a <+106>: jne 0x101492726 ; <+134> at main.m:25
0x101492710 <+112>: leaq 0x1989(%rip), %rax ; @"a == 3"
0x101492717 <+119>: movq %rax, %rdi
- 以上代码是if判断的汇编实现, 问题来了, jne的时候 怎么判断是否相等而去跳转呢?
-
是用标记寄存区来存放比较的结果的:
image.png
- 如上图, cmp前后 如果rflags值没有发生改变, 说明并不相等; 若发生改变, 说明相等
-