代码之家  ›  专栏  ›  技术社区  ›  jww avp

引用英特尔语法GNU C内联程序集中的内存操作数

  •  0
  • jww avp  · 技术社区  · 7 年前

    在编译源文件并将其与内联程序集链接时,我捕获到一个链接错误。

    via:$ cat test.cxx
    extern int libtest();
    int main(int argc, char* argv[])
    {
        return libtest();
    }
    
    $ cat lib.cxx
    #include <stdint.h>
    int libtest()
    {
        uint32_t rnds_00_15;    
        __asm__ __volatile__
        (
            ".intel_syntax noprefix         ;\n\t"
            "mov DWORD PTR [rnds_00_15], 1  ;\n\t"
            "cmp DWORD PTR [rnds_00_15], 1  ;\n\t"
            "je  done                       ;\n\t"
            "done:                          ;\n\t"
            ".att_syntax noprefix           ;\n\t"
            :
            : [rnds_00_15] "m" (rnds_00_15)
            : "memory", "cc"
        );
    
        return 0;
    }
    

    编译并链接程序结果:

    via:$ g++ -fPIC test.cxx lib.cxx -c
    via:$ g++ -fPIC lib.o test.o -o test.exe
    lib.o: In function `libtest()':
    lib.cxx:(.text+0x1d): undefined reference to `rnds_00_15'
    lib.cxx:(.text+0x27): undefined reference to `rnds_00_15'
    collect2: error: ld returned 1 exit status
    

    真正的程序更复杂。例行程序超出寄存器,因此标志 rnds_00_15 rnds_00_15号

    为什么我会收到链接错误,如何修复?

    1 回复  |  直到 7 年前
        1
  •  4
  •   Peter Cordes    7 年前

    我不相信Intel语法使用百分号。也许我遗漏了什么?

    你被搞混了 %operand 单一的 % ) ,而不是汇编程序看到的最后一个asm。

    %% 使用文字 "mov %%eax, 1" 在Intel syntax inline asm中,但仍然使用 "mov %0, 1" %[named_operand] .

    https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html . 在Basic asm(没有操作数)中,没有替换,而且%在模板中不特殊,因此您可以编写 mov $1, %eax mov $1, %%eax 在Extended中,如果出于某种原因,您没有使用 mov $1, %[tmp] mov $1, %0 .


    uint32_t rnds_00_15;

    使用 %[rnds_00_15] 和编译 -masm=intel .att_syntax 最后;这将破坏编译器生成后面的asm。)

    DWORD PTR ,因为操作数扩展已经包含了它,例如。 DWORD PTR [rsp - 4] ,并在 DWORD PTR DWORD PTR [rsp - 4] . (气体接受它很好,但第二个接受进动,所以它毫无意义,可能会误导我们。)

    "=m" 如果希望编译器在堆栈上保留一些暂存空间,则输出操作数。不能修改只输入的操作数,即使它在C中未使用。也许编译器决定它可以重叠其他操作数,因为它没有被写入和初始化(即UB)。(我不确定你的 "memory" clobber使其安全,但没有理由不在这里使用早期的clobber输出操作数。)

    并且您希望通过使用 %= 得到一个唯一的号码。

    工作示例(GCC和ICC,但不幸的是不是clang) , on the Godbolt compiler explorer . 您可以使用“二进制模式”(11010按钮)来证明它在编译到asm后实际上是在没有警告的情况下进行汇编的。

    int libtest_intel()
    {
        uint32_t rnds_00_15;
        // Intel syntax operand-size can only be overriden with operand modifiers
        // because the expansion includes an explicit DWORD PTR
        __asm__ __volatile__
        (  // ".intel_syntax noprefix \n\t"
            "mov %[rnds_00_15], 1  \n\t"
            "cmp %[rnds_00_15], 1  \n\t"
            "je  .Ldone%=                 \n\t"
            ".Ldone%=:                    \n\t"
            : [rnds_00_15] "=&m" (rnds_00_15)
            :
            : // no clobbers
        );
        return 0;
    }
    

    编译(使用 gcc -O3 )为了这个小高潮。也与 gcc -m32 当然:

    libtest_intel:
        mov DWORD PTR [rsp-4], 1  
        cmp DWORD PTR [rsp-4], 1  
        je  .Ldone8                 
    .Ldone8:                    
    
        xor     eax, eax
        ret
    

    .intel_syntax noprefix 当我明确地把它放在 .


    你必须使用 %b[tmp] BYTE PTR [rsp-4] 只访问dword输入操作数的低位字节。如果您想做很多这方面的工作,我建议您使用AT&T语法。



    使用 %[rnds_00_15] Error: junk '(%ebp)' after expression.

    那是因为你没有告诉编译器就切换到了英特尔语法。如果你想让它使用英特尔寻址模式, 编译时使用 -masm=英特尔 所以编译器可以用正确的语法替换到模板中。

    这就是为什么我要不惜一切代价避免那个糟糕的GCC内联程序集。伙计,我看不起这个破工具。

    你只是用错了。它有点麻烦,但是如果你理解它是如何设计的,它是有意义的,而且大部分工作得很好。

    在我后面重复: 编译器不分析asm字符串 完全 ,除了做文本替换 . 这就是为什么它没有注意到 .intel_syntax noprefex 并不断替换AT&T语法。

    4 + %[mem] 在AT&T语法中工作)。


    方言选择:

    -masm=英特尔 或者不, use Dialect alternatives

    还演示操作数大小重写

    #include <stdint.h>
    int libtest_override_operand_size()
    {
        uint32_t rnds_00_15;
        // Intel syntax operand-size can only be overriden with operand modifiers
        // because the expansion includes an explicit DWORD PTR
        __asm__ __volatile__
        (
            "{movl $1, %[rnds_00_15] | mov %[rnds_00_15], 1}  \n\t"
            "{cmpl $1, %[rnds_00_15] | cmp %k[rnds_00_15], 1}  \n\t"
            "{cmpw $1, %[rnds_00_15] | cmp %w[rnds_00_15], 1}  \n\t"
            "{cmpb $1, %[rnds_00_15] | cmp %b[rnds_00_15], 1}  \n\t"
            "je  .Ldone%=                     \n\t"
            ".Ldone%=:                        \n\t"
            : [rnds_00_15] "=&m" (rnds_00_15)
        );
        return 0;
    }
    

    使用英特尔语法,gcc将其编译为:

         mov DWORD PTR [rsp-4], 1  
         cmp DWORD PTR [rsp-4], 1  
         cmp WORD PTR [rsp-4], 1  
         cmp BYTE PTR [rsp-4], 1  
        je  .Ldone38                     
    .Ldone38:                        
    
        xor     eax, eax
        ret
    

    使用AT&T语法,编译为:

        movl $1, -4(%rsp)   
        cmpl $1, -4(%rsp)   
        cmpw $1, -4(%rsp)   
        cmpb $1, -4(%rsp)   
        je  .Ldone38                     
    .Ldone38:                        
    
        xorl    %eax, %eax
        ret
    
    推荐文章