diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2015-07-17 14:31:12 +0200 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2015-07-17 14:31:12 +0200 |
commit | 1020bb292aa0c19c487010aa1506c3d745ded6d5 (patch) | |
tree | 6d3b470e4823cf05e8577b251063500b72f49187 /winsup/cygwin/exceptions.cc | |
parent | 9a69aac0ed556d1141626b987bc4c0c877fdb169 (diff) | |
download | newlib-1020bb292aa0c19c487010aa1506c3d745ded6d5.zip newlib-1020bb292aa0c19c487010aa1506c3d745ded6d5.tar.gz newlib-1020bb292aa0c19c487010aa1506c3d745ded6d5.tar.bz2 |
Implement getcontext, setcontext, makecontext, swapcontext
* common.din (getcontext): Export.
(makecontext): Export.
(setcontext): Export.
(swapcontext): Export.
* exceptions.cc (__unwind_single_frame): New static functions, 64 bit
only.
(setcontext): New function.
(getcontext): New function.
(swapcontext): New function.
(__cont_link_context): New function.
(makecontext): New function.
* include/cygwin/version.h (CYGWIN_VERSION_DLL_MAJOR): Bump to 2002.
(CYGWIN_VERSION_API_MINOR): Bump.
* include/ucontext.h (getcontext): Add prototype.
(setcontext): Ditto.
(swapcontext): Ditto.
(makecontext): Ditto.
* ntdll.h (NtContinue): Ditto.
* new-features.xml (ov-new2.2): Add new section. Document getcontext,
setcontext, makecontext, swapcontext.
* posix.xml (std-deprec): Add getcontext, setcontext, makecontext,
swapcontext.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r-- | winsup/cygwin/exceptions.cc | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 67df4fe..eea2be3 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -14,6 +14,7 @@ details. */ #include "miscfuncs.h" #include <imagehlp.h> #include <stdlib.h> +#include <stdarg.h> #include <syslog.h> #include <wchar.h> #include <ucontext.h> @@ -1863,3 +1864,226 @@ _cygtls::signal_debugger (siginfo_t& si) ResumeThread (th); } } + +#ifdef __x86_64__ +static inline void +__unwind_single_frame (PCONTEXT ctx) +{ + /* Amazing, but true: On 32 bit, RtlCaptureContext returns the context + matching the caller of getcontext, so all we have to do is call it. + On 64 bit, RtlCaptureContext returns the exact context of its own + caller, so we have to unwind virtually by a single frame to get the + context of the caller of getcontext. */ + PRUNTIME_FUNCTION f; + ULONG64 imagebase; + UNWIND_HISTORY_TABLE hist; + DWORD64 establisher; + PVOID hdl; + + f = RtlLookupFunctionEntry (ctx->Rip, &imagebase, &hist); + if (f) + RtlVirtualUnwind (0, imagebase, ctx->Rip, f, ctx, &hdl, &establisher, + NULL); + else + { + ctx->Rip = *(ULONG_PTR *) ctx->Rsp; + ctx->Rsp += 8; + } +} +#endif + +extern "C" int +setcontext (const ucontext_t *ucp) +{ + PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext; + _my_tls.sigmask = ucp->uc_sigmask; +#ifdef __x86_64__ + /* Apparently a call to NtContinue works on 64 bit as well, but using + RtlRestoreContext is the blessed way. */ + RtlRestoreContext (ctx, NULL); +#else + NtContinue (ctx, FALSE); +#endif + /* If we got here, something was wrong. */ + set_errno (EINVAL); + return -1; +} + +#ifdef __x86_64__ + +extern "C" int +getcontext (ucontext_t *ucp) +{ + PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext; + ctx->ContextFlags = CONTEXT_FULL; + RtlCaptureContext (ctx); + __unwind_single_frame (ctx); + /* Successful getcontext is supposed to return 0. If we don't set rax to 0 + here, there's a chance that code like this: + + if (getcontext (&ctx) != 0) + + assumes that getcontext failed after calling setcontext (&ctx). + Same goes for eax on 32 bit, see assembler implementation below. */ + ucp->uc_mcontext.rax = 0; + ucp->uc_sigmask = ucp->uc_mcontext.oldmask = _my_tls.sigmask; + /* Do not touch any other member of ucontext_t. */ + return 0; +} + +extern "C" int +swapcontext (ucontext_t *oucp, const ucontext_t *ucp) +{ + PCONTEXT ctx = (PCONTEXT) &oucp->uc_mcontext; + ctx->ContextFlags = CONTEXT_FULL; + RtlCaptureContext (ctx); + __unwind_single_frame (ctx); + /* See above. */ + oucp->uc_mcontext.rax = 0; + oucp->uc_sigmask = oucp->uc_mcontext.oldmask = _my_tls.sigmask; + return setcontext (ucp); +} + +/* Trampoline function to set the context to uc_link. The pointer to the + address of uc_link is stored in the callee-saved register $rbx. If uc_link + is NULL, call exit. */ +__asm__ (" \n\ + .global __cont_link_context \n\ +__cont_link_context: \n\ + movq %rbx, %rsp \n\ + popq %rcx \n\ + testq %rcx, %rcx \n\ + je 1f \n\ + call setcontext \n\ + movq $0xff, %rcx \n\ +1: \n\ + call cygwin_exit \n\ + nop \n\ + "); + +#else + +/* On 32 bit it's crucial to call RtlCaptureContext in a way which makes sure + the callee-saved registers, especially $ebx, are not changed by the calling + function. If so, makecontext/__cont_link_context would be broken. + + Both functions are split into the first half in assembler, and the second + half in C to allow easy access to _my_tls. */ + +extern "C" int +__getcontext (ucontext_t *ucp) +{ + ucp->uc_mcontext.eax = 0; + ucp->uc_sigmask = ucp->uc_mcontext.oldmask = _my_tls.sigmask; + return 0; +} + +__asm__ (" \n\ + .global _getcontext \n\ +_getcontext: \n\ + pushl %ebp \n\ + movl %esp, %ebp \n\ + movl 8(%esp), %eax \n\ + pushl %eax \n\ + call _RtlCaptureContext@4 \n\ + popl %ebp \n\ + jmp ___getcontext \n\ + nop \n\ + "); + +extern "C" int +__swapcontext (ucontext_t *oucp, const ucontext_t *ucp) +{ + oucp->uc_mcontext.eax = 0; + oucp->uc_sigmask = oucp->uc_mcontext.oldmask = _my_tls.sigmask; + return setcontext (ucp); +} + +__asm__ (" \n\ + .global _swapcontext \n\ +_swapcontext: \n\ + pushl %ebp \n\ + movl %esp, %ebp \n\ + movl 8(%esp), %eax \n\ + pushl %eax \n\ + call _RtlCaptureContext@4 \n\ + popl %ebp \n\ + jmp ___swapcontext \n\ + nop \n\ + "); + +/* Trampoline function to set the context to uc_link. The pointer to the + address of uc_link is stored in the callee-saved register $ebx. If uc_link + is NULL, call exit. */ +__asm__ (" \n\ + .global ___cont_link_context \n\ +___cont_link_context: \n\ + movl %ebx, %esp \n\ + movl (%esp), %eax \n\ + testl %eax, %eax \n\ + je 1f \n\ + call _setcontext \n\ + movl $0xff, (%esp) \n\ +1: \n\ + call _cygwin_exit \n\ + nop \n\ + "); +#endif + +/* makecontext is modelled after GLibc's makecontext. The stack from uc_stack + is prepared so that it starts with a pointer to the linked context uc_link, + followed by the arguments to func, and finally at the bottom the "return" + address set to __cont_link_context. In the ucp context, rbx/ebx is set to + point to the stack address where the pointer to uc_link is stored. The + requirement to make this work is that rbx/ebx are callee-saved registers + per the ABI. If any function is called which doesn't follow the ABI + conventions, e.g. assembler code, this method will break. But that's ok. */ +extern "C" void +makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...) +{ + extern void __cont_link_context (void); + uintptr_t *sp; + va_list ap; + + sp = (uintptr_t *) ((uintptr_t) ucp->uc_stack.ss_sp + + ucp->uc_stack.ss_size); + sp -= (argc + 1); + sp = (uintptr_t *) ((uintptr_t) sp & ~0xf); + --sp; + sp[0] = (uintptr_t) __cont_link_context; + sp[argc + 1] = (uintptr_t) ucp->uc_link; +#ifdef __x86_64__ + ucp->uc_mcontext.rip = (uint64_t) func; + ucp->uc_mcontext.rbx = (uint64_t) (sp + argc + 1); + ucp->uc_mcontext.rsp = (uint64_t) sp; +#else + ucp->uc_mcontext.eip = (uint32_t) func; + ucp->uc_mcontext.ebx = (uint32_t) (sp + argc + 1); + ucp->uc_mcontext.esp = (uint32_t) sp; +#endif + va_start (ap, argc); + for (int i = 0; i < argc; ++i) +#ifdef __x86_64__ + switch (i) + { + case 0: + ucp->uc_mcontext.rcx = va_arg (ap, uintptr_t); + break; + case 1: + ucp->uc_mcontext.rdx = va_arg (ap, uintptr_t); + break; + case 2: + ucp->uc_mcontext.r8 = va_arg (ap, uintptr_t); + break; + case 3: + ucp->uc_mcontext.r9 = va_arg (ap, uintptr_t); + break; + default: + sp[i + 1] = va_arg (ap, uintptr_t); + break; + } +#else + sp[i + 1] = va_arg (ap, uintptr_t); +#endif + va_end (ap); +} |