x86_64汇编调用过程简介

过程是软件中一种很重要的抽象。

它提供了一种封装代码的方式,用一组指定的参数和一个可选的返回值实现了某种功能。
设计良好的软件用过程作为抽象机制,隐藏某个行为的具体实现,同时又提供清晰简洁的接口定义,说明要计算的是哪些值,过程会对程序状态产生什么样的影响。
不同编程语言中,过程的形式多样:函数,方法,子例程,处理函数等。
假设过程P调用过程Q,Q执行后返回到P。过程的实现需要使用下面三个机制。

  • 转移控制。在进入过程Q的时候,程序计数器必须被设置为Q的代码的起始地址,然后在返回时,要把程序计数器设置为P中调用Q后面那条指令的地址。
  • 传递数据。P必须能够向Q提供一个或多个参数,Q必须能够向P返回一个值。
  • 分配和释放内存。在开始时,Q可能需要为局部变量分配空间,而在返回前,又必须释放这些存储空间。
转移控制:转移控制是通过call指令以及ret指令来实现的。

call指令会把地址A压入栈中,并将PC设置为Q的起始地址。压入栈中的地址A被称为返回地址,是紧跟在call指令后面的那条指令的地址。
ret指令会从栈中弹出地址A,并把PC设置为A。

传递数据:数据可以通过寄存器和栈来传递。

x86_64中,可以通过寄存器最多传递6个整形参数。按照参数的顺序分别为寄存器%rdi,%rsi,%rdx,%rcx,%r8,%r9。
如果一个函数有大于6个整形参数,超出6个的部分就要通过栈来传递。通过栈传递参数时,所有的数据大小都向8的倍数对齐。

分配和释放内存:局部存储分为栈上的局部存储以及寄存器中的局部存储。

栈上的局部存储:
有些时候局部数据必须存放在内存中,常见的情况包括:

  • 寄存器不足够存放所有的本地数据。
  • 对一个局部变量使用地址运算符&,因此必须能够为它产生一个地址。
  • 某些局部变量是数组或结构,因此必须能够通过数组或结构引用被访问到。

寄存器中的局部存储:
寄存器组是唯一被所有过程共享的资源。虽然在给定时刻只有一个过程是活动的,我们仍然必须确保当一个过程(调用者)调用另一个过程(被调用)时,被调用者不会覆盖调用者稍后会使用的寄存器值。
为此,x86_64采用了一组统一的寄存器使用惯例,所有的过程都必须遵循。

根据惯例,寄存器%rbx,%rbp和%r12~%r15被划分为调用者保存寄存器
当过程P调用过程Q时,Q必须保存这些寄存器的值,保证它们的值在Q返回到P时与Q被调用时是一样的。
过程Q保存一个寄存器的值不变,要么就是根本不去改变它,要么就是把原始值压入栈中,改变寄存器的值,然后在返回前从栈中弹出旧值。压入寄存器的值会在栈帧中创建标号为保存的寄存器的一部分。

所有其他的寄存器,除了栈指针%rsp,都被分类为调用者保存寄存器。这就意味着任何函数都能修改它们。可以这样来理解调用者保存这个名字:过程P在某个此类寄存器中有局部数据,然后调用过程Q。因为Q可以随意修改这个寄存器,所有在调用之前首先保存好这个数据是P(调用者)的责任。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值