第14章函数和变量

提示:函数调用说白了就是变量和参数存储和传递的规定。


一、程序调用约定

1.1 什么是程序调用约定

  所谓程序调用约定(calling convention)就是根据 CPU 和 OS 来决定函数调用的具体实现方法的约定。譬如参数传递的实现,既有把参数保存到寄存器来传递的方法,也有把参数入栈来传递的方法。

1.2 Linux/x86 下的程序调用约定

在这里插入图片描述
在这里插入图片描述

二、Linux/X86下函数调用

3.1 寄存器的保存与复原

  x86 架构提供了 6 个可随意使用的通用寄存器,在程序运行的整个生命周期里能够使用的寄存器也只有这 6 个(包括了函数调用的子函数也公用这6个寄存器)。
  要怎样做才能安全地访问寄存器呢?最保险的方法是,每次在调用别的函数前,把寄存器的值保存到栈中。也就是说,在函数开始执时,把所有寄存器的值压栈,而在函数内部执行return 指令返回的时候,把寄存器的值出栈,恢复函数调用前的状态。通过这个方法,各个函数就都可以随意使用所有的寄存器了。
  这个方法的确是最安全的,但效率非常低。访问栈等价于访问机器内存,和单纯使用寄存器相比,访问内存的速度明显下降。因此,很有必要花心思去减少栈的使用次数
  并不是所有寄存器的值都需要保存。之所以要保存一个寄存器的值,是因为我们不想去更改这个寄存器的值。也就是说,如果是函数不会使用(不会变更)的寄存器,那么这个寄存器的值就不用保存
  程序调用约定中指定了 callee-save 寄存器以及 caller-save 寄存器两种分类,以最大限度地重复利用寄存器。利用这个约定,可以进一步减少访问栈的次数。

3.2 caller-save 寄存器和 callee-save 寄存器

  caller-save 寄存器和 callee-save 寄存器是程序调用约定里规定的关于寄存器使用方法的规则。这个约定把所有的寄存器分为 caller-save 寄存器和callee-save 寄存器两类不同类别的寄存器使用方法也不相同。Linux/X86下寄存器的分类如下图所示。
在这里插入图片描述

  1. caller-save寄存器
      指的是为函数调用方保存值的寄存器。在使用caller-save 寄存器时,调用其他函数前必须把寄存器的值保存到内存,并在函数调用结束后恢复寄存器的值。不过相应地,被调用函数中可以不保存这个寄存器的值,直接更改。
  2. callee-save寄存器
      被调用函数必须显式保存寄存器的值的寄存器。也就说,使用 callee-save 寄存器的函数,必须在函数执行开始时把寄存器的值压栈保存到内存,并且在函数执行结束时把寄存器的值复原

3.3 caller-save 寄存器和 callee-save 寄存器的灵活应用

  caller-save 寄存器和 callee-save 寄存器的使用方法:caller-save 寄存器不需要保存和恢复原来的值,因此适合用来保存临时变量、生命周期比较短的值等;而 callee-save 寄存器在调用其他函数的时候不需要进行值的保存和恢复处理,比较适用于保存在函数内一直使用的生命周期比较长的值。

3.4 大数值和浮点数的返回方法

  Linux/x86 的程序调用约定中指定把返回值存进 eax 寄存器后返回。不过,eax 寄存器只占32 位宽,需要采用特殊的办法才能返回超出其上限的值。譬如 long long 类型的值、double float 类型的浮点数、结构体等最少需要 64 位宽的值。

  首先,返回 long long 类型的值时,和 div 指令的处理一样,把 edx 寄存器和 eax 寄存器连结为 64 位宽的寄存器来使用。也就是说,把 64 位数值的高 32 位保存到 edx 寄存器,低32 位保存到 eax 寄存器并返回。
  其次,需要返回浮点数的时候,可以把数值保存到 st(0) 这个浮点数寄存器中。这种情况下不需要使用 eax 寄存器。
  最后是返回结构体和联合体的情况,这两种情况相对比较麻烦。返回结构体或者联合体的时候,需要函数调用方和函数本身进行协作,具体如下所示。

  1. 由函数调用方在栈上申请保存返回值的区域
  2. 把该区域的内存地址作为第 1 个参数压栈
  3. 被调用函数把返回值写入该区域
  4. 把内存地址保存到 eax 寄存器中,并且执行 ret $4 指令

总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值