gcc函式呼叫
進入一個函式後的棧佈局
/*
+----------------------------+
| 第6個引數後的引數(如果有) |
+----------------------------+
| 返回地址(呼叫函式的下一個指令)|
+----------------------------+
| 上一個函式rbp |
+----------------------------+
| 當前函式區域性變數 |
+----------------------------+
*/
makecontext方法實現
void
__makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
{
extern void __start_context (void) attribute_hidden;
extern void __push___start_context (ucontext_t *)
attribute_hidden;
greg_t *sp;
unsigned int idx_uc_link;
va_list ap;
int i;
/* Generate room on stack for parameter if needed and uc_link. */
sp = (greg_t *) ((uintptr_t) ucp->uc_stack.ss_sp
+ ucp->uc_stack.ss_size);
sp -= (argc > 6 ? argc - 6 : 0) + 1; /*預留6個引數後的空間 + 後續ucontext的指標空間*/
/* Align stack and make space for trampoline address. */
/**
* 預留__start_context函式的空間, __start_context會判斷是否結束後是否存在uc_link
* 如果存在就跳轉到下一個ucontext的地址
*/
sp = (greg_t *) ((((uintptr_t) sp) & -16L) - 8); /*
idx_uc_link = (argc > 6 ? argc - 6 : 0) + 1;
/* Setup context ucp. */
/* Address to jump to. */
ucp->uc_mcontext.gregs[REG_RIP] = (uintptr_t) func;
/* Setup rbx.*/
ucp->uc_mcontext.gregs[REG_RBX] = (uintptr_t) &sp[idx_uc_link];
ucp->uc_mcontext.gregs[REG_RSP] = (uintptr_t) sp;
/*
佈局:
+-----------------------+
| next context |
+-----------------------+
| parameter 7-n |
+-----------------------+
| trampoline address |
%rsp -> +-----------------------+
*/
sp[0] = (uintptr_t) &__start_context; /*儲存__start_context函式指標*/
sp[idx_uc_link] = (uintptr_t) ucp->uc_link; /* 儲存下一個ucontext */
va_start (ap, argc);
/* Handle arguments.
The standard says the parameters must all be int values. This is
an historic accident and would be done differently today. For
x86-64 all integer values are passed as 64-bit values and
therefore extending the API to copy 64-bit values instead of
32-bit ints makes sense. It does not break existing
functionality and it does not violate the standard which says
that passing non-int values means undefined behavior. */
for (i = 0; i < argc; ++i)
switch (i)
{
case 0:
ucp->uc_mcontext.gregs[REG_RDI] = va_arg (ap, greg_t);
break;
case 1:
ucp->uc_mcontext.gregs[REG_RSI] = va_arg (ap, greg_t);
break;
case 2:
ucp->uc_mcontext.gregs[REG_RDX] = va_arg (ap, greg_t);
break;
case 3:
ucp->uc_mcontext.gregs[REG_RCX] = va_arg (ap, greg_t);
break;
case 4:
ucp->uc_mcontext.gregs[REG_R8] = va_arg (ap, greg_t);
break;
case 5:
ucp->uc_mcontext.gregs[REG_R9] = va_arg (ap, greg_t);
break;
default:
/* Put value on stack. */
sp[i - 5] = va_arg (ap, greg_t);
break;
}
va_end (ap);
}
__start_context實現
ENTRY(__start_context)
/* This removes the parameters passed to the function given to
'makecontext' from the stack. RBX contains the address
on the stack pointer for the next context. */
movq %rbx, %rsp /* %rbx暫存器儲存下一個ucontext */
/* Don't use pop here so that stack is aligned to 16 bytes. */
movq (%rsp), %rdi /* This is the next context. */
testq %rdi, %rdi
je 2f /* If it is zero exit. */ /* 如果沒有下一個ucontext就退出 */
call __setcontext /*呼叫__setcontext方法 */
/* If this returns (which can happen if the syscall fails) we'll
exit the program with the return error value (-1). */
movq %rax,%rdi
2:
call HIDDEN_JUMPTARGET(exit)
/* The 'exit' call should never return. In case it does cause
the process to terminate. */
L(hlt):
hlt
END(__start_context)
ENTRY(__setcontext)
/* Save argument since syscall will destroy it. */
pushq %rdi
cfi_adjust_cfa_offset(8)
/* Set the signal mask with
rt_sigprocmask (SIG_SETMASK, mask, NULL, _NSIG/8). */
leaq oSIGMASK(%rdi), %rsi
xorl %edx, %edx
movl $SIG_SETMASK, %edi
movl $_NSIG8,%r10d
movl $__NR_rt_sigprocmask, %eax
syscall
/* Pop the pointer into RDX. The choice is arbitrary, but
leaving RDI and RSI available for use later can avoid
shuffling values. */
popq %rdx
cfi_adjust_cfa_offset(-8)
cmpq $-4095, %rax /* Check %rax for error. */
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
/* Restore the floating-point context. Not the registers, only the
rest. */
movq oFPREGS(%rdx), %rcx
fldenv (%rcx)
ldmxcsr oMXCSR(%rdx)
/* Load the new stack pointer, the preserved registers and
registers used for passing args. */
cfi_def_cfa(%rdx, 0)
cfi_offset(%rbx,oRBX)
cfi_offset(%rbp,oRBP)
cfi_offset(%r12,oR12)
cfi_offset(%r13,oR13)
cfi_offset(%r14,oR14)
cfi_offset(%r15,oR15)
cfi_offset(%rsp,oRSP)
cfi_offset(%rip,oRIP)
movq oRSP(%rdx), %rsp
movq oRBX(%rdx), %rbx
movq oRBP(%rdx), %rbp
movq oR12(%rdx), %r12
movq oR13(%rdx), %r13
movq oR14(%rdx), %r14
movq oR15(%rdx), %r15
/* The following ret should return to the address set with
getcontext. Therefore push the address on the stack. */
/* 把ucontext裡的rip存到棧中,執行ret指令就會從棧中取返回地址 */
movq oRIP(%rdx), %rcx
pushq %rcx
movq oRSI(%rdx), %rsi
movq oRDI(%rdx), %rdi
movq oRCX(%rdx), %rcx
movq oR8(%rdx), %r8
movq oR9(%rdx), %r9
/* Setup finally %rdx. */
movq oRDX(%rdx), %rdx
/* End FDE here, we fall into another context. */
cfi_endproc
cfi_startproc
/* Clear rax to indicate success. */
xorl %eax, %eax
ret
PSEUDO_END(__setcontext)
getcontext實現
ENTRY(__getcontext)
/* Save the preserved registers, the registers used for passing
args, and the return address. */
/* 儲存暫存器 */
movq %rbx, oRBX(%rdi)
movq %rbp, oRBP(%rdi)
movq %r12, oR12(%rdi)
movq %r13, oR13(%rdi)
movq %r14, oR14(%rdi)
movq %r15, oR15(%rdi)
movq %rdi, oRDI(%rdi)
movq %rsi, oRSI(%rdi)
movq %rdx, oRDX(%rdi)
movq %rcx, oRCX(%rdi)
movq %r8, oR8(%rdi)
movq %r9, oR9(%rdi)
movq (%rsp), %rcx
movq %rcx, oRIP(%rdi) /* 把當前呼叫getcontext函式後下一條指令的位置儲存到ucontext中的rip變數中 */
leaq 8(%rsp), %rcx /* Exclude the return address. */
movq %rcx, oRSP(%rdi) /* 去掉返回地址後的棧指標位置, 呼叫getcontext函式前的rsp位置 */
/* We have separate floating-point register content memory on the
stack. We use the __fpregs_mem block in the context. Set the
links up correctly. */
leaq oFPREGSMEM(%rdi), %rcx
movq %rcx, oFPREGS(%rdi)
/* Save the floating-point environment. */
fnstenv (%rcx)
fldenv (%rcx)
stmxcsr oMXCSR(%rdi)
/* Save the current signal mask with
rt_sigprocmask (SIG_BLOCK, NULL, set,_NSIG/8). */
leaq oSIGMASK(%rdi), %rdx
xorl %esi,%esi
#if SIG_BLOCK == 0
xorl %edi, %edi
#else
movl $SIG_BLOCK, %edi
#endif
movl $_NSIG8,%r10d
movl $__NR_rt_sigprocmask, %eax
syscall
cmpq $-4095, %rax /* Check %rax for error. */
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
/* All done, return 0 for success. */
xorl %eax, %eax
ret
PSEUDO_END(__getcontext)
swapcontext實現
ENTRY(__swapcontext)
/* Save the preserved registers, the registers used for passing args,
and the return address. */
/* 儲存當前的上下文到第一個ucontext */
movq %rbx, oRBX(%rdi)
movq %rbp, oRBP(%rdi)
movq %r12, oR12(%rdi)
movq %r13, oR13(%rdi)
movq %r14, oR14(%rdi)
movq %r15, oR15(%rdi)
movq %rdi, oRDI(%rdi)
movq %rsi, oRSI(%rdi)
movq %rdx, oRDX(%rdi)
movq %rcx, oRCX(%rdi)
movq %r8, oR8(%rdi)
movq %r9, oR9(%rdi)
movq (%rsp), %rcx
movq %rcx, oRIP(%rdi)
leaq 8(%rsp), %rcx /* Exclude the return address. */
movq %rcx, oRSP(%rdi)
/* We have separate floating-point register content memory on the
stack. We use the __fpregs_mem block in the context. Set the
links up correctly. */
leaq oFPREGSMEM(%rdi), %rcx
movq %rcx, oFPREGS(%rdi)
/* Save the floating-point environment. */
fnstenv (%rcx)
stmxcsr oMXCSR(%rdi)
/* The syscall destroys some registers, save them. */
movq %rsi, %r12
movq %rdi, %r9
/* Save the current signal mask and install the new one with
rt_sigprocmask (SIG_BLOCK, newset, oldset,_NSIG/8). */
leaq oSIGMASK(%rdi), %rdx
leaq oSIGMASK(%rsi), %rsi
movl $SIG_SETMASK, %edi
movl $_NSIG8,%r10d
movl $__NR_rt_sigprocmask, %eax
syscall
cmpq $-4095, %rax /* Check %rax for error. */
jae SYSCALL_ERROR_LABEL /* Jump to error handler if error. */
/* Restore destroyed register into RDX. The choice is arbitrary,
but leaving RDI and RSI available for use later can avoid
shuffling values. */
/* 系統呼叫前把第二個引數rsi儲存到r12, 然後把第二個引數r12儲存到rdx */
movq %r12, %rdx
/* Restore the floating-point context. Not the registers, only the
rest. */
movq oFPREGS(%rdx), %rcx
fldenv (%rcx)
ldmxcsr oMXCSR(%rdx)
/* Load the new stack pointer and the preserved registers. */
movq oRSP(%rdx), %rsp
movq oRBX(%rdx), %rbx
movq oRBP(%rdx), %rbp
movq oR12(%rdx), %r12
movq oR13(%rdx), %r13
movq oR14(%rdx), %r14
movq oR15(%rdx), %r15
/* The following ret should return to the address set with
getcontext. Therefore push the address on the stack. */
movq oRIP(%rdx), %rcx
pushq %rcx
/* Setup registers used for passing args. */
movq oRDI(%rdx), %rdi
movq oRSI(%rdx), %rsi
movq oRCX(%rdx), %rcx
movq oR8(%rdx), %r8
movq oR9(%rdx), %r9
/* Setup finally %rdx. */
movq oRDX(%rdx), %rdx
/* Clear rax to indicate success. */
xorl %eax, %eax
ret
PSEUDO_END(__swapcontext)