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>
This commit is contained in:
Corinna Vinschen 2015-07-17 14:31:12 +02:00
parent 9a69aac0ed
commit 1020bb292a
10 changed files with 301 additions and 2 deletions

View File

@ -1,3 +1,24 @@
2015-07-17 Corinna Vinschen <corinna@vinschen.de>
* 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 <corinna@vinschen.de>
* include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Document the

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -11,6 +11,18 @@ details. */
#ifndef _UCONTEXT_H
#define _UCONTEXT_H
#include <sys/cdefs.h>
#include <sys/ucontext.h>
__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 */

View File

@ -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,

View File

@ -0,0 +1,13 @@
What's new:
-----------
- New APIs: getcontext, setcontext, makecontext, swapcontext.
What changed:
-------------
Bug Fixes
---------

View File

@ -1,3 +1,10 @@
2015-07-17 Corinna Vinschen <corinna@vinschen.de>
* 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 <jon.turney@dronecode.org.uk>
* configure.ac: Add check for DOCBOOK2XTEXI

View File

@ -4,6 +4,18 @@
<sect1 id="ov-new"><title>What's new and what changed in Cygwin</title>
<sect2 id="ov-new2.2"><title>What's new and what changed in 2.2</title>
<itemizedlist mark="bullet">
<listitem><para>
New APIs: getcontext, setcontext, makecontext, swapcontext.
</para></listitem>
</itemizedlist>
</sect2>
<sect2 id="ov-new2.1"><title>What's new and what changed in 2.1</title>
<itemizedlist mark="bullet">

View File

@ -1322,6 +1322,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
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).</para>
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).</para>
putw (SVID)
rindex (SUSv3)
scalb (SUSv3)
setcontext (SUSv3)
setutent (XPG2)
stime (SVID)
swapcontext (SUSv3)
sys_errlist (BSD)
sys_nerr (BSD)
sys_siglist (BSD)