【问题标题】:What operand-size should RET default to?RET 默认的操作数大小是多少?
【发布时间】:2016-10-23 08:51:12
【问题描述】:

我的问题涉及 RETurn 指令在 64 位模式 及其操作数大小,它指定从堆栈弹出多少信息到 RIP 以及 RSP 递增多少字节由。

我注意到一些汇编程序保留了 Intel 定义的默认操作数大小。即 64 位用于近返回和 32 位用于远返回:

 1          ; Assembled with NASM 2.12 command "nasm TestRET.asm -l TestRET.lst"
 2                     BITS 64
 3 00000000 66CF       IRETW ; Opsize 16.
 4 00000002 CF         IRETD ; Opsize 32.
 5 00000003 48CF       IRETQ ; Opsize 64.
 6 00000005 66CF   o16 IRET  ; Opsize 16.
 7 00000007 CF     o32 IRET  ; Opsize 32.
 8 00000008 48CF   o64 IRET  ; Opsize 64.
 9 0000000A CF         IRET  ; Opsize 32, default.
10                 
11 0000000B 66CB   o16 RETF  ; Opsize 16.
12 0000000D CB     o32 RETF  ; Opsize 32.
13 0000000E 48CB   o64 RETF  ; Opsize 64.
14 00000010 CB         RETF  ; Opsize 32, default.
15                 
16 00000011 66C3   o16 RETN  ; Opsize 16.
17 00000013 C3     o32 RETN  ; Opsize 32 should not be available, NASM error?
18 00000014 48C3   o64 RETN  ; Opsize 64, REX.W ignored as RETN is promoted to 64 bits.
19 00000016 C3         RETN  ; Opsize 64, default.

我没有编写调用门和任务门的经验,但我认为默认操作数大小应与段大小相对应,在 64 位模式下为 64。

当程序员在没有进一步规范的情况下使用 IRET、RETF、RETN 时,他们期望 64 位模式下的汇编器的操作数大小是多少?

【问题讨论】:

  • 谁知道?我怀疑有人做过调查。你真正的问题是什么?
  • @RossRidge 我的程序应该将 RETF 翻译为 CB 还是 48CB ?
  • 你的程序是汇编程序?我认为这并不重要,真的没有任何理由在 64 位代码中编写远回报。 RETF 也不是正式的指令,所以如果你记录它,你可以做任何你想做的事情。
  • @RossRidge 是的,我发生的一个原因是编写中断处理程序、调用门等。助记符 RETF 正式记录在 AMD 程序员手册第 3 卷第 280 页。support.amd.com/TechDocs/24594.pdf
  • 英特尔只是将 RET 用于远近回报。没有人调用门等代码......并且中断处理程序使用 IRET。

标签: assembly x86 64-bit nasm x86-64


【解决方案1】:

假设在 asm 源文件中写入 ret 将汇编为单字节指令。

IDK 为什么有人会在 64 位代码中使用 retf,所以我对此没有意见。


在更仔细地阅读您的表格时,我明白您所说的关于 NASM 的操作数大小覆盖的内容。它不会将 o32 / o64 修饰符用于像 RETN 这样的指令的特殊情况,从而导致像 o64 retn 这样的虚假行为产生 REX.W=1 前缀 (48 C3)。

我认为我们应该将 NASM 的 o32 前缀理解为“16 位代码中的操作数大小前缀,否则什么都没有”,无论它应用于什么指令,并且从不暗示 REX.W=0(因为那不push/pop 无论如何都不能工作。

同样,o64 真正的意思是应用 REX.W=1,不管它在什么指令上。即它不会在像pushpop 这样的insn 上省略它,对吗?



ISA 的设计使单字节ret 指令“工作”。普通的ret 上不需要 REX 前缀。如the instruction set manual's ret entry 中所述:

在 64 位模式下,该指令的默认操作大小是堆栈地址大小,即 64 位。这适用于近回报,而不是远回报;远返回的默认操作大小为 32 位。

这类似于push/pop 在长模式下默认为 64 位操作数大小。 (而且,与手册中的暗示相反,REX.W=0 无法编码 32 位推送/弹出)。

AMD 通常会尽量保持解码简单。例如movsxd r64, r/m32 需要一个 REX 前缀(否则它只是作为一个可能效率较低的 mov 运行),但对于非常常见的指令,如 push/pop 和 ret,他们通过这样的设计选择节省了代码大小。

【讨论】:

  • a16 retf 和 a32 retf 的场景 - 您正在尝试返回 16 位或 32 位代码。 a64 retf,这很难,但假设您刚刚使用 LGDT 重新加载了 GDT,并且表中的代码段描述符不一样,您会想要强制 CS 重新加载。在这种情况下,a64 retf 就有意义了。
  • @MichaelPetch Tnx,另一个需要 o64 IRET (48CF) 的示例可能是从加载超过 4GB 的代码触发的中断处理程序的返回。顺便说一句,不应该读o64,o32,o16 而不是a64,a32,a16?如 POP 中所述,AFAIK a64(地址大小前缀 67)对 64 位模式下基于 RSP 的指令没有任何意义。此处堆栈指针大小固定为 64 位。
  • 是的@vitsoft 都应该是o 而不是a。这是我昨晚发表的最后一条评论,甚至没有注意到错字。
猜你喜欢
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 2010-12-07
  • 2013-08-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多