【问题标题】:How does inline (x86) assembly affect the program flow?内联 (x86) 汇编如何影响程序流程?
【发布时间】:2015-10-29 17:44:03
【问题描述】:

我试图了解在运行时如何调用此类 sn-ps:

__asm{
    PUSH ES
    MOV CX,0
    //... More x86 assembly
};

调整寄存器不会破坏程序流程的执行吗?

例如:如果上面的 CX 持有某个值,这是否意味着这个寄存器的值将不再有效?

编译器是否会处理这些依赖关系,或者sn-p的执行是否发生在特殊情况下?

在哪些编译器上内联汇编的使用不透明?

【问题讨论】:

  • 尝试查看生成的整个函数的汇编代码。
  • 在这样的问题中,告诉我们编译器会很漂亮。 GCC(带有扩展程序集)处理这种情况的方式与例如 clang 不同。
  • 这取决于编译器。有些编译器希望保留所有寄存器,而有些编译器会尝试为您跟踪代码的寄存器使用情况,而其他编译器则要求您手动标记被破坏的寄存器。内联汇编本质上是特定于编译器的,因此您必须阅读编译器手册中的规则。

标签: c visual-c++ gcc x86 inline-assembly


【解决方案1】:

海合会

GCC 中,您必须明确指定受影响的寄存器以防止执行流程损坏:

asm [volatile] ( AssemblerTemplate
                  : OutputOperands
                  [ : InputOperands
                  [ : Clobbers ] ])

虽然编译器知道输出中列出的条目发生了变化 操作数,内联 asm 代码可以修改的不仅仅是 输出。[...] 计算可能需要额外的寄存器,[...] 将它们列在 clobber 列表中。

如果您的代码对已列出的其他项目执行读取或写入操作,请使用 "memory" clobber 参数。

“内存”破坏器告诉编译器汇编代码 对未列出的项目执行内存读取或写入 输入输出操作数

参考https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

MSVC

另一方面,在 MSVC 中,您不需要保留通用寄存器:

当使用 __asm 在 C/C++ 函数中编写汇编语言时,你 不需要保留 EAX、EBX、ECX、EDX、ESI 或 EDI 寄存器。 [...]

您应该保留您使用的其他寄存器(例如 DS、SS、SP、BP、 和标志寄存器)用于 __asm 块的范围。你应该 保留 ESP 和 EBP 寄存器,除非您有某些理由 改变它们。

参考:https://msdn.microsoft.com/en-us/library/k1a8ss06.aspx


编辑:根据 Olafs 的建议,将 gcc 的 should 更改为 have to 并添加了关于“memory”clobber 参数的注释。 p>

【讨论】:

  • 实际上你必须指定被破坏的寄存器甚至内存。这不是可选的。
  • @Olaf:我同意 :)。改编了答案。
  • 我也有意提到了记忆。如果没有这个列表,gcc 可能会保存来自内存变量的值,例如数组/结构/等。在非破坏寄存器中,因此您的汇编代码在读取它们时可能会访问过时的数据。这就是您可以使用asm volatile { "" :::"memory" } 作为编译器屏障的原因。
【解决方案2】:

还有一些额外的标志可以传递给内联汇编代码。其中之一是“clobber 列表”,它向 C/C++ 编译器指示将由汇编代码块修改的寄存器列表。

请注意,指定这些附加标志的方式取决于编译器(在 Microsoft Visual C++、GCC 等中完全不同......)

对于 GCC,例如:

https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#ss5.3

【讨论】:

  • 他明确询问(Microsoft)Visual C++。
  • @JonathonReinhart:最新编辑也将 GCC 添加到标签中。
  • 内联汇编器是特定于实现的。如所写,您的答案令人印象深刻,有一种常见的语法/语义。请编辑。
猜你喜欢
  • 2011-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多