c - ASM 约束副作用

标签 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;
}

最佳答案

==X意味着这是一个 OUTPUT ONLY 约束(与 + 的更新约束相反)。这意味着汇编代码预计会向操作数写入一些内容(写入 %0 ),并且这将是输出的值。但由于您的汇编代码永远不会写入 %0 ,你会得到该位置中发生的任何垃圾(可能是寄存器分配器选择的寄存器)。

尝试添加 mov %%rcx,%0运行 asm 代码看看实际发生了什么。

很可能您真正想要的更像是:

__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"对整个缓冲区的约束而不是内存破坏和 volatile 意味着如果没有使用任何结果,则可以消除死代码
  • 使用临时指针在缓冲区上前进意味着可以保留原始缓冲区(起始)值。
  • 如果必须对缓冲区使用 malloc "=m"(*(char (*)[100])buffer)可用于获取整个缓冲区的约束。

然而,我坚持我之前的评论,即不使用 asm 编写它更好;它更简单、更容易理解,编译器的优化器可能会为您对其进行矢量化。

关于c - ASM 约束副作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55191718/

相关文章:

c - 去除 NL 的最佳方法

c - 为什么 C 运行时不调用我的 exit()?

assembly - assembly 中出现奇怪的字符?

gcc - 如何使用 GCC 4.4.7-17 禁用 bmi 指令

assembly - xmm 寄存器上的字节操作 (AMD64)

c - 可以使用 execvp 和 fork 退出和更改目录的简单 Shell

c - 将双关语输入整数并排序

linux - 比较程序集 x86 中的数字和变量

c - 将一些流水线翻译成 C 代码

assembly - 为什么我在这个基本的汇编程序上会失败?