ASM 约束副作用
ASM constraints side effects
我很难理解某些特定的效果
GCC 中对内联汇编的约束。
在下面的示例中,如果我 运行 在输出上使用“=X”并且在所有输入上使用 "X",则 2 打印输出
0x562f39629260, 100
0x14, 100
这表明指向我分配的缓冲区的指针已更改。导致段错误的是尝试读取缓冲区的内容
在我的汇编代码之后。
相反,如果我在输出中输入“+X”或在输入中输入"m",那么
地址保持不变,打印输出:
0x55571bb83260, 100
0x55571bb83260, 100
而且我可以安全地读取我的缓冲区而不会出现段错误。
我不明白这个指针 should/could 是如何或为什么被修改的?
有没有办法安全地选择约束? 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 , (%%rcx) \n"
"add , %%rcx \n"
"sub , %%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
,因此您会得到恰好位于该位置的任何垃圾(可能是寄存器分配器选择的寄存器)。
尝试在 asm 代码中添加 mov %%rcx,%0
行以查看实际发生的情况。
很可能您真正想要的是:
__asm__ volatile (
"mov %[size], %%rbx \n"
"loop: \n"
"movb , (%[out]) \n"
"add , %[out] \n"
"sub , %%rbx \n"
"jnz loop \n"
: [out]"+r"(buffer) //outputs
: [size]"X"(size) //inputs
: "rbx", "memory" //clobbers
);
请注意,这会使 buffer
指向插入的值之后(在缓冲区的末尾)——不清楚这是否是您想要的。你可以对大小做同样的事情,让它变得更简单:
__asm__ volatile (
"loop: \n"
"movb , (%[out]) \n"
"add , %[out] \n"
"sub , %[size] \n"
"jnz loop \n"
: [out]"+r"(buffer), [size]"+X"(size) //outputs
: //inputs
: "memory" //clobbers
);
虽然根本不使用 asm 会更简单(对优化器来说更好):
do { *buffer++ = ''; } while (--size);
所以总结下面的所有评论,您可能想要的是:
long size = 100;
char buffer[100];
char *temp;
__asm__(
"loop: \n"
"movb , (%[out]) \n"
"add , %[out] \n"
"sub , %[size] \n"
"jnz loop \n"
: [out]"=r"(temp), [size]"+X"(size), "=m"(buffer) //outputs
: "0"(buffer) // inputs
) // no clobbers
- 在整个缓冲区上使用
"=m"
约束而不是内存破坏和易失性意味着如果使用 none 结果可以消除死代码
- 对在缓冲区上前进的指针使用临时意味着可以保留原始缓冲区(起始)值。
- 如果必须对缓冲区使用 malloc
"=m"(*(char (*)[100])buffer)
可用于获取整个缓冲区的约束。
但是,我坚持我之前的评论,即不使用 asm 编写它更好;它更简单易懂,编译器的优化器可能会为您对其进行矢量化处理。
我很难理解某些特定的效果 GCC 中对内联汇编的约束。
在下面的示例中,如果我 运行 在输出上使用“=X”并且在所有输入上使用 "X",则 2 打印输出
0x562f39629260, 100
0x14, 100
这表明指向我分配的缓冲区的指针已更改。导致段错误的是尝试读取缓冲区的内容 在我的汇编代码之后。
相反,如果我在输出中输入“+X”或在输入中输入"m",那么 地址保持不变,打印输出:
0x55571bb83260, 100
0x55571bb83260, 100
而且我可以安全地读取我的缓冲区而不会出现段错误。
我不明白这个指针 should/could 是如何或为什么被修改的? 有没有办法安全地选择约束? 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 , (%%rcx) \n"
"add , %%rcx \n"
"sub , %%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
,因此您会得到恰好位于该位置的任何垃圾(可能是寄存器分配器选择的寄存器)。
尝试在 asm 代码中添加 mov %%rcx,%0
行以查看实际发生的情况。
很可能您真正想要的是:
__asm__ volatile (
"mov %[size], %%rbx \n"
"loop: \n"
"movb , (%[out]) \n"
"add , %[out] \n"
"sub , %%rbx \n"
"jnz loop \n"
: [out]"+r"(buffer) //outputs
: [size]"X"(size) //inputs
: "rbx", "memory" //clobbers
);
请注意,这会使 buffer
指向插入的值之后(在缓冲区的末尾)——不清楚这是否是您想要的。你可以对大小做同样的事情,让它变得更简单:
__asm__ volatile (
"loop: \n"
"movb , (%[out]) \n"
"add , %[out] \n"
"sub , %[size] \n"
"jnz loop \n"
: [out]"+r"(buffer), [size]"+X"(size) //outputs
: //inputs
: "memory" //clobbers
);
虽然根本不使用 asm 会更简单(对优化器来说更好):
do { *buffer++ = ''; } while (--size);
所以总结下面的所有评论,您可能想要的是:
long size = 100;
char buffer[100];
char *temp;
__asm__(
"loop: \n"
"movb , (%[out]) \n"
"add , %[out] \n"
"sub , %[size] \n"
"jnz loop \n"
: [out]"=r"(temp), [size]"+X"(size), "=m"(buffer) //outputs
: "0"(buffer) // inputs
) // no clobbers
- 在整个缓冲区上使用
"=m"
约束而不是内存破坏和易失性意味着如果使用 none 结果可以消除死代码 - 对在缓冲区上前进的指针使用临时意味着可以保留原始缓冲区(起始)值。
- 如果必须对缓冲区使用 malloc
"=m"(*(char (*)[100])buffer)
可用于获取整个缓冲区的约束。
但是,我坚持我之前的评论,即不使用 asm 编写它更好;它更简单易懂,编译器的优化器可能会为您对其进行矢量化处理。