C++ 多线程内联汇编
Posted
技术标签:
【中文标题】C++ 多线程内联汇编【英文标题】:C++ multithreaded inline asm 【发布时间】:2015-12-24 21:01:33 【问题描述】:我希望在线程中运行内联 asm,但在某些指令上出现段错误,如下所示:
#include <thread>
void Foo(int &x)
int temp;
asm volatile ("movl $5, %%edx;"
"movl $3, %%eax;"
"addl %%edx, %%eax;"
"movl %%eax, -24(%%rbp);" // seg faults here
"movl -24(%%rbp), %0;"
: "=r" (temp) : : );
x=temp;
int main()
int x;
std::thread t1(Foo, std::ref(x));
t1.join();
return 0;
(我使用std::ref
能够传递对std::thread
的引用,但必须使用temp
变量,因为扩展的asm 语法不适用于引用。)
我尝试破坏所有相关的寄存器,但没有帮助。如果我不向线程传递任何参数,它似乎可以工作:如果我破坏%ebx
寄存器(不涉及),它也可以正常工作。我在 64 位 Ubuntu 14.04 上使用 gcc 4.8.4。
我可以在线程中执行内联 asm 的最佳/最安全的方式是什么?
【问题讨论】:
-24(%%rbp)
来自哪里?在我看来,您可能会破坏堆栈中可能用于函数序言/结尾的内容。 -24(%%rbp) 可能是%rdi
(第一个参数实际上是一个指针)临时存储在堆栈上的位置。你覆盖它,然后我猜x=temp
会出现段错误,因为x
不再有一个有效的指针。但这一切都取决于生成的代码。
您设计的示例任意覆盖了堆栈上可能实际用于稍后使用的信息的位置。破坏堆栈会导致奇怪而奇妙的错误。看起来像一个XY问题。你真正想用这样的代码实现什么?
该代码使用 ATT 语法,前两条指令是正确的,但随后它尝试将 eax 写入 -24[rbp],因此该移动的操作数需要反转。另一个问题是,假设这是 64 位代码和正常的入口代码,那么读取第一个参数是 movq 16(%%rbp),rax。如果通过编译器选项禁用帧指针,则不会设置 rbp。
@rcgldr :回到我关于But that all really depends on the code that gets generated.
优化的评论可能会消除对任何堆栈帧的需求。
为什么你认为在线程上运行它会有所不同?
【参考方案1】:
我仍然不完全相信我了解您的目标是什么。我发现注释“但必须使用临时变量,因为扩展的 asm 语法不适用于引用。”有点不寻常。我希望您可以将引用传递给汇编程序模板,因为它实际上只是引擎盖下的指针。由于您使用的是人为的示例,因此我将保持这一点,但不包括将数据移动到堆栈上的任意位置,并且我会将被破坏的寄存器添加到列表中。你可能会选择这样的东西:
#include <thread>
void Foo(int &x)
asm volatile ("movl $5, %%edx;" // We clobber EDX
"movl $3, %%eax;" // We clobber EAX
"addl %%edx, %%eax;" // Result in EAX=3+5=8
"movl %%eax, %0;" // Move to variable x
: "=r" (x) : : "eax", "edx" );
int main()
int x;
std::thread t1(Foo, std::ref(x));
t1.join();
return 0;
我已将"eax"
和"edx"
添加到clobber 列表中,因为我们在汇编程序模板中销毁它们(并且它们不会显示为输入或输出约束)。您还应该注意到我不使用临时变量。汇编代码可以简化为一条指令,因为人为设计的示例等效于movl $8, %0;
。
您也可以像这样使用x
的内存地址(引用):
void Foo(int &x)
asm volatile ("movl $5, %%edx;" // We clobber EDX
"movl $3, %%eax;" // We clobber EAX
"addl %%edx, %%eax;" // Result in EAX=3+5=8
"movl %%eax, %0;" // Move to variable x
: "=mr" (x) : : "eax", "edx" );
在这种情况下,我使用=mr
(内存操作数或寄存器)作为输出约束。这允许我们将值向右移动到内存操作数,而无需中间寄存器。
【讨论】:
我会使用"=mr"
约束,让编译器可以选择放置它的位置。因此,例如,在内联时它不会强制存储到内存中。或=r
并使用它而不是暂存寄存器之一。此外,您可以使用像 int tmp
这样的 C 变量作为输出操作数(您从不在 C 代码中使用),让编译器选择您的内联汇编将使用哪些暂存寄存器。我在过去几天写了一些内联 asm 答案,稍后会挖掘链接。
@PeterCordes 我同意"=mr"
。问题是他使用了一个人为的例子(他的话),其中真正的含义是未知的。正如我在我的问题中所说,我并没有试图做任何比他人为的例子所做的更多的事情。正如我指出的那样,他的人为示例可以简化为没有任何中间体的单一指令。我的引述:“汇编代码可以简化为一条指令,因为人为设计的示例相当于movl $8, %0
”。
@PeterCordes :我并非不知道指定输入用作临时寄存器。鉴于 OP 关于clobbers 的评论,我故意将它们保留在其中。如果 OP 没有提供人为的示例,并且我们知道他们正在尝试做什么,那么我们可以找到一种有效的方法来做到这一点。看来这个OP有办法理解。我的一个问题是暗示这听起来像是一个 XY 问题,我要求澄清但从未出现。以上是关于C++ 多线程内联汇编的主要内容,如果未能解决你的问题,请参考以下文章