From 1020bb292aa0c19c487010aa1506c3d745ded6d5 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 17 Jul 2015 14:31:12 +0200 Subject: [PATCH] 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 --- winsup/cygwin/ChangeLog | 21 +++ winsup/cygwin/common.din | 4 + winsup/cygwin/exceptions.cc | 224 +++++++++++++++++++++++++ winsup/cygwin/include/cygwin/version.h | 5 +- winsup/cygwin/include/ucontext.h | 12 ++ winsup/cygwin/ntdll.h | 1 + winsup/cygwin/release/2.2.0 | 13 ++ winsup/doc/ChangeLog | 7 + winsup/doc/new-features.xml | 12 ++ winsup/doc/posix.xml | 4 + 10 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 winsup/cygwin/release/2.2.0 diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index db7d723c3..f2aabf706 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,24 @@ +2015-07-17 Corinna Vinschen + + * 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. + 2015-07-17 Corinna Vinschen * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Document the diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index 644eb2eae..e89a6bdb7 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -503,6 +503,7 @@ getc SIGFE getc_unlocked SIGFE getchar SIGFE getchar_unlocked SIGFE +getcontext NOSIGFE getcwd SIGFE getdelim = __getdelim SIGFE getdomainname SIGFE @@ -717,6 +718,7 @@ lsetxattr SIGFE lstat SIGFE lutimes SIGFE madvise = posix_madvise SIGFE +makecontext NOSIGFE mallinfo SIGFE malloc SIGFE malloc_stats SIGFE @@ -1054,6 +1056,7 @@ sendmsg = cygwin_sendmsg SIGFE sendto = cygwin_sendto SIGFE setbuf SIGFE setbuffer SIGFE +setcontext NOSIGFE sethostname SIGFE setdtablesize SIGFE setegid SIGFE @@ -1199,6 +1202,7 @@ strtoumax = strtoull NOSIGFE strupr NOSIGFE strxfrm NOSIGFE swab NOSIGFE +swapcontext NOSIGFE swprintf SIGFE swscanf SIGFE symlink SIGFE diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 67df4fec3..eea2be375 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -14,6 +14,7 @@ details. */ #include "miscfuncs.h" #include #include +#include #include #include #include @@ -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); +} diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 7843136b0..d909ec3ce 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -42,7 +42,7 @@ details. */ the Cygwin shared library". This version is used to track important changes to the DLL and is mainly informative in nature. */ -#define CYGWIN_VERSION_DLL_MAJOR 2001 +#define CYGWIN_VERSION_DLL_MAJOR 2002 #define CYGWIN_VERSION_DLL_MINOR 0 /* Major numbers before CYGWIN_VERSION_DLL_EPOCH are @@ -469,13 +469,14 @@ details. */ 285: Export wcstold. 286: Export cabsl, cimagl, creall, finitel, hypotl, sqrtl. 287: Export issetugid. + 288: Export getcontext, makecontext, setcontext, swapcontext. */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 287 +#define CYGWIN_VERSION_API_MINOR 288 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible diff --git a/winsup/cygwin/include/ucontext.h b/winsup/cygwin/include/ucontext.h index 4240597b8..dd44d750b 100644 --- a/winsup/cygwin/include/ucontext.h +++ b/winsup/cygwin/include/ucontext.h @@ -11,6 +11,18 @@ details. */ #ifndef _UCONTEXT_H #define _UCONTEXT_H +#include #include +__BEGIN_DECLS + +extern int getcontext (ucontext_t *) __attribute__((__nonnull__)); +extern int setcontext (const ucontext_t *) __attribute__((__nonnull__)); +extern int swapcontext (ucontext_t *, const ucontext_t *) + __attribute__((__nonnull__)); +extern void makecontext (ucontext_t *, void (*) (void), int, ...) + __attribute__((__nonnull__ (1))); + +__END_DECLS + #endif /* _UCONTEXT_H */ diff --git a/winsup/cygwin/ntdll.h b/winsup/cygwin/ntdll.h index 7d8ccbec3..27a50be39 100644 --- a/winsup/cygwin/ntdll.h +++ b/winsup/cygwin/ntdll.h @@ -1258,6 +1258,7 @@ extern "C" NTSTATUS NTAPI NtCancelTimer (HANDLE, PBOOLEAN); NTSTATUS NTAPI NtClose (HANDLE); NTSTATUS NTAPI NtCommitTransaction (HANDLE, BOOLEAN); + NTSTATUS NTAPI NtContinue (PCONTEXT, BOOLEAN); NTSTATUS NTAPI NtCreateDirectoryObject (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); NTSTATUS NTAPI NtCreateKey (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG, diff --git a/winsup/cygwin/release/2.2.0 b/winsup/cygwin/release/2.2.0 new file mode 100644 index 000000000..7831178e2 --- /dev/null +++ b/winsup/cygwin/release/2.2.0 @@ -0,0 +1,13 @@ +What's new: +----------- + +- New APIs: getcontext, setcontext, makecontext, swapcontext. + + +What changed: +------------- + + +Bug Fixes +--------- + diff --git a/winsup/doc/ChangeLog b/winsup/doc/ChangeLog index b452fa906..a4c30369a 100644 --- a/winsup/doc/ChangeLog +++ b/winsup/doc/ChangeLog @@ -1,3 +1,10 @@ +2015-07-17 Corinna Vinschen + + * new-features.xml (ov-new2.2): Add new section. Document getcontext, + setcontext, makecontext, swapcontext. + * posix.xml (std-deprec): Add getcontext, setcontext, makecontext, + swapcontext. + 2015-07-05 Jon Turney * configure.ac: Add check for DOCBOOK2XTEXI diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index c52574ce4..85d6ec780 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -4,6 +4,18 @@ What's new and what changed in Cygwin +What's new and what changed in 2.2 + + + + +New APIs: getcontext, setcontext, makecontext, swapcontext. + + + + + + What's new and what changed in 2.1 diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml index 8a74f24f0..112ddc8ee 100644 --- a/winsup/doc/posix.xml +++ b/winsup/doc/posix.xml @@ -1322,6 +1322,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). fcvt (SUSv3) ftime (SUSv3) gcvt (SUSv3) + getcontext (SUSv3) gethostbyaddr (SUSv3) gethostbyname (SUSv3) gethostbyname2 (first defined in BIND 4.9.4) @@ -1333,6 +1334,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). getwd (SUSv3) h_errno (SUSv3) index (SUSv3) + makecontext (SUSv3) mallinfo (SVID) mallopt (SVID) mktemp (SUSv3) @@ -1347,8 +1349,10 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). putw (SVID) rindex (SUSv3) scalb (SUSv3) + setcontext (SUSv3) setutent (XPG2) stime (SVID) + swapcontext (SUSv3) sys_errlist (BSD) sys_nerr (BSD) sys_siglist (BSD)