段错误内联汇编
Segfault inline assembly
我正在尝试创建一个基于 this tutorial 的绿色线程实现,但是我的开关函数给我一个段错误,因为加载寄存器的代码不是 运行功能。这是我的代码:
void ThreadSwitch(Thread in, Thread out) {
if (!out && !in) {
return;
}
if (out) {
// save registers for out
}
if (in) {
SetCurrentThread(in);
mtx_lock(&in->mutex);
uint64_t rsp = in->cpu.rsp;
uint64_t r15 = in->cpu.r15;
uint64_t r14 = in->cpu.r14;
uint64_t r13 = in->cpu.r13;
uint64_t r12 = in->cpu.r12;
uint64_t rbx = in->cpu.rbx;
uint64_t rbp = in->cpu.rbp;
mtx_unlock(&in->mutex);
asm volatile("mov %[rsp], %%rsp\n"
"mov %[r15], %%r15\n"
"mov %[r14], %%r14\n"
"mov %[r13], %%r13\n"
"mov %[r12], %%r12\n"
"mov %[rbx], %%rbx\n"
"mov %[rbp], %%rbp\n" : : [rsp] "r"(rsp), [r15] "r"(r15), [r14] "r"(r14), [r13] "r"(r13), [r12] "r"(r12), [rbx] "r"(rbx), [rbp] "r"(rbp));
}
}
Xcode 说内联汇编导致了段错误,但我的 lldb 反汇编看起来像这样(你可以忽略其中的 95%,只是为了上下文提供):
0x1000f88b4: movq -0x8(%rbp), %rdi
0x1000f88b8: callq 0x1000f83a0 ; SetCurrentThread at thread.cc:21
0x1000f88bd: movq -0x8(%rbp), %rdi
0x1000f88c1: addq [=11=]x50, %rdi
0x1000f88c8: callq 0x1000f7b80 ; mtx_lock at tct.c:106
0x1000f88cd: movq -0x8(%rbp), %rdi
0x1000f88d1: movq (%rdi), %rdi
0x1000f88d4: movq %rdi, -0x18(%rbp)
0x1000f88d8: movq -0x8(%rbp), %rdi
0x1000f88dc: movq 0x8(%rdi), %rdi
0x1000f88e0: movq %rdi, -0x20(%rbp)
0x1000f88e4: movq -0x8(%rbp), %rdi
0x1000f88e8: movq 0x10(%rdi), %rdi
0x1000f88ec: movq %rdi, -0x28(%rbp)
0x1000f88f0: movq -0x8(%rbp), %rdi
0x1000f88f4: movq 0x18(%rdi), %rdi
0x1000f88f8: movq %rdi, -0x30(%rbp)
0x1000f88fc: movq -0x8(%rbp), %rdi
0x1000f8900: movq 0x20(%rdi), %rdi
0x1000f8904: movq %rdi, -0x38(%rbp)
0x1000f8908: movq -0x8(%rbp), %rdi
0x1000f890c: movq 0x28(%rdi), %rdi
0x1000f8910: movq %rdi, -0x40(%rbp)
0x1000f8914: movq -0x8(%rbp), %rdi
0x1000f8918: movq 0x30(%rdi), %rdi
0x1000f891c: movq %rdi, -0x48(%rbp)
0x1000f8920: movq -0x8(%rbp), %rdi
0x1000f8924: addq [=11=]x50, %rdi
0x1000f892b: movl %eax, -0x54(%rbp)
0x1000f892e: callq 0x1000f7de0 ; mtx_unlock at tct.c:264
0x1000f8933: movq -0x18(%rbp), %rdi ; beginning of inline asm
0x1000f8937: movq -0x20(%rbp), %rcx
0x1000f893b: movq -0x28(%rbp), %rdx
0x1000f893f: movq -0x30(%rbp), %rsi
0x1000f8943: movq -0x38(%rbp), %r8
0x1000f8947: movq -0x40(%rbp), %r9
0x1000f894b: movq -0x48(%rbp), %r10
0x1000f894f: movq %rdi, %rsp
0x1000f8952: movq %rcx, %r15
0x1000f8955: movq %rdx, %r14
0x1000f8958: movq %rsi, %r13
0x1000f895b: movq %r8, %r12
0x1000f895e: movq %r9, %rbx
0x1000f8961: movq %r10, %rbp ; end of inline asm
-> 0x1000f8964: movl %eax, -0x58(%rbp)
0x1000f8967: addq [=11=]x60, %rsp
0x1000f896b: popq %rbp
0x1000f896c: retq
段错误发生在它试图访问堆栈中的内容时,这是有道理的,因为它只是切换了堆栈。但是为什么编译器要插入这个呢?编译器还将 %eax 存储在堆栈的 0x1000f892b 处。编译器是否打开寄存器?因为它在内联 asm 中不使用 %rax。有解决方法吗?
这是在 OSX 10.10.2 上使用 Apple LLVM version 6.0 (clang-600.0.57)
,如果有帮助的话。
提前致谢。
我强烈建议你不要编写依赖于未定义行为的程序。
不允许跳入和跳出内联汇编,因为编译器无法分析它不知道的控制流,在创建线程时,您从无处跳入 asm 语句然后离开它。为了避免这些隐式跳转,您需要在同一 asm 语句中保存和恢复包括 %rip 在内的寄存器。
asm 语句更改的所有寄存器必须列为输出或 clobbers,对于线程切换例程,它是所有未保存值的寄存器,因为它们已被其他线程更改。如果您不这样做,编译器将错误地认为它们没有被更改。
asm 语句必须避免在使用之前覆盖它的输入,在您的代码中没有任何内容禁止编译器将变量 r12 存储在寄存器 %r14 中。
你的锁要么没有意义要么不合适。
完全用汇编编写函数要简单得多,就像您引用的教程中那样。
我正在尝试创建一个基于 this tutorial 的绿色线程实现,但是我的开关函数给我一个段错误,因为加载寄存器的代码不是 运行功能。这是我的代码:
void ThreadSwitch(Thread in, Thread out) {
if (!out && !in) {
return;
}
if (out) {
// save registers for out
}
if (in) {
SetCurrentThread(in);
mtx_lock(&in->mutex);
uint64_t rsp = in->cpu.rsp;
uint64_t r15 = in->cpu.r15;
uint64_t r14 = in->cpu.r14;
uint64_t r13 = in->cpu.r13;
uint64_t r12 = in->cpu.r12;
uint64_t rbx = in->cpu.rbx;
uint64_t rbp = in->cpu.rbp;
mtx_unlock(&in->mutex);
asm volatile("mov %[rsp], %%rsp\n"
"mov %[r15], %%r15\n"
"mov %[r14], %%r14\n"
"mov %[r13], %%r13\n"
"mov %[r12], %%r12\n"
"mov %[rbx], %%rbx\n"
"mov %[rbp], %%rbp\n" : : [rsp] "r"(rsp), [r15] "r"(r15), [r14] "r"(r14), [r13] "r"(r13), [r12] "r"(r12), [rbx] "r"(rbx), [rbp] "r"(rbp));
}
}
Xcode 说内联汇编导致了段错误,但我的 lldb 反汇编看起来像这样(你可以忽略其中的 95%,只是为了上下文提供):
0x1000f88b4: movq -0x8(%rbp), %rdi
0x1000f88b8: callq 0x1000f83a0 ; SetCurrentThread at thread.cc:21
0x1000f88bd: movq -0x8(%rbp), %rdi
0x1000f88c1: addq [=11=]x50, %rdi
0x1000f88c8: callq 0x1000f7b80 ; mtx_lock at tct.c:106
0x1000f88cd: movq -0x8(%rbp), %rdi
0x1000f88d1: movq (%rdi), %rdi
0x1000f88d4: movq %rdi, -0x18(%rbp)
0x1000f88d8: movq -0x8(%rbp), %rdi
0x1000f88dc: movq 0x8(%rdi), %rdi
0x1000f88e0: movq %rdi, -0x20(%rbp)
0x1000f88e4: movq -0x8(%rbp), %rdi
0x1000f88e8: movq 0x10(%rdi), %rdi
0x1000f88ec: movq %rdi, -0x28(%rbp)
0x1000f88f0: movq -0x8(%rbp), %rdi
0x1000f88f4: movq 0x18(%rdi), %rdi
0x1000f88f8: movq %rdi, -0x30(%rbp)
0x1000f88fc: movq -0x8(%rbp), %rdi
0x1000f8900: movq 0x20(%rdi), %rdi
0x1000f8904: movq %rdi, -0x38(%rbp)
0x1000f8908: movq -0x8(%rbp), %rdi
0x1000f890c: movq 0x28(%rdi), %rdi
0x1000f8910: movq %rdi, -0x40(%rbp)
0x1000f8914: movq -0x8(%rbp), %rdi
0x1000f8918: movq 0x30(%rdi), %rdi
0x1000f891c: movq %rdi, -0x48(%rbp)
0x1000f8920: movq -0x8(%rbp), %rdi
0x1000f8924: addq [=11=]x50, %rdi
0x1000f892b: movl %eax, -0x54(%rbp)
0x1000f892e: callq 0x1000f7de0 ; mtx_unlock at tct.c:264
0x1000f8933: movq -0x18(%rbp), %rdi ; beginning of inline asm
0x1000f8937: movq -0x20(%rbp), %rcx
0x1000f893b: movq -0x28(%rbp), %rdx
0x1000f893f: movq -0x30(%rbp), %rsi
0x1000f8943: movq -0x38(%rbp), %r8
0x1000f8947: movq -0x40(%rbp), %r9
0x1000f894b: movq -0x48(%rbp), %r10
0x1000f894f: movq %rdi, %rsp
0x1000f8952: movq %rcx, %r15
0x1000f8955: movq %rdx, %r14
0x1000f8958: movq %rsi, %r13
0x1000f895b: movq %r8, %r12
0x1000f895e: movq %r9, %rbx
0x1000f8961: movq %r10, %rbp ; end of inline asm
-> 0x1000f8964: movl %eax, -0x58(%rbp)
0x1000f8967: addq [=11=]x60, %rsp
0x1000f896b: popq %rbp
0x1000f896c: retq
段错误发生在它试图访问堆栈中的内容时,这是有道理的,因为它只是切换了堆栈。但是为什么编译器要插入这个呢?编译器还将 %eax 存储在堆栈的 0x1000f892b 处。编译器是否打开寄存器?因为它在内联 asm 中不使用 %rax。有解决方法吗?
这是在 OSX 10.10.2 上使用 Apple LLVM version 6.0 (clang-600.0.57)
,如果有帮助的话。
提前致谢。
我强烈建议你不要编写依赖于未定义行为的程序。
不允许跳入和跳出内联汇编,因为编译器无法分析它不知道的控制流,在创建线程时,您从无处跳入 asm 语句然后离开它。为了避免这些隐式跳转,您需要在同一 asm 语句中保存和恢复包括 %rip 在内的寄存器。
asm 语句更改的所有寄存器必须列为输出或 clobbers,对于线程切换例程,它是所有未保存值的寄存器,因为它们已被其他线程更改。如果您不这样做,编译器将错误地认为它们没有被更改。
asm 语句必须避免在使用之前覆盖它的输入,在您的代码中没有任何内容禁止编译器将变量 r12 存储在寄存器 %r14 中。
你的锁要么没有意义要么不合适。
完全用汇编编写函数要简单得多,就像您引用的教程中那样。