wil*_*ptr 3 c assembly x86-64 abi fibers
我正在用 C 创建纤维线程系统,遵循https://graphitemaster.github.io/ Fibers/ 。我有一个设置和恢复上下文的函数,我想要完成的是将函数作为具有自己的堆栈的 Fiber 启动。Linux、x86_64 SysV ABI。
\nextern void restore_context(struct fiber_context*);\nextern void create_context(struct fiber_context*);\n\nvoid foo_fiber()\n{\n printf("Called as a fiber");\n exit(0);\n}\n\nint main()\n{\n const uint32_t stack_size = 4096 * 16;\n const uint32_t red_zone_abi = 128;\n\n char* stack = aligned_alloc(16, stack_size);\n char* sp = stack + stack_size - red_zone_abi;\n\n struct fiber_context c = {0};\n c.rip = (void*)foo_fiber;\n c.rsp = (void*)sp;\n\n restore_context(&c);\n}\nRun Code Online (Sandbox Code Playgroud)\n其中restore_context代码如下:
\n.type restore_context, @function\n.global restore_context\nrestore_context:\n movq 8*0(%rdi), %r8\n\n # Load new stack pointer.\n movq 8*1(%rdi), %rsp\n\n # Load preserved registers.\n movq 8*2(%rdi), %rbx\n movq 8*3(%rdi), %rbp\n movq 8*4(%rdi), %r12\n movq 8*5(%rdi), %r13\n movq 8*6(%rdi), %r14\n movq 8*7(%rdi), %r15\n\n # Push RIP to stack for RET.\n pushq %r8\n\n xorl %eax, %eax\n ret\nRun Code Online (Sandbox Code Playgroud)\n所以基本上我在堆上创建一个新的堆栈,并且由于堆栈向下增长,我采用结束地址 - 128 字节的红色区域(这在 ABI 中是必需的)。Restore_context 所做的只是将 %rsp 交换到我的新堆栈,并将 foo_optical 的地址压入其中,然后 ret\'s 跳转到 foo_optical 中。(它还从 Fiber_context 结构加载一些寄存器,但现在应该不重要)。
\n从我在 GDB 中看到的情况来看,程序成功地正确跳转到 foo_ Fiber 并进入 printf,然后_vprintf_internal在movaps %xmm1, 0x10(%rsp).
| 0x7ffff7e2f389 <__vfprintf_internal+153> movdqu (%rax),%xmm1 \xe2\x94\x82\n\xe2\x94\x82 0x7ffff7e2f38d <__vfprintf_internal+157> movups %xmm1,0x128(%rsp) \xe2\x94\x82\n\xe2\x94\x82 0x7ffff7e2f395 <__vfprintf_internal+165> mov 0x10(%rax),%rax \xe2\x94\x82\n\xe2\x94\x82 >0x7ffff7e2f399 <__vfprintf_internal+169> movaps %xmm1,0x10(%rsp) \nRun Code Online (Sandbox Code Playgroud)\n我发现这非常奇怪,因为它管理的movups %xmm1, 0x128(%rsp)堆栈指针的偏移量要高得多。那里发生了什么事?
如果我更改 foo_ Fiber 的代码来做其他事情,例如分配并随机填充 char[100],它就可以工作。
\n我对正在发生的事情有点不知所措。起初我以为我可能有对齐问题,因为向量 xmm 函数崩溃了,所以我将 malloc 更改为aligned_alloc。我遇到的崩溃是 SIGSEGV,但是 0x10
\n同意评论:您的堆栈对齐不正确。
确实,堆栈必须对齐到 16 字节。然而,问题是何时? 通常的规则是,在调用 ABI 兼容函数的调用指令处,堆栈指针必须是 16 的倍数。
好吧,您不使用 call 指令,但这真正意味着在进入 ABI 兼容函数时,堆栈指针必须比16 的倍数小 8,或者换句话说,是 8 的奇数倍,因为它假设它是用一条call推送 8 字节返回地址的指令调用的。这与您的代码所做的恰恰相反,因此堆栈与程序的其余部分未对齐,这printf在尝试使用对齐的移动指令时会导致崩溃。
sp您可以从C 代码中的计算结果中减去 8 。
或者,我不太确定为什么您要麻烦地将目标地址加载到寄存器中,然后按下 和ret,而间接跳转或调用就可以了。(除非你故意试图欺骗间接分支预测器?)间接调用也会通过推送返回地址(即使它永远不会被使用)来杀死堆栈对齐鸟。因此,您可以保留其余代码,并将所有 r8/ret 内容替换restore_context为
callq *(8*0)(%rdi)
Run Code Online (Sandbox Code Playgroud)