ASM约束副作用

时间:2019-03-15 23:03:14

标签: c assembly x86-64 inline-assembly

我很难理解某些特定的影响 GCC中用于内联汇编的约束。

在下面的示例中,如果我在输出上运行“ = X”,在所有输入上运行“ X”,则这2条打印输出

  

0x562f39629260,100

     

0x14,100

这表示指向我分配的缓冲区的指针已更改。导致段错误是尝试读取缓冲区的内容 在我的汇编代码之后。

相反,如果我在输出上输入“ + X”或在输入上输入“ m”, 地址保持不变,打印输出:

  

0x55571bb83260,100

     

0x55571bb83260,100

而且我可以安全地读取我的缓冲区而无需进行段错误。

我不知道如何/为什么应该/应该修改此指针? 有没有办法安全地选择约束? GCC在线文档 对此没有太多见识。

非常感谢

int main() {
    long size = 100;
    char * buffer = (char*)malloc(size*sizeof(char));

    printf("%p, %d\n",buffer, size);

    __asm__(
    "mov %[out], %%rcx \n"
    "mov %[size], %%rbx \n"
    "loop: \n"
    "movb $1, (%%rcx) \n"
    "add $1, %%rcx \n"
    "sub $1, %%rbx \n"
    "jnz loop \n"
    : "=X"(buffer) //outputs
    : [out]"X"(buffer), [size]"X"(size) //inputs
    : "rbx", "rcx" //clobbers
    );

    printf("%p, %d\n",buffer, size);

    return 0;
}

1 个答案:

答案 0 :(得分:2)

=中的=X表示这是仅输出约束(与+的更新约束相反)。这意味着期望汇编代码向操作数(到%0)中写入一些内容,并且这将是输出的值。但是由于您的汇编代码从未写入%0,因此您会在该位置(可能是寄存器分配器选择的寄存器)碰到任何垃圾。

尝试在asm代码中添加mov %%rcx,%0行,以查看实际发生的情况。

最可能您真正想要的是类似的东西:

__asm__ volatile (
"mov %[size], %%rbx \n"
"loop: \n"
"movb $1, (%[out]) \n"
"add $1, %[out] \n"
"sub $1, %%rbx \n"
"jnz loop \n"
: [out]"+r"(buffer) //outputs
: [size]"X"(size) //inputs
: "rbx", "memory" //clobbers
);

请注意,这使buffer指向插入值的后面(在缓冲区的末尾)-不清楚是否是您想要的。您可以对大小做同样的事情,使其更简单:

__asm__ volatile (
"loop: \n"
"movb $1, (%[out]) \n"
"add $1, %[out] \n"
"sub $1, %[size] \n"
"jnz loop \n"
: [out]"+r"(buffer), [size]"+X"(size) //outputs
: //inputs
: "memory" //clobbers
);

虽然根本不使用asm会更简单(对于优化程序来说更好):

do { *buffer++ = '\1'; } while (--size);

因此,总结下面的所有评论,您可能想要的是:

long size = 100;
char buffer[100];
char *temp;
__asm__(
"loop: \n"
"movb $1, (%[out]) \n"
"add $1, %[out] \n"
"sub $1, %[size] \n"
"jnz loop \n"
: [out]"=r"(temp), [size]"+X"(size), "=m"(buffer) //outputs
: "0"(buffer) // inputs
)  // no clobbers
  • 在整个缓冲区上使用"=m"约束,而不是使用内存阻塞和易失性,意味着如果不使用任何结果,则可以消除死代码
  • 使用临时指针使指针前进到缓冲区上方意味着可以保留原始缓冲区(起始)值。
  • 如果必须对缓冲区使用malloc "=m"(*(char (*)[100])buffer)可用于获取整个缓冲区的约束。

但是,我支持我先前的评论,即不使用asm编写脚本会更好。它更容易理解,并且编译器的优化器可能会为您向量化它。