当前位置: 首页 > 知识库问答 >
问题:

在GCC内联asm中,影响内存操作数寻址模式的早期clobber的错误行为的具体示例?

郭胤
2023-03-14

下面摘录自GCC手册的扩展Asm文档,关于使用Asm关键字在C中嵌入汇编指令:

如果一个输出参数(a)允许寄存器约束,而另一个输出参数(b)允许存储器约束,也会出现同样的问题。GCC生成的用于访问b中的存储器地址的代码可以包含可能由a共享的寄存器,并且GCC认为这些寄存器是ASM的输入。如上所述,GCC假设这样的输入寄存器在写入任何输出之前就被消耗了。如果asm语句在使用B之前写入a,则此假设可能导致不正确的行为。将'&'修饰符与a上的register约束结合起来确保修改a不会影响B引用的地址。否则,如果在使用b之前修改了a,则未定义b的位置。

斜体句子表示,如果asm语句在使用b之前写入a中,则可能存在“不正确的行为”。

如果我们只有一个带有一个核心的CPU,请出示一个可能产生这种不正确行为的asm代码,即修改A会影响B引用的地址,使得B的位置未定义。

我唯一熟悉的汇编语言是Intel x86汇编语言,所以请让示例针对那个平台。

共有1个答案

顾永福
2023-03-14

请考虑以下示例:

extern int* foo();
int bar()
{
    int r;

    __asm__(
        "mov $0, %0 \n\t"
        "add %1, %0"
    : "=r" (r) : "m" (*foo()));

    return r;
}

通常的调用约定将返回值放入eax寄存器。因此,编译器很有可能决定在整个过程中使用eax,以避免不必要的复制。生成的程序集可能如下所示:

        subl    $12, %esp
        call    foo
        mov $0, %eax
        add (%eax), %eax
        addl    $12, %esp
        ret

请注意,mov$0,%eax在下一条指令尝试使用eax引用输入参数之前将eax置零,因此此代码将崩溃。使用早期的clobber,您会强制编译器选择不同的寄存器。在我的例子中,得到的代码是:

        subl    $12, %esp
        call    foo
        mov $0, %edx
        add (%eax), %edx
        addl    $12, %esp
        movl    %edx, %eax
        ret
        subl    $12, %esp
        call    foo
        mov     %eax, %edx
        mov $0, %eax
        add (%edx), %eax
        addl    $12, %esp
        ret
 类似资料:
  • 谢谢你的帮助。

  • 尝试使用gcc:https://github.com/wolf9466/cpuminer-multi/blob/master/cryptonight_aesni.c编译此源文件时遇到此错误 “CRYPTONIGT_AESNI.c:162:4:错误:操作数约束不一致”

  • 我正在尝试同时处理MSVC和GCC编译器,同时更新这个代码库以在GCC上工作。但我不确定GCCs内联ASM是如何工作的。现在我并不擅长将ASM转换为C,否则我就会使用C而不是ASM。 我假设ROR13的工作方式类似于,但代码不会产生相同的输出。 什么是将这个内联ASM翻译成GCC的正确方法,或者这个代码的C翻译是什么?

  • 有没有人要对我说或评论? 跟随生成的程序集 多亏了弄臣的解决之道:

  • 我的理解是,当编写gcc样式的内联asm时,您必须非常具体和准确地了解所有的输入和输出参数(和clobbers),这样编译器就会确切地知道如何为代码分配寄存器,以及它可以对那些寄存器的值和asm代码可能读取和/或修改的任何内存假设什么。编译器使用这些信息尽可能地优化周围的代码(如果它认为内联asm对任何东西都没有影响,甚至完全删除它)。对此不够具体可能会导致不正确的行为,因为编译器是根据您的不正确

  • 我在函数中有以下代码: 现在我不知道为什么这不起作用。Gcc说:“错误:'asm'操作数有不可能的约束”我一直在学习Gcc内联汇编教程,我认为这是将参数从c代码带到内联汇编块的正确方法。 我还使用了为32位x86构建的gcc交叉编译器。