我正在为x86-64实现我自己的光纤库。它的部分动机是缺乏跨平台的标准上下文切换(GCC/Linux有makecontext,它以void *s为变量,Windows有它的纤程API,它需要1 void * arg)以及API设计和实现的学习练习。在我的API中,一个协程函数有两个参数:一个协程上下文和一个void *参数,所以我正在学习它是如何工作的。我将从调用API开始,它是C。
struct win64_mcontext {
U64 rdi, rsi, rbx, rbp, r12, r13, r14, r15;
U64 rax, rsp, rip;
U64 rcx, rdx, r8, r9;
};
struct coroutine {
struct win64_mcontext caller;
struct win64_mcontext callee;
U32 state;
};
void coprepare(struct coroutine **co,
void *stack, U64 stack_size, cofunc_t func)
{
*co = malloc(sizeof **co); /* TODO: replace with something cheaper */
_coprepare(&(*co)->caller, &(*co)->callee, stack, stack_size, func);
}
void coenter(struct coroutine *co, void *enter_arg)
{
_coenter(&co->caller, &co->callee, enter_arg);
}
void coyield(struct coroutine *co, void *yield_arg)
{
_coyield(&co->callee, &co->caller, yield_arg);
}
int coresume(struct coroutine *co)
{
_coresume(&co->caller, &co->callee);
return 0; /* punt this for now */
}
这是驱动整个过程的组件。_coenter、_coyield和_coresume都作为jmp __cotransfer
实现
;;; _coprepare(struct win64_mcontext *old, struct win64_mcontext *new,
;;; void *stack, U64 stack_size,
;;; cofunc_t func);
;;; RCX -> old
;;; RDX -> new
;;; R8 -> stack
;;; R9 -> stack_size
;;; RSP + ? -> func
_coprepare proc
;; save non-volatile GPRs in 'old'
mov [RCX + OFF_RSI], RSI
mov [RCX + OFF_RDI], RDI
mov [RCX + OFF_RBP], RBP
mov [RCX + OFF_RBX], RBX
mov [RCX + OFF_R12], R12
mov [RCX + OFF_R13], R13
mov [RCX + OFF_R14], R14
mov [RCX + OFF_R15], R15
;; save stack frame info in 'old'
mov R10, RSP
mov R11, OFFSET _coyield
mov [RCX + OFF_RSP], R10
mov [RCX + OFF_RIP], R11
;; init non-volatile GPRs in 'new'
lea R10, [R8 + R9] ; new RSP, = stack + stack_size
lea R11, [RBP - 32] ; load func
xor EAX, EAX
mov [RDX + OFF_RSI], RAX
mov [RDX + OFF_RDI], RAX
mov [RDX + OFF_RBX], RAX
mov [RDX + OFF_RBP], R10
mov [RDX + OFF_R12], RAX
mov [RDX + OFF_R13], RAX
mov [RDX + OFF_R14], RAX
mov [RDX + OFF_R15], RAX
mov [RDX + OFF_RSP], R10
mov [RDX + OFF_RIP], R11
ret
_coprepare endp
;;; __cotransfer(struct win64_context *old, struct win64_mcontext *new, void *trans_arg);
;;; RCX : old
;;; RDX : new
;;; R8 : trans_arg
__cotransfer proc
;; save non-volatile GPRs
mov [RCX + OFF_RSI], RSI
mov [RCX + OFF_RDI], RDI
mov [RCX + OFF_RBX], RBX
mov [RCX + OFF_RBP], RBP
mov [RCX + OFF_R12], R12
mov [RCX + OFF_R13], R13
mov [RCX + OFF_R14], R14
mov [RCX + OFF_R15], R15
;; save argument GPRs
mov [RCX + OFF_RCX], RCX
mov [RCX + OFF_RDX], RDX
mov [RCX + OFF_R8], R8
mov [RCX + OFF_R9], R9
;; save stack frame info
lea R10, [RSP - 8] ; save SP, exclude IP
lea R11, [RSP] ; save IP
mov [RCX + OFF_RSP], R10
mov [RCX + OFF_RIP], R11
;; switch stacks
mov RAX, RSP
mov RSP, [RDX + OFF_RSP]
mov [RCX + OFF_RSP], RAX
;; load non-volatile GPRs
mov RSI, [RDX + OFF_RSI]
mov RDI, [RDX + OFF_RDI]
mov RBX, [RDX + OFF_RBX]
mov RBP, [RDX + OFF_RBP]
mov R12, [RDX + OFF_R12]
mov R13, [RDX + OFF_R13]
mov R14, [RDX + OFF_R14]
mov R15, [RDX + OFF_R15]
;; load argument registers
mov R10, RCX
mov R11, RDX
mov RCX, [R11 + OFF_RCX]
mov RDX, [R11 + OFF_RDX]
mov R8, [R11 + OFF_R8]
mov R9, [R11 + OFF_R9]
; push new return address
mov RAX, [R11 + OFF_RIP]
push RAX
ret ; jump to new return address
__cotransfer endp
我是不是遗漏了什么?它总是在__cotransfer中的某个地方崩溃。我不知道在调试过程中我会在哪里结束,所以我一定是做错了什么,比如弄乱了BP、IP或SP。我丢失了堆栈,因为我交换了它,而MSVC无法计算出我们现在的位置。我很迷茫,我需要有这方面经验的人的帮助。
https://stackoverflow.com/questions/50575559
复制相似问题