一个简单的示例代码
// c 代码 long three_n_sum(long a1, long a2, long a3) { return a1 + a2 + a3; } long sum(long a1, long a2, long a3, long a4, long a5, long a6, long a7, long a8) { long b1 = three_n_sum(a1, a2, a3); long b2 = three_n_sum(a4, a5, a6); long b3 = three_n_sum(a7, a8, 0); long b = b1 + b2 + b3; return b; } int main() { long s = sum(1, 2, 3, 4, 5, 6, 7, 8); } // 反汇编的二进制代码 0000000000001125 <three_n_sum>: 1125: 55 push %rbp 1126: 48 89 e5 mov %rsp,%rbp 1129: 48 89 7d f8 mov %rdi,-0x8(%rbp) 112d: 48 89 75 f0 mov %rsi,-0x10(%rbp) 1131: 48 89 55 e8 mov %rdx,-0x18(%rbp) 1135: 48 8b 55 f8 mov -0x8(%rbp),%rdx 1139: 48 8b 45 f0 mov -0x10(%rbp),%rax 113d: 48 01 c2 add %rax,%rdx 1140: 48 8b 45 e8 mov -0x18(%rbp),%rax 1144: 48 01 d0 add %rdx,%rax 1147: 5d pop %rbp 1148: c3 retq 0000000000001149 <sum>: 1149: 55 push %rbp 114a: 48 89 e5 mov %rsp,%rbp 114d: 48 83 ec 50 sub $0x50,%rsp 1151: 48 89 7d d8 mov %rdi,-0x28(%rbp) 1155: 48 89 75 d0 mov %rsi,-0x30(%rbp) 1159: 48 89 55 c8 mov %rdx,-0x38(%rbp) 115d: 48 89 4d c0 mov %rcx,-0x40(%rbp) 1161: 4c 89 45 b8 mov %r8,-0x48(%rbp) 1165: 4c 89 4d b0 mov %r9,-0x50(%rbp) 1169: 48 8b 55 c8 mov -0x38(%rbp),%rdx 116d: 48 8b 4d d0 mov -0x30(%rbp),%rcx 1171: 48 8b 45 d8 mov -0x28(%rbp),%rax 1175: 48 89 ce mov %rcx,%rsi 1178: 48 89 c7 mov %rax,%rdi 117b: e8 a5 ff ff ff callq 1125 <three_n_sum> 1180: 48 89 45 f8 mov %rax,-0x8(%rbp) 1184: 48 8b 55 b0 mov -0x50(%rbp),%rdx 1188: 48 8b 4d b8 mov -0x48(%rbp),%rcx 118c: 48 8b 45 c0 mov -0x40(%rbp),%rax 1190: 48 89 ce mov %rcx,%rsi 1193: 48 89 c7 mov %rax,%rdi 1196: e8 8a ff ff ff callq 1125 <three_n_sum> 119b: 48 89 45 f0 mov %rax,-0x10(%rbp) 119f: 48 8b 45 18 mov 0x18(%rbp),%rax 11a3: ba 00 00 00 00 mov $0x0,%edx 11a8: 48 89 c6 mov %rax,%rsi 11ab: 48 8b 7d 10 mov 0x10(%rbp),%rdi 11af: e8 71 ff ff ff callq 1125 <three_n_sum> 11b4: 48 89 45 e8 mov %rax,-0x18(%rbp) 11b8: 48 8b 55 f8 mov -0x8(%rbp),%rdx 11bc: 48 8b 45 f0 mov -0x10(%rbp),%rax 11c0: 48 01 c2 add %rax,%rdx 11c3: 48 8b 45 e8 mov -0x18(%rbp),%rax 11c7: 48 01 d0 add %rdx,%rax 11ca: 48 89 45 e0 mov %rax,-0x20(%rbp) 11ce: 48 8b 45 e0 mov -0x20(%rbp),%rax 11d2: c9 leaveq 11d3: c3 retq 00000000000011d4 <main>: 11d4: 55 push %rbp 11d5: 48 89 e5 mov %rsp,%rbp 11d8: 48 83 ec 10 sub $0x10,%rsp 11dc: 6a 08 pushq $0x8 11de: 6a 07 pushq $0x7 11e0: 41 b9 06 00 00 00 mov $0x6,%r9d 11e6: 41 b8 05 00 00 00 mov $0x5,%r8d 11ec: b9 04 00 00 00 mov $0x4,%ecx 11f1: ba 03 00 00 00 mov $0x3,%edx 11f6: be 02 00 00 00 mov $0x2,%esi 11fb: bf 01 00 00 00 mov $0x1,%edi 1200: e8 44 ff ff ff callq 1149 <sum> 1205: 48 83 c4 10 add $0x10,%rsp 1209: 48 89 45 f8 mov %rax,-0x8(%rbp) 120d: b8 00 00 00 00 mov $0x0,%eax 1212: c9 leaveq 1213: c3 retq 1214: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 121b: 00 00 00 121e: 66 90 xchg %ax,%ax 转移控制将控制从函数\(P\)转移到\(Q\)只需要简单的把\(PC\)设置为\(Q\)的代码起始位置。
程序的返回处理器需要记录\(P\)的执行的代码位置,这个信息由指令\(call \, Q\)来记录
call指令将地址 A 压入栈中,并将\(PC\)设置为\(Q\)的起始地址,压入的地址 A 是紧跟在 call 指令后面的那条指令的地址,被称为返回地址。
运行时栈一个函数调用栈的变化,用函数 sum() 做示例
将 \(\%rbx\) 入栈保存上一个栈基地址,设置新的栈帧的基地址,分配栈内存(sub $0x50,%rsp)
将寄存器保存在栈中,设置参数,最多六个参数保存在寄存器中(参考main函数)
将 \(PC\) 设置为 1149, 压入call之后的指令地址 1205, 跳转
调用子例程则重复以上动作
返回则执行 leaveq 等价于 movl %ebp %esp popl %ebp; ret 弹出返回地址并且跳转
用一张图来表示栈的变化, 观察汇编代码地址 119f 和 11ab, 在第三次调用 three_n_sum()时参数的取值时存在于 main 函数栈帧中,而参数都存在于栈顶位置也利于子例程取值。