如何在构建英特尔风格的内联汇编代码时使 gcc 编译器保留寄存器



我正在使用 Xcode 4 上的 gcc 编译器构建一些英特尔风格的内联汇编代码。下面列出了内联程序集代码的一部分:

_asm
{   
    mov eax, esp
    sub esp, 116
    and esp, ~15
    mov [esp+112], eax       
}

在运输模式下,GCC 将上述 4 行 asm 代码编译为:

mov    %esp,%eax
sub    $0x74,%esp
and    $0xfffffff0,%esp
mov    %eax,0x70(%esp)

这正是我想要的。但是,在调试模式下,GCC 会将该代码编译为

mov    %esp,%eax
mov    %eax,%esp
mov    %esp,%eax
mov    %eax,-0x28(%ebp)
mov    %esp,%eax
mov    %eax,%esp
sub    $0x74,%esp
mov    %esp,%eax
mov    %eax,-0x24(%ebp)
mov    %esp,%eax
mov    %eax,%esp
**and    $0xfffffff0,%esp**         
**mov    %esp,%eax**        **//changing the value of “eax”**
mov    %eax,-0x24(%ebp)
mov    %esp,%ecx
mov    %ecx,%esp
**mov    %eax,0x70(%esp)**  **//store a “dirty” value to address 0x70(%esp), which is not we want**

解决上述问题的一种方法是使用 AT&T 样式指令重写内联 asm 代码,并将寄存器添加到崩溃列表中。但是这种方式将是一项非常耗时的工作,因为要重写的代码是如此......哦,长。

还有其他有效的方法来解决这个问题吗?让 gcc 编译器知道应该保留寄存器"eax"?

有两种方法:

  1. 解决它的最佳方法是使用 gcc 程序集模板能力。然后你可以告诉编译器你在做什么寄存器分配器不会将您的寄存器用于任何事情还。

  2. 一个快速技巧是只使用"asm volatile"而不是"asm",这样 gcc 就不会重新安排该块内的任何指令。您仍然需要告诉海湾合作委员会您正在使用寄存器,因此它不会存储任何内容在那里。您还应该在 clobber 列表中列出"内存",因此 gcc知道它不能信任它可能在你之前加载的值代码块。

    ASM volatile( "代码在这里" : : : : "EAX", "ESP", "内存");

顺便说一句:你的代码正在做一些"坏事",比如移动esp,这可能会造成麻烦,除非你确切地知道你在做什么。

Intel 样式块后面的空asm块解决了问题,如下所示:

__asm volatile {   
    mov eax, esp
    sub esp, 116
    and esp, ~15
    mov [esp+112], eax       
};
__asm__ __volatile__ ("":::"eax", "memory");

但是,如果你不恢复%esp,它将造成严重破坏。

最新更新