Cygwin: drop i686 exception handling

Leave x86_64 CPU-specific code and #error out when trying to build
for another target. Access special registers CPU-agnostic.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2022-05-17 14:12:32 +02:00
parent 830a9b707c
commit 93bea9f538
3 changed files with 72 additions and 484 deletions

View File

@ -276,7 +276,6 @@ private:
extern _cygtls *_main_tls;
extern _cygtls *_sig_tls;
#ifdef __x86_64__
class san
{
san *_clemente;
@ -305,53 +304,9 @@ public:
earlier in the function, so this call serves as a register barrier. */
void leave () __attribute__ ((returns_twice));
};
#else
class san
{
san *_clemente;
jmp_buf _context;
uint32_t _c_cnt;
uint32_t _w_cnt;
public:
int setup () __attribute__ ((always_inline))
{
_clemente = _my_tls.andreas;
_my_tls.andreas = this;
_c_cnt = _my_tls.locals.pathbufs.c_cnt;
_w_cnt = _my_tls.locals.pathbufs.w_cnt;
return __sjfault (_context);
}
void leave () __attribute__ ((always_inline))
{
/* Restore tls_pathbuf counters in case of error. */
_my_tls.locals.pathbufs.c_cnt = _c_cnt;
_my_tls.locals.pathbufs.w_cnt = _w_cnt;
__ljfault (_context, 1);
}
void reset () __attribute__ ((always_inline))
{
_my_tls.andreas = _clemente;
}
};
class myfault
{
san sebastian;
public:
~myfault () __attribute__ ((always_inline)) { sebastian.reset (); }
inline int faulted () __attribute__ ((always_inline))
{
return sebastian.setup ();
}
};
#endif
/* Exception handling macros. These are required because SEH differs a lot
between 32 and 64 bit. Essentially, on 64 bit, we have to create compile
time SEH tables which define the handler and try/except labels, while on
32 bit we can simply set up an SJLJ handler within the myfault class. */
/* Exception handling macros. This is a handmade SEH try/except. */
#define __mem_barrier __asm__ __volatile__ ("" ::: "memory")
#ifdef __x86_64__
#define __try \
{ \
__label__ __l_try, __l_except, __l_endtry; \
@ -387,31 +342,6 @@ public:
__mem_barrier; \
}
#else /* !__x86_64__ */
#define __try \
{ \
__label__ __l_endtry; \
myfault efault; \
if (!efault.faulted ()) \
{
#define __leave \
goto __l_endtry
#define __except(__errno) \
goto __l_endtry; \
} \
{ \
if (__errno) \
set_errno (__errno);
#define __endtry \
} \
__l_endtry: \
__mem_barrier; \
}
#endif /* __x86_64__ */
class wait_signal_arrived
{
public:

View File

@ -6,125 +6,6 @@ details. */
#pragma once
#ifdef __i386__
/* Documentation on the innards of 32 bit Windows exception handling (i.e.
from the perspective of a compiler implementor) apparently doesn't exist.
However, the following came from Onno Hovers <onno@stack.urc.tue.nl>
The first pointer to the chain of handlers is in the thread environment block
at FS:[0]. This chain has the following format:
typedef struct __EXCEPTION_FRAME
{
struct __EXCEPTION_FRAME *Prev; /-* pointer to the previous frame *-/
PEXCEPTION_HANDLER Handler; /-* handler function *-/
}
You register an exception handler in your compiler with this simple ASM
sequence:
PUSH _MyExceptionHandler
PUSH FS:[0]
MOV FS:[0],ESP
An exception frame MUST be on the stack! The frame may have more fields and
both Visual C++ and Borland C++ use more fields for themselves.
When an exception occurs the system calls all handlers starting with the
handler at FS:0, and then the previous etc. until one handler returns
ExceptionContinueExecution, which is 0. If a handler does not want to handle
the exception it should just return ExceptionContinueSearch, which is 1.
The handler has the following parameters:
ehandler (
PEXCEPTION_RECORD erecord,
PEXCEPTION_FRAME myframe,
PCONTEXT context, /-* context before and after *-/
PVOID dispatch) /-* something *-/
When a handler wants to handle the exception, it has some alternatives:
-one is to do do something about the exception condition, like emulating
an invalid instruction, mapping memory where there was a page fault, etc.
If the handler wants to have the context of the thread that causes the
exception changed, it should make that change in the context passed to the
handler.
-the second alternative is to call all exception handlers again, indicating
that you want them to clean up. This way all the __finally blocks get
executed. After doing that you change the context passed to the handler so
the code starts executing in the except block. For this purpose you could
call RtlUnwind. This (undocumented) function calls all exception handlers
up to but not including the exception frame passed to it. If NULL is passed
as exception frame RtlUnwind calls all exception handlers and then exits the
process. The parameters to RtlUnwind are:
RtlUnwind (
PEXCEPTION_FRAME endframe,
PVOID unusedEip,
PEXCEPTION_RECORD erecord,
DWORD returnEax)
You should set unusedEip to the address where RtlUnwind should return like
this:
PUSH 0
PUSH OFFSET ReturnUnwind
PUSH 0
PUSH 0
CALL RtlUnwind
ReturnUnwind:
.....
If no EXCEPTION_RECORD is passed, RtlUnwind makes a default exception
record. In any case, the ExceptionFlags part of this record has the
EH_UNWINDING (=2), flag set. (and EH_EXIT_UNWIND (=4), when NULL is passed as the end
frame.).
The handler for a exception as well as a for unwinds may be executed in the
thread causing the exception, but may also be executed in another (special
exception) thread. So it is not wise to make any assumptions about that!
As an alternative you may consider the SetUnhandledExceptionFilter API
to install your own exception filter. This one is documented.
*/
/* The January 1994 MSJ has an article entitled "Clearer, More Comprehensive
Error Processing with Win32 Structured Exception Handling". It goes into
a teensy bit of detail of the innards of exception handling (i.e. what we
have to do). */
typedef EXCEPTION_DISPOSITION (exception_handler) (EXCEPTION_RECORD *,
struct _exception_list *,
CONTEXT *,
void *);
typedef struct _exception_list
{
struct _exception_list *prev;
exception_handler *handler;
} exception_list;
extern exception_list *_except_list asm ("%fs:0");
typedef void *PDISPATCHER_CONTEXT;
class exception
{
exception_list el;
exception_list *save;
static EXCEPTION_DISPOSITION handle (EXCEPTION_RECORD *, exception_list *,
CONTEXT *, PDISPATCHER_CONTEXT);
public:
exception () __attribute__ ((always_inline))
{
/* Install SEH handler. */
save = _except_list;
el.handler = handle;
el.prev = _except_list;
_except_list = &el;
};
~exception () __attribute__ ((always_inline)) { _except_list = save; }
};
#else /* !__i386__ */
#define exception_list void
typedef struct _DISPATCHER_CONTEXT *PDISPATCHER_CONTEXT;
@ -159,8 +40,6 @@ public:
LONG CALLBACK myfault_altstack_handler (EXCEPTION_POINTERS *);
#endif /* __i386__ */
class cygwin_exception
{
PUINT_PTR framep;

View File

@ -30,15 +30,21 @@ details. */
#include "posix_timer.h"
#include "gcc_seh.h"
/* Definitions for code simplification */
/* Define macros for CPU-agnostic register access. The _CX_foo
macros are for access into CONTEXT, the _MC_foo ones for access into
mcontext. The idea is to access the registers in terms of their job,
not in terms of their name on the given target. */
#ifdef __x86_64__
# define _GR(reg) R ## reg
# define _AFMT "%011X"
# define _ADDR DWORD64
#define _CX_instPtr Rip
#define _CX_stackPtr Rsp
#define _CX_framePtr Rbp
/* For special register access inside mcontext. */
#define _MC_retReg rax
#define _MC_instPtr rip
#define _MC_stackPtr rsp
#define _MC_uclinkReg rbx /* MUST be callee-saved reg */
#else
# define _GR(reg) E ## reg
# define _AFMT "%08x"
# define _ADDR DWORD
#error unimplemented for this target
#endif
#define CALL_HANDLER_RETRY_OUTER 10
@ -204,20 +210,12 @@ cygwin_exception::dump_exception ()
small_printf ("rbp=%016X rsp=%016X\r\n", ctx->Rbp, ctx->Rsp);
small_printf ("program=%W, pid %u, thread %s\r\n",
myself->progname, myself->pid, mythreadname ());
#else
if (exception_name)
small_printf ("Exception: %s at eip=%08x\r\n", exception_name, ctx->Eip);
else
small_printf ("Signal %d at eip=%08x\r\n", e->ExceptionCode, ctx->Eip);
small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx, ctx->Esi, ctx->Edi);
small_printf ("ebp=%08x esp=%08x program=%W, pid %u, thread %s\r\n",
ctx->Ebp, ctx->Esp, myself->progname, myself->pid,
mythreadname ());
#endif
small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
ctx->SegCs, ctx->SegDs, ctx->SegEs, ctx->SegFs,
ctx->SegGs, ctx->SegSs);
#else
#error unimplemented for this target
#endif
}
/* A class for manipulating the stack. */
@ -227,11 +225,9 @@ class stack_info
char *next_offset () {return *((char **) sf.AddrFrame.Offset);}
bool needargs;
PUINT_PTR dummy_frame;
#ifdef __x86_64__
CONTEXT c;
UNWIND_HISTORY_TABLE hist;
__tlsstack_t *sigstackptr;
#endif
public:
STACKFRAME sf; /* For storing the stack information */
void init (PUINT_PTR, bool, PCONTEXT); /* Called the first time that stack info is needed */
@ -250,7 +246,6 @@ static NO_COPY stack_info thestack;
void
stack_info::init (PUINT_PTR framep, bool wantargs, PCONTEXT ctx)
{
#ifdef __x86_64__
memset (&hist, 0, sizeof hist);
if (ctx)
memcpy (&c, ctx, sizeof c);
@ -260,7 +255,6 @@ stack_info::init (PUINT_PTR framep, bool wantargs, PCONTEXT ctx)
c.ContextFlags = CONTEXT_ALL;
}
sigstackptr = _my_tls.stackptr;
#endif
memset (&sf, 0, sizeof (sf));
if (ctx)
sf.AddrFrame.Offset = (UINT_PTR) framep;
@ -277,7 +271,6 @@ stack_info::init (PUINT_PTR framep, bool wantargs, PCONTEXT ctx)
extern "C" void _cygwin_exit_return ();
#ifdef __x86_64__
static inline void
__unwind_single_frame (PCONTEXT ctx)
{
@ -287,19 +280,16 @@ __unwind_single_frame (PCONTEXT ctx)
DWORD64 establisher;
PVOID hdl;
f = RtlLookupFunctionEntry (ctx->Rip, &imagebase, &hist);
f = RtlLookupFunctionEntry (ctx->_CX_instPtr, &imagebase, &hist);
if (f)
RtlVirtualUnwind (0, imagebase, ctx->Rip, f, ctx, &hdl, &establisher,
NULL);
RtlVirtualUnwind (0, imagebase, ctx->_CX_instPtr, f, ctx, &hdl,
&establisher, NULL);
else
{
ctx->Rip = *(ULONG_PTR *) ctx->Rsp;
ctx->Rsp += 8;
ctx->_CX_instPtr = *(ULONG_PTR *) ctx->_CX_stackPtr;
ctx->_CX_stackPtr += 8;
}
}
#else
#define __unwind_single_frame(ctx)
#endif
/* Walk the stack.
@ -308,57 +298,30 @@ __unwind_single_frame (PCONTEXT ctx)
int
stack_info::walk ()
{
#ifdef __x86_64__
if (!c.Rip)
if (!c._CX_instPtr)
return 0;
sf.AddrPC.Offset = c.Rip;
sf.AddrStack.Offset = c.Rsp;
sf.AddrFrame.Offset = c.Rbp;
sf.AddrPC.Offset = c._CX_instPtr;
sf.AddrStack.Offset = c._CX_stackPtr;
sf.AddrFrame.Offset = c._CX_framePtr;
if ((c.Rip >= (DWORD64)&_sigbe) && (c.Rip < (DWORD64)&_sigdelayed_end))
if ((c._CX_instPtr >= (DWORD64)&_sigbe)
&& (c._CX_instPtr < (DWORD64)&_sigdelayed_end))
{
/* _sigbe and sigdelayed don't have SEH unwinding data, so virtually
unwind the tls sigstack */
c.Rip = sigstackptr[-1];
c._CX_instPtr = sigstackptr[-1];
sigstackptr--;
return 1;
}
__unwind_single_frame (&c);
if (needargs && c.Rip)
if (needargs && c._CX_instPtr)
{
PULONG_PTR p = (PULONG_PTR) c.Rsp;
PULONG_PTR p = (PULONG_PTR) c._CX_stackPtr;
for (unsigned i = 0; i < NPARAMS; ++i)
sf.Params[i] = p[i + 1];
}
return 1;
#else
char **framep;
if ((void (*) ()) sf.AddrPC.Offset == _cygwin_exit_return)
return 0; /* stack frames are exhausted */
if (((framep = (char **) next_offset ()) == NULL)
|| (framep >= (char **) cygwin_hmodule))
return 0;
sf.AddrFrame.Offset = (_ADDR) framep;
sf.AddrPC.Offset = sf.AddrReturn.Offset;
/* The return address always follows the stack pointer */
sf.AddrReturn.Offset = (_ADDR) *++framep;
if (needargs)
{
unsigned nparams = NPARAMS;
/* The arguments follow the return address */
sf.Params[0] = (_ADDR) *++framep;
for (unsigned i = 1; i < nparams; i++)
sf.Params[i] = (_ADDR) *++framep;
}
return 1;
#endif
}
void
@ -379,17 +342,13 @@ cygwin_exception::dumpstack ()
int i;
thestack.init (framep, 1, ctx); /* Initialize from the input CONTEXT */
#ifdef __x86_64__
small_printf ("Stack trace:\r\nFrame Function Args\r\n");
#else
small_printf ("Stack trace:\r\nFrame Function Args\r\n");
#endif
for (i = 0; i < DUMPSTACK_FRAME_LIMIT && thestack++; i++)
{
small_printf (_AFMT " " _AFMT, thestack.sf.AddrFrame.Offset,
small_printf ("%011X %011X", thestack.sf.AddrFrame.Offset,
thestack.sf.AddrPC.Offset);
for (unsigned j = 0; j < NPARAMS; j++)
small_printf ("%s" _AFMT, j == 0 ? " (" : ", ",
small_printf ("%s%011X", j == 0 ? " (" : ", ",
thestack.sf.Params[j]);
small_printf (")\r\n");
}
@ -413,8 +372,8 @@ _cygtls::inside_kernel (CONTEXT *cx)
return true;
memset (&m, 0, sizeof m);
if (!VirtualQuery ((LPCVOID) cx->_GR(ip), &m, sizeof m))
sigproc_printf ("couldn't get memory info, pc %p, %E", cx->_GR(ip));
if (!VirtualQuery ((LPCVOID) cx->_CX_instPtr, &m, sizeof m))
sigproc_printf ("couldn't get memory info, pc %p, %E", cx->_CX_instPtr);
size_t size = (windows_system_directory_length + 6) * sizeof (WCHAR);
PWCHAR checkdir = (PWCHAR) alloca (size);
@ -439,7 +398,7 @@ _cygtls::inside_kernel (CONTEXT *cx)
res = wcsncasecmp (windows_system_directory, checkdir,
windows_system_directory_length) == 0;
}
sigproc_printf ("pc %p, h %p, inside_kernel %d", cx->_GR(ip), h, res);
sigproc_printf ("pc %p, h %p, inside_kernel %d", cx->_CX_instPtr, h, res);
# undef h
return res;
}
@ -451,7 +410,7 @@ cygwin_stackdump ()
CONTEXT c;
c.ContextFlags = CONTEXT_FULL;
RtlCaptureContext (&c);
cygwin_exception exc ((PUINT_PTR) c._GR(bp), &c);
cygwin_exception exc ((PUINT_PTR) c._CX_framePtr, &c);
exc.dumpstack ();
}
@ -548,35 +507,6 @@ try_to_debug ()
return dbg;
}
#ifdef __x86_64__
/* Don't unwind the stack on x86_64. It's not necessary to do that from the
exception handler. */
#define rtl_unwind(el,er)
#else
static void __reg3 rtl_unwind (exception_list *, PEXCEPTION_RECORD)
__attribute__ ((noinline, regparm (3)));
void __reg3
rtl_unwind (exception_list *frame, PEXCEPTION_RECORD e)
{
__asm__ ("\n\
pushl %%ebx \n\
pushl %%edi \n\
pushl %%esi \n\
pushl $0 \n\
pushl %1 \n\
pushl $1f \n\
pushl %0 \n\
call _RtlUnwind@16 \n\
1: \n\
popl %%esi \n\
popl %%edi \n\
popl %%ebx \n\
": : "r" (frame), "r" (e));
}
#endif /* __x86_64 */
#ifdef __x86_64__
/* myfault exception handler. */
EXCEPTION_DISPOSITION
exception::myfault (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
@ -610,16 +540,14 @@ myfault_altstack_handler (EXCEPTION_POINTERS *exc)
/* Unwind the stack manually and call RtlRestoreContext. This
is necessary because RtlUnwindEx checks the stack for validity,
which, as outlined above, fails for the alternate stack. */
while (c->Rsp < me.andreas->frame)
while (c->_CX_stackPtr < me.andreas->frame)
__unwind_single_frame (c);
c->Rip = me.andreas->ret;
c->_CX_instPtr = me.andreas->ret;
RtlRestoreContext (c, NULL);
}
return EXCEPTION_CONTINUE_SEARCH;
}
#endif
/* Main exception handler. */
EXCEPTION_DISPOSITION
exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
@ -628,11 +556,6 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
static int NO_COPY debugging = 0;
_cygtls& me = _my_tls;
#ifdef __i386__
if (me.andreas)
me.andreas->leave (); /* Return from a "san" caught fault */
#endif
if (debugging && ++debugging < 500000)
{
SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
@ -759,7 +682,6 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
handling. */
return ExceptionContinueExecution;
#ifdef __x86_64__
case STATUS_GCC_THROW:
case STATUS_GCC_UNWIND:
case STATUS_GCC_FORCED:
@ -767,7 +689,6 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
_Unwind_RaiseException(), GCC expects us to continue all the
(continuable) GCC exceptions that reach us. */
return ExceptionContinueExecution;
#endif
default:
/* If we don't recognize the exception, we have to assume that
@ -777,40 +698,21 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
}
debug_printf ("In cygwin_except_handler exception %y at %p sp %p",
e->ExceptionCode, in->_GR(ip), in->_GR(sp));
e->ExceptionCode, in->_CX_instPtr, in->_CX_stackPtr);
debug_printf ("In cygwin_except_handler signal %d at %p",
si.si_signo, in->_GR(ip));
si.si_signo, in->_CX_instPtr);
#ifdef __x86_64__
PUINT_PTR framep = (PUINT_PTR) in->Rbp;
/* Sometimes, when a stack is screwed up, Rbp tends to be NULL. In that
case, base the stacktrace on Rsp. In most cases, it allows to generate
useful stack trace. */
PUINT_PTR framep = (PUINT_PTR) in->_CX_framePtr;
/* Sometimes, when a stack is screwed up, the frame pointer tends to be NULL.
In that case, base the stacktrace on the stack pointer. In most cases,
it allows to generate useful stack trace. */
if (!framep)
framep = (PUINT_PTR) in->Rsp;
#else
PUINT_PTR framep = (PUINT_PTR) in->_GR(sp);
for (PUINT_PTR bpend = (PUINT_PTR) __builtin_frame_address (0);
framep > bpend;
framep--)
if (*framep == in->SegCs && framep[-1] == in->_GR(ip))
{
framep -= 2;
break;
}
/* Temporarily replace windows top level SEH with our own handler.
We don't want any Windows magic kicking in. This top level frame
will be removed automatically after our exception handler returns. */
_except_list->handler = handle;
#endif
framep = (PUINT_PTR) in->_CX_stackPtr;
if (exit_state >= ES_SIGNAL_EXIT
&& (NTSTATUS) e->ExceptionCode != STATUS_CONTROL_C_EXIT)
api_fatal ("Exception during process exit");
else if (!try_to_debug ())
rtl_unwind (frame, e);
else
else if (try_to_debug ())
{
debugging = 1;
return ExceptionContinueExecution;
@ -823,7 +725,7 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
be the address of the faulting instruction. Other signals are apparently
undefined so we just set those to the faulting instruction too. */
si.si_addr = (si.si_signo == SIGSEGV || si.si_signo == SIGBUS)
? (void *) e->ExceptionInformation[1] : (void *) in->_GR(ip);
? (void *) e->ExceptionInformation[1] : (void *) in->_CX_instPtr;
me.incyg++;
sig_send (NULL, si, &me); /* Signal myself */
if ((NTSTATUS) e->ExceptionCode == STATUS_STACK_OVERFLOW)
@ -928,7 +830,7 @@ _cygtls::interrupt_now (CONTEXT *cx, siginfo_t& si, void *handler,
interrupted = false;
else
{
_ADDR &ip = cx->_GR(ip);
DWORD64 &ip = cx->_CX_instPtr;
push (ip);
interrupt_setup (si, handler, siga);
ip = pop ();
@ -1364,13 +1266,8 @@ signal_exit (int sig, siginfo_t *si, void *)
{
CONTEXT c;
c.ContextFlags = CONTEXT_FULL;
#ifdef __x86_64__
RtlCaptureContext (&c);
cygwin_exception exc ((PUINT_PTR) __builtin_frame_address (0), &c);
#else
GetThreadContext (GetCurrentThread (), &c);
cygwin_exception exc ((PUINT_PTR) __builtin_frame_address (0), &c);
#endif
exc.dumpstack ();
}
break;
@ -1691,7 +1588,7 @@ _cygtls::call_signal_handler ()
{
/* Software-generated signal. We're fetching the current
context, unwind to the caller and in case we're called
from sigdelayed, fix rip/eip accordingly. */
from sigdelayed, fix the instruction pointer accordingly. */
context.uc_mcontext.ctxflags = CONTEXT_FULL;
RtlCaptureContext ((PCONTEXT) &context.uc_mcontext);
__unwind_single_frame ((PCONTEXT) &context.uc_mcontext);
@ -1700,7 +1597,7 @@ _cygtls::call_signal_handler ()
#ifdef __x86_64__
context.uc_mcontext.rip = retaddr ();
#else
context.uc_mcontext.eip = retaddr ();
#error unimplemented for this target
#endif
}
}
@ -1808,39 +1705,7 @@ _cygtls::call_signal_handler ()
[WRAPPER] "o" (altstack_wrapper)
: "memory");
#else
/* Clobbered regs: ecx, edx, ebp, esp */
__asm__ ("\n\
movl %[NEW_SP], %%eax # Load alt stack into eax \n\
subl $32, %%eax # Make room on alt stack for \n\
# clobbered regs and args to \n\
# signal handler \n\
movl %%ecx, 16(%%eax) # Save clobbered regs \n\
movl %%edx, 20(%%eax) \n\
movl %%ebp, 24(%%eax) \n\
movl %%esp, 28(%%eax) \n\
movl %[SIG], %%ecx # thissig to 1st arg slot \n\
movl %%ecx, (%%eax) \n\
leal %[SI], %%ecx # &thissi to 2nd arg slot \n\
movl %%ecx, 4(%%eax) \n\
movl %[CTX], %%ecx # thiscontext to 3rd arg slot\n\
movl %%ecx, 8(%%eax) \n\
movl %[FUNC], %%ecx # thisfunc to 4th arg slot \n\
movl %%ecx, 12(%%eax) \n\
leal %[WRAPPER], %%ecx # thisfunc to ecx \n\
movl %%eax, %%esp # Move alt stack into esp \n\
call *%%ecx # Call thisfunc \n\
movl %%esp, %%eax # Restore clobbered regs \n\
movl 28(%%eax), %%esp \n\
movl 24(%%eax), %%ebp \n\
movl 20(%%eax), %%edx \n\
movl 16(%%eax), %%eax \n"
: : [NEW_SP] "o" (new_sp),
[SIG] "o" (thissig),
[SI] "o" (thissi),
[CTX] "o" (thiscontext),
[FUNC] "o" (thisfunc),
[WRAPPER] "o" (altstack_wrapper)
: "memory");
#error unimplemented for this target
#endif
}
else
@ -1885,11 +1750,7 @@ _cygtls::signal_debugger (siginfo_t& si)
if (GetThreadContext (th, &c))
{
if (incyg)
#ifdef __x86_64__
c.Rip = retaddr ();
#else
c.Eip = retaddr ();
#endif
c._CX_instPtr = retaddr ();
memcpy (&context.uc_mcontext, &c, sizeof (CONTEXT));
/* Enough space for 32/64 bit addresses */
char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING
@ -1908,20 +1769,12 @@ setcontext (const ucontext_t *ucp)
PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext;
set_signal_mask (_my_tls.sigmask, ucp->uc_sigmask);
_my_tls.incyg = true;
#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)
{
@ -1929,14 +1782,13 @@ getcontext (ucontext_t *ucp)
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:
/* Successful getcontext is supposed to return 0. If we don't set the
return register 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;
assumes that getcontext failed after calling setcontext (&ctx). */
ucp->uc_mcontext._MC_retReg = 0;
ucp->uc_sigmask = ucp->uc_mcontext.oldmask = _my_tls.sigmask;
/* Do not touch any other member of ucontext_t. */
return 0;
@ -1950,14 +1802,16 @@ swapcontext (ucontext_t *oucp, const ucontext_t *ucp)
RtlCaptureContext (ctx);
__unwind_single_frame (ctx);
/* See comment in getcontext. */
oucp->uc_mcontext.rax = 0;
oucp->uc_mcontext._MC_retReg = 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. */
address of uc_link is stored in a callee-saved register, referenced by
_MC_uclinkReg from the C code. If uc_link is NULL, call exit. */
#ifdef __x86_64__
/* _MC_uclinkReg == %rbx */
__asm__ (" \n\
.global __cont_link_context \n\
.seh_proc __cont_link_context \n\
@ -1979,77 +1833,7 @@ __cont_link_context: \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.
Amazing, but true: While on 64 bit RtlCaptureContext returns the exact
context of its own caller, as expected, on 32 bit RtlCaptureContext returns
the context of the callers caller. So while we have to unwind another frame
on 64 bit, we can skip this step on 32 bit.
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\
");
#error unimplemented for this target
#endif
/* makecontext is modelled after GLibc's makecontext. The stack from uc_stack
@ -2079,7 +1863,7 @@ makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
sp[0] = (uintptr_t) __cont_link_context;
/* Fetch arguments and store them on the stack.
x86_64 only:
x86_64:
- Store first four args in the AMD64 ABI arg registers.
@ -2114,23 +1898,18 @@ makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
break;
}
#else
sp[i + 1] = va_arg (ap, uintptr_t);
#error unimplemented for this target
#endif
va_end (ap);
/* Store pointer to uc_link at the top of the stack. */
sp[argc + 1] = (uintptr_t) ucp->uc_link;
/* Last but not least set the register in the context at ucp so that a
subsequent setcontext or swapcontext picks up the right values:
- Set rip/eip to the target function.
- Set rsp/esp to the just computed stack pointer value.
- Set rbx/ebx to the address of the pointer to uc_link. */
#ifdef __x86_64__
ucp->uc_mcontext.rip = (uint64_t) func;
ucp->uc_mcontext.rsp = (uint64_t) sp;
ucp->uc_mcontext.rbx = (uint64_t) (sp + argc + 1);
#else
ucp->uc_mcontext.eip = (uint32_t) func;
ucp->uc_mcontext.esp = (uint32_t) sp;
ucp->uc_mcontext.ebx = (uint32_t) (sp + argc + 1);
#endif
- Set instruction pointer to the target function.
- Set stack pointer to the just computed stack pointer value.
- Set Cygwin-specific uclink register to the address of the pointer
to uc_link. */
ucp->uc_mcontext._MC_instPtr = (uint64_t) func;
ucp->uc_mcontext._MC_stackPtr = (uint64_t) sp;
ucp->uc_mcontext._MC_uclinkReg = (uint64_t) (sp + argc + 1);
}