You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

经近调用进入的函数执行远尾调用是否会破坏返回地址预测?

分析结论

_tail实现一定会导致返回栈缓冲(Return Stack Buffer, RSB)的返回预测错误。


核心原因

先明确RSB的硬件工作逻辑:

  • RSB是CPU专门为函数返回设计的专用预测器,只识别call和ret两类指令,完全不感知栈上手动构造的返回地址,也不会在执行普通跳转类指令时做额外的RSB操作:
    • 只要执行call(不管是近调用call还是远调用lcall),CPU就会自动把call指令后面的下一条指令地址压入RSB的硬件栈;
    • 只要执行ret(不管是近返回ret还是远返回lret),CPU就会直接从RSB栈顶弹出一个地址作为预测的返回目标,不会提前比对栈上的实际返回地址值。

再对比两个实现的流程差异:

  • 对照项_non_tail用lcall进入64位的heavensgate代码段:执行lcall时CPU已经把lcall后面的ret指令地址压入了RSB,后续执行lret时弹出的RSB预测值正好和栈上实际返回地址一致,不会触发误预测。
  • 目标项_tail进入64位段用的是ljmp远跳转,不是任何形式的call指令:执行ljmp时CPU不会往RSB压入任何地址。等后续执行lret时,CPU依然会按照固定逻辑从RSB弹栈顶值做预测,这时候弹出的要么是更早之前其他函数调用压入的无关地址,要么是RSB为空时填充的默认哨兵值,哪怕手动在栈上构造的返回地址和正常call/ret流程的目标地址完全一样,RSB的预测值和实际返回目标也不匹配,必然产生误预测。

额外纠正一个认知偏差:描述里提到的“近调用与远返回配对”的前提不成立,_tail的整个执行流程里根本没有执行近调用指令,只是手动在栈上拼了远返回需要的CS段寄存器值+返回地址结构,全程没有触发RSB压栈的动作,这才是预测错误的核心根源。


待分析的原始汇编代码如下:

.globl _non_tail, _tail
.text

.code32
_non_tail:
    lcall $0x33, $_non_tail.heavensgate
    ret

.code64
_non_tail.heavensgate:
    # do stuff. there's 12 bytes on the stack before the first argument
    lret

.code32
_tail:
    pushl (%esp)
    movw %cs, 4(%esp)
    ljmp $0x33, $_tail.heavensgate

.code64
_tail.heavensgate:
    # do stuff. there's 8 bytes on the stack before the first argument
    lret

内容的提问来源于stack exchange,提问作者Joseph Sible-Reinstate Monica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 12:51:20