返回地址是一个指向代码的指针,调用者将其压入堆栈,通常是通过call 指令。由于这是由调用者推送的,因此该值(以及调用者推送的任何其他值)必须在f 的第一条机器代码指令运行之前在堆栈上。约定要求返回地址在(控制流)从调用者到被调用者的转换时位于栈顶。
所以,就在f 开始之前,堆栈上有返回地址。在旧文本中,返回地址有时称为链接。它是调用者传递的一个参数,被调用者用来知道返回到哪里。将返回地址作为参数传递给被调用者使用允许从许多不同的地方(在调用链的许多不同深度)调用函数(此处为f),并始终返回其调用者,无论是谁是。
返回地址和rbp不同。我们不能说这里的调用者如何使用 rbp b/c 我们看不到调用者。但是,调用约定要求 rbp 寄存器在从被调用者返回到调用者时保留其原始值。因此,由于这个被调用者选择修改 rbp 值(虽然没有明显的原因),因此有必要在返回之前恢复原始 rbp 值,它确实这样做了。
在从调用者到被调用者的转换时,它被称为旧的 rbp b/c,rbp 值属于调用堆栈中更高的某个人,而不是属于 f 的 rbp 值(而是给fs 的来电者)。
我们不能说 rbp 中属于调用者的内容,但是,f 选择使用 rbp 作为指向数据的指针,特别是堆栈数据(虽然它不使用它)。
- return address 真的是我们第一次进入 main 框架时 rbp 的值吗?
没有。 rbp 一般用作栈的指针,称为帧指针,它与返回地址是分开的。
- 如果是这样,为什么不像其他人那样叫老rbp?如果 f 调用另一个函数,我们应该将其称为返回地址还是 rbp?
如果f 调用另一个函数,堆栈上将至少有 2 个返回地址。 rbp 是另一回事,如果main 和f 使用帧指针(并且f 调用另一个函数),那么将至少有一个帧指针(mains 作为f 放在那里)堆栈也是如此。
- 返回地址是main的返回地址,和f()没有关系吧?
我们不能从这个 sn-p 说 main 称为 f。 f 几乎可以被任何调用者调用。如果main确实调用了f,则返回地址代表main调用f的动态链接。例如,main 在mains 实现中从两个不同的地方调用f 是可以的。在main 中的每个不同调用站点,不同的代码地址将作为返回地址传递给f,f 将使用传递的返回地址参数动态返回到正确的调用者和调用站点。
返回地址应该被认为是一个附加的(但对C隐藏)参数,所以它是由调用者传递的,但属于就像另一个被调用者一样(在C中明确看到)参数。我们不能在不传递返回地址参数的情况下调用 f — 如果我们不传递 f 会在查找其他参数时以及在完成时返回时感到困惑。
建议您在调试器中尝试一个小示例和单步操作。密切关注堆栈推送(和推送的值)和弹出,并观察从调用者到被调用者的控制流转换,然后再返回。