【问题标题】:How can the processor discern a far return from a near return?处理器如何辨别远回波和近回波?
【发布时间】:2018-02-21 13:30:27
【问题描述】:

阅读英特尔的大手册,我看到如果你想从一个远调用返回,即对另一个代码段中的过程的调用,你只需发出一个返回指令(可能带有一个移动堆栈的立即参数指针弹出后指针向上 n 个字节)。

显然,如果我的解释正确,硬件足以将段选择器和偏移量弹出到正确的寄存器中。

但是,系统如何知道返回应该是远返回,以及偏移量和选择器都需要弹出?

如果硬件只是弹出偏移量指针而不是它之后的选择器,那么您将指向正确的偏移量但错误的段。

远返回命令与近返回版本相比没有什么特别之处。

据我所知,它们看起来都一样。

然后我假设处理器,可能在微架构级别,跟踪哪些调用是远的,哪些是近的,这样当它们返回时,系统知道要弹出多少字节以及从哪里弹出它们(指针寄存器和段选择器寄存器)。

我的假设正确吗?

你们对这个机制了解多少?

【问题讨论】:

    标签: pointers x86 return memory-segmentation micro-architecture


    【解决方案1】:

    处理器不跟踪呼叫是远还是近; 编译器决定如何编码函数调用并使用远或近操作码返回。

    事实上,FAR 调用在现代处理器上没有用处,因为您不需要更改任何段寄存器值;这就是平面内存模型的意义所在。段寄存器仍然存在,但操作系统将它们设置为 base=0 和 limit=0xffffffff,因此只需一个普通的 32 位指针即可访问所有内存。一切都近在咫尺,如果您需要为其命名。

    通常您甚至不会考虑分段,因此您实际上也不会调用它。但是该手册仍然将我们用于普通代码的 call/ret 操作码描述为 NEAR 版本。


    FAR 和 NEAR 用于使用分段内存模型的旧 86 处理器。当时的程序需要选择他们希望支持的架构类型,从“小”到“大”。如果您的程序足够小以适合单个段,则可以使用 NEAR 调用对其进行编译并以独占方式返回。如果它是“大”的,则相反。对于介于两者之间的任何事情,您有权选择本地函数是否需要能够从另一个段中的代码调用/返回。

    大多数现代程序(除了引导加载程序等)都在不同的结构上运行:它们需要平坦的内存模型。在幕后,操作系统会根据需要换出内存(分页而不是分段),但就程序而言,它的虚拟地址空间完全属于自己。


    但是,为了回答您的问题,调用/返回的区别在于使用的操作码;处理器服从给它的命令。如果你弄错了(比如说,在平面模式下给它一个 FAR 返回操作码),它就会失败。

    【讨论】:

    • 好的。毕竟,实际上有不同形式的返回指令。知道了。谢谢。
    • 不,普通代码使用 NEAR call/ret,不会修改 CS。这就是平面内存模型的全部意义所在。 ret far 从堆栈中弹出一个新的 CS:RIP,普通的ret 只是从堆栈中弹出一个新的RIP。例如,x86 条件分支以 NEAR (rel32) 和 SHORT (rel8) 形式提供,不远。
    • 但是是的,第一句话是正确的;近跳和远跳有不同的操作码。
    • @Dúthomhas:NEAR 和 FAR 在现代 x86 上仍然具有特定的技术含义。一切都近了,因为您可以使用 1 段访问所有内存。 FAR 跳转会改变 CS 以及 IP/EIP/RIP。这不会让您访问更多内存(在平面模型中),但它可以例如通过在 GDT 中选择不同的代码段描述符,从 32 位模式更改为 64 位模式。普通代码使用0xc3 ret, not 0xcb ret far。 FAR 调用/返回要慢得多。根据agner.org/optimize 的说法,与 Core2 一样,RETF 是每 78 个周期一个,而每 2 个周期一个
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-14
    • 1970-01-01
    • 1970-01-01
    • 2014-04-26
    • 2023-04-05
    • 2020-07-19
    • 1970-01-01
    相关资源
    最近更新 更多