/* A slightly modified version of boost.context 1.69.
// `self` is updated prior to `to` starts executing. // // RDI: self // RSI: to // RDX: context void jump_context(void** self, void* to, void* context); */
/* Copyright Oliver Kowalke 2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
/* pass `context` as first arg to fiber being run */ /* RDX: third argument to `jump_context`, */ /* RDI: first argument to `start_proc`. */ movq %rdx, %rdi
/* indirect jump to context */ jmp *%r8 .size jump_context,.-jump_context
/* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits
在FiberEntity::Resume函数处,会调用 jump_context:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
inlinevoidFiberEntity::Resume()noexcept{ // Note that there are some inconsistencies. The stack we're running on is not // our stack. This should be easy to see, since we're actually running in // caller's context (including its stack). auto caller = GetCurrentFiberEntity(); FLARE_DCHECK_NE(caller, this, "Calling `Resume()` on self is undefined.");
// Argument `context` (i.e., `this`) is only used the first time the context // is jumped to (in `FiberProc`). jump_context(&caller->state_save_area, state_save_area, this); // 换fiber // ... SetCurrentFiberEntity(caller); // The caller has back.
// Check for pending `ResumeOn`. DestructiveRunCallbackOpt(&caller->resume_proc); }
/* pass `context` as first arg to fiber being run */ /* RDX: third argument to `jump_context`, */ /* RDI: first argument to `start_proc`. */ movq %rdx, %rdi
// Get top (highest address) of the runtime stack (after skipping this // control structure). // // Calling this method on main fiber is undefined. void* GetStackTop()constnoexcept{ // The runtime stack is placed right below us. returnreinterpret_cast<char*>(const_cast<FiberEntity*>(this)); }
/* A slightly modified version of boost.context 1.69. // DO NOT RETURN FROM `start_proc`, THIS LEADS TO CRASH (IN AN UNFRIENDLY // WAY.). // // Returns: Jump target (same as `sp`) for **first** call to `jump_context`. // // RDI: sp // RSI: size (not used) // RDX: start_proc (jump target is passed as parameter) void* make_context(void* sp, std::size_t size, void (*start_proc)(void*)); */
/* Copyright Oliver Kowalke 2009. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */
.file "make_context.S" .text .globl make_context .type make_context,@function .align 16 make_context: /* first arg of make_context() == top of context-stack */ movq %rdi, %rax
/* shift address in RAX to lower 16 byte boundary */ andq $-16, %rax // 低4bit置0, 这里应该是为了stack高地址limit处是16B对齐的, 为什么是16B对齐,不是8B对齐?因为一些寄存器操作需要16B对齐,如xmm寄存器(和浮点数相关)
/* reserve space for context-data on context-stack */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x40(%rax), %rax
/* third arg of make_context() == address of context-function */ /* stored in RBX */ movq %rdx, 0x28(%rax)
/* save MMX control- and status-word */ stmxcsr (%rax) /* save x87 control-word */ fnstcw 0x4(%rax) // 保存和浮点运算相关的状态
/* compute abs address of label trampoline */ leaq trampoline(%rip), %rcx // 相对rip的offset寻址,结果是rcx 中的地址为trampoline label下的第一条指令的地址 /* save address of trampoline as return-address for context-function */ /* will be entered after calling jump_fcontext() first time */ movq %rcx, 0x38(%rax) ret /* return pointer to context-data */ trampoline: /* Crash on return. */ push $0 /* jump to context-function */ jmp *%rbx .size make_context,.-make_context /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits
第一步,把stack有效高地址给rax,并把rax按16B对齐。
1 2 3 4 5
/* first arg of make_context() == top of context-stack */ movq %rdi, %rax
/* shift address in RAX to lower 16 byte boundary */ andq $-16, %rax
第二步,准备context需要的内存, 给这片区域0x40B的内存
1 2 3
/* reserve space for context-data on context-stack */ /* on context-function entry: (RSP -0x8) % 16 == 0 */ leaq -0x40(%rax), %rax
/* compute abs address of label trampoline */ leaq trampoline(%rip), %rcx // 相对offset寻址,rcx 中的地址为trmpoline label下的第一条指令的地址 /* save address of trampoline as return-address for context-function */ /* will be entered after calling jump_fcontext() first time */ movq %rcx, 0x38(%rax)