首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >x86-64 / Windows下正确的上下文切换

x86-64 / Windows下正确的上下文切换
EN

Stack Overflow用户
提问于 2018-05-29 10:33:33
回答 1查看 659关注 0票数 0

我正在为x86-64实现我自己的光纤库。它的部分动机是缺乏跨平台的标准上下文切换(GCC/Linux有makecontext,它以void *s为变量,Windows有它的纤程API,它需要1 void * arg)以及API设计和实现的学习练习。在我的API中,一个协程函数有两个参数:一个协程上下文和一个void *参数,所以我正在学习它是如何工作的。我将从调用API开始,它是C。

代码语言:javascript
复制
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实现

代码语言:javascript
复制
;;; _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无法计算出我们现在的位置。我很迷茫,我需要有这方面经验的人的帮助。

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50575559

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档