函数调用栈——x86-64汇编分析

函数调用栈

  • C源码
  • x86-64-Intel汇编分析
  • 函数调用栈图解
  • 总结

C源码

#include 
int add(int a, int b)
{
	return a + b;
}

int main(int argc, char **argv)
{
	int  sum = add(10, 20);
	return 0;
}

x86-64-Intel汇编分析

  main:
   0x000055555555460e <+0>:     push   rbp           //压栈:两步走:1)%rsp-1  2)mov rsp, rbp
   0x000055555555460f <+1>:     mov    rbp,rsp      //保存rsp当前位置
   0x0000555555554612 <+4>:     sub    rsp,0x20   //rsp分配0x20栈空间
   0x0000555555554616 <+8>:     mov    DWORD PTR [rbp-0x14],edi  //argv参数压栈保存
   0x0000555555554619 <+11>:    mov    QWORD PTR [rbp-0x20],rsi  //argc参数压栈保存
   0x000055555555461d <+15>:    mov    esi,0x14   //  20 存进 $esi (32bit)
   0x0000555555554622 <+20>:    mov    edi,0xa    //  10 存进 $edi (32bit)
   0x0000555555554627 <+25>:    call   0x5555555545fa    //调用add   1)%rsp -1   2)%rip值(call指令下一条指令地址)压栈   
   0x000055555555462c <+30>:    mov    DWORD PTR [rbp-0x4],eax   // %eax(值:30)写入main函数栈中
   0x000055555555462f <+33>:     mov    eax,0x0
   0x0000555555554634 <+38>:    leave
   0x0000555555554635 <+39>:    ret
 add:
   0x00005555555545fa <+0>:     push   rbp   	//保存main函数栈帧寄存器$rbp值(保护现场)
   0x00005555555545fb <+1>:     mov    rbp,rsp     //保存main函数%rsp值,因为没动态局部变量,不需提前分配栈空间
   0x00005555555545fe <+4>:     mov    DWORD PTR [rbp-0x4],edi     //10 存进add函数栈中
   0x0000555555554601 <+7>:     mov    DWORD PTR [rbp-0x8],esi    //20 存进add函数栈中
   0x0000555555554604 <+10>:    mov    edx,DWORD PTR [rbp-0x4]   //从add函数栈中取出10保存在$edx寄存器
   0x0000555555554607 <+13>:    mov    eax,DWORD PTR [rbp-0x8]  // 从add函数栈中取出20保存在$eax寄存器
   0x000055555555460a <+16>:    add    eax,edx	// %eax = %eax + %edx = 10 + 10 = 20
   0x000055555555460c <+18>:    pop    rbp	      //出栈:1)%rsp值(main函数之前栈顶位置) 存进%rbp 2)%rsp +1
   0x000055555555460d <+19>:    ret  		      // 1)%rip 指向call 指令的下一条指令地址   2)%rsp + 1

函数调用栈图解

函数调用栈——x86-64汇编分析_第1张图片

总结

1> gcc 编译器默认传参顺序“从右往左”依次压入寄存器%r9 %8 %rcx %rdx %rsi %rdi ,第7个(包含7)之后参数压入caller栈中。
2> push rbp :1) %rsp栈顶指针减8字节(64位CPU) 2)%rbp栈帧寄存器值压入栈中
3> call 指令 :1) %rsp-0x8 后,caller中call指令下一条指令地址(%rip)压入caller 栈中 2)%rip:指向callee函数第一条指令地址
4> pop rbp : 1) %rbp: 指向caller函数原来栈帧位置 2)%rsp + 0x8(64位CPU)
5> ret 指令 :1) %rip:指向caller函数call指令的下一条指令地址 2) %rsp + 0x8(64位CPU)
6> 由上可知,push call 指令用于保护现场,而 pop ret 指令用于恢复现场

你可能感兴趣的:(Linux专题)