Я реализую свою собственную библиотеку волокон для x86-64.Это частично мотивировано отсутствием стандартного переключения контекста между платформами (GCC / Linux имеет makecontext, который принимает void * s как varargs, а Windows имеет свой оптоволоконный API, который принимает 1 void * arg), а также учебное упражнение по разработке и реализации API,В моем API функция сопрограммы принимает 2 аргумента: контекст сопрограммы и аргумент 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.Я не могу сказать, где я оказался во время отладки, поэтому я, должно быть, делаю что-то не так, например, искажаю БП, IP или SP.Я теряю стек, потому что переключил его, и MSVC не может понять, где мы сейчас находимся.Я довольно заблудился, и мне нужна помощь кого-то, кто имеет опыт в подобных вещах.