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 *_main_tls;
extern _cygtls *_sig_tls; extern _cygtls *_sig_tls;
#ifdef __x86_64__
class san class san
{ {
san *_clemente; san *_clemente;
@ -305,53 +304,9 @@ public:
earlier in the function, so this call serves as a register barrier. */ earlier in the function, so this call serves as a register barrier. */
void leave () __attribute__ ((returns_twice)); 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 /* Exception handling macros. This is a handmade SEH try/except. */
{
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. */
#define __mem_barrier __asm__ __volatile__ ("" ::: "memory") #define __mem_barrier __asm__ __volatile__ ("" ::: "memory")
#ifdef __x86_64__
#define __try \ #define __try \
{ \ { \
__label__ __l_try, __l_except, __l_endtry; \ __label__ __l_try, __l_except, __l_endtry; \
@ -387,31 +342,6 @@ public:
__mem_barrier; \ __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 class wait_signal_arrived
{ {
public: public:

View File

@ -6,125 +6,6 @@ details. */
#pragma once #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 #define exception_list void
typedef struct _DISPATCHER_CONTEXT *PDISPATCHER_CONTEXT; typedef struct _DISPATCHER_CONTEXT *PDISPATCHER_CONTEXT;
@ -159,8 +40,6 @@ public:
LONG CALLBACK myfault_altstack_handler (EXCEPTION_POINTERS *); LONG CALLBACK myfault_altstack_handler (EXCEPTION_POINTERS *);
#endif /* __i386__ */
class cygwin_exception class cygwin_exception
{ {
PUINT_PTR framep; PUINT_PTR framep;

View File

@ -30,15 +30,21 @@ details. */
#include "posix_timer.h" #include "posix_timer.h"
#include "gcc_seh.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__ #ifdef __x86_64__
# define _GR(reg) R ## reg #define _CX_instPtr Rip
# define _AFMT "%011X" #define _CX_stackPtr Rsp
# define _ADDR DWORD64 #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 #else
# define _GR(reg) E ## reg #error unimplemented for this target
# define _AFMT "%08x"
# define _ADDR DWORD
#endif #endif
#define CALL_HANDLER_RETRY_OUTER 10 #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 ("rbp=%016X rsp=%016X\r\n", ctx->Rbp, ctx->Rsp);
small_printf ("program=%W, pid %u, thread %s\r\n", small_printf ("program=%W, pid %u, thread %s\r\n",
myself->progname, myself->pid, mythreadname ()); 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", 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->SegCs, ctx->SegDs, ctx->SegEs, ctx->SegFs,
ctx->SegGs, ctx->SegSs); ctx->SegGs, ctx->SegSs);
#else
#error unimplemented for this target
#endif
} }
/* A class for manipulating the stack. */ /* A class for manipulating the stack. */
@ -227,11 +225,9 @@ class stack_info
char *next_offset () {return *((char **) sf.AddrFrame.Offset);} char *next_offset () {return *((char **) sf.AddrFrame.Offset);}
bool needargs; bool needargs;
PUINT_PTR dummy_frame; PUINT_PTR dummy_frame;
#ifdef __x86_64__
CONTEXT c; CONTEXT c;
UNWIND_HISTORY_TABLE hist; UNWIND_HISTORY_TABLE hist;
__tlsstack_t *sigstackptr; __tlsstack_t *sigstackptr;
#endif
public: public:
STACKFRAME sf; /* For storing the stack information */ STACKFRAME sf; /* For storing the stack information */
void init (PUINT_PTR, bool, PCONTEXT); /* Called the first time that stack info is needed */ 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 void
stack_info::init (PUINT_PTR framep, bool wantargs, PCONTEXT ctx) stack_info::init (PUINT_PTR framep, bool wantargs, PCONTEXT ctx)
{ {
#ifdef __x86_64__
memset (&hist, 0, sizeof hist); memset (&hist, 0, sizeof hist);
if (ctx) if (ctx)
memcpy (&c, ctx, sizeof c); memcpy (&c, ctx, sizeof c);
@ -260,7 +255,6 @@ stack_info::init (PUINT_PTR framep, bool wantargs, PCONTEXT ctx)
c.ContextFlags = CONTEXT_ALL; c.ContextFlags = CONTEXT_ALL;
} }
sigstackptr = _my_tls.stackptr; sigstackptr = _my_tls.stackptr;
#endif
memset (&sf, 0, sizeof (sf)); memset (&sf, 0, sizeof (sf));
if (ctx) if (ctx)
sf.AddrFrame.Offset = (UINT_PTR) framep; 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 (); extern "C" void _cygwin_exit_return ();
#ifdef __x86_64__
static inline void static inline void
__unwind_single_frame (PCONTEXT ctx) __unwind_single_frame (PCONTEXT ctx)
{ {
@ -287,19 +280,16 @@ __unwind_single_frame (PCONTEXT ctx)
DWORD64 establisher; DWORD64 establisher;
PVOID hdl; PVOID hdl;
f = RtlLookupFunctionEntry (ctx->Rip, &imagebase, &hist); f = RtlLookupFunctionEntry (ctx->_CX_instPtr, &imagebase, &hist);
if (f) if (f)
RtlVirtualUnwind (0, imagebase, ctx->Rip, f, ctx, &hdl, &establisher, RtlVirtualUnwind (0, imagebase, ctx->_CX_instPtr, f, ctx, &hdl,
NULL); &establisher, NULL);
else else
{ {
ctx->Rip = *(ULONG_PTR *) ctx->Rsp; ctx->_CX_instPtr = *(ULONG_PTR *) ctx->_CX_stackPtr;
ctx->Rsp += 8; ctx->_CX_stackPtr += 8;
} }
} }
#else
#define __unwind_single_frame(ctx)
#endif
/* Walk the stack. /* Walk the stack.
@ -308,57 +298,30 @@ __unwind_single_frame (PCONTEXT ctx)
int int
stack_info::walk () stack_info::walk ()
{ {
#ifdef __x86_64__ if (!c._CX_instPtr)
if (!c.Rip)
return 0; return 0;
sf.AddrPC.Offset = c.Rip; sf.AddrPC.Offset = c._CX_instPtr;
sf.AddrStack.Offset = c.Rsp; sf.AddrStack.Offset = c._CX_stackPtr;
sf.AddrFrame.Offset = c.Rbp; 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 /* _sigbe and sigdelayed don't have SEH unwinding data, so virtually
unwind the tls sigstack */ unwind the tls sigstack */
c.Rip = sigstackptr[-1]; c._CX_instPtr = sigstackptr[-1];
sigstackptr--; sigstackptr--;
return 1; return 1;
} }
__unwind_single_frame (&c); __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) for (unsigned i = 0; i < NPARAMS; ++i)
sf.Params[i] = p[i + 1]; sf.Params[i] = p[i + 1];
} }
return 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 void
@ -379,17 +342,13 @@ cygwin_exception::dumpstack ()
int i; int i;
thestack.init (framep, 1, ctx); /* Initialize from the input CONTEXT */ thestack.init (framep, 1, ctx); /* Initialize from the input CONTEXT */
#ifdef __x86_64__
small_printf ("Stack trace:\r\nFrame Function Args\r\n"); 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++) 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); thestack.sf.AddrPC.Offset);
for (unsigned j = 0; j < NPARAMS; j++) for (unsigned j = 0; j < NPARAMS; j++)
small_printf ("%s" _AFMT, j == 0 ? " (" : ", ", small_printf ("%s%011X", j == 0 ? " (" : ", ",
thestack.sf.Params[j]); thestack.sf.Params[j]);
small_printf (")\r\n"); small_printf (")\r\n");
} }
@ -413,8 +372,8 @@ _cygtls::inside_kernel (CONTEXT *cx)
return true; return true;
memset (&m, 0, sizeof m); memset (&m, 0, sizeof m);
if (!VirtualQuery ((LPCVOID) cx->_GR(ip), &m, sizeof m)) if (!VirtualQuery ((LPCVOID) cx->_CX_instPtr, &m, sizeof m))
sigproc_printf ("couldn't get memory info, pc %p, %E", cx->_GR(ip)); sigproc_printf ("couldn't get memory info, pc %p, %E", cx->_CX_instPtr);
size_t size = (windows_system_directory_length + 6) * sizeof (WCHAR); size_t size = (windows_system_directory_length + 6) * sizeof (WCHAR);
PWCHAR checkdir = (PWCHAR) alloca (size); PWCHAR checkdir = (PWCHAR) alloca (size);
@ -439,7 +398,7 @@ _cygtls::inside_kernel (CONTEXT *cx)
res = wcsncasecmp (windows_system_directory, checkdir, res = wcsncasecmp (windows_system_directory, checkdir,
windows_system_directory_length) == 0; 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 # undef h
return res; return res;
} }
@ -451,7 +410,7 @@ cygwin_stackdump ()
CONTEXT c; CONTEXT c;
c.ContextFlags = CONTEXT_FULL; c.ContextFlags = CONTEXT_FULL;
RtlCaptureContext (&c); RtlCaptureContext (&c);
cygwin_exception exc ((PUINT_PTR) c._GR(bp), &c); cygwin_exception exc ((PUINT_PTR) c._CX_framePtr, &c);
exc.dumpstack (); exc.dumpstack ();
} }
@ -548,35 +507,6 @@ try_to_debug ()
return dbg; 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. */ /* myfault exception handler. */
EXCEPTION_DISPOSITION EXCEPTION_DISPOSITION
exception::myfault (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, 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 /* Unwind the stack manually and call RtlRestoreContext. This
is necessary because RtlUnwindEx checks the stack for validity, is necessary because RtlUnwindEx checks the stack for validity,
which, as outlined above, fails for the alternate stack. */ 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); __unwind_single_frame (c);
c->Rip = me.andreas->ret; c->_CX_instPtr = me.andreas->ret;
RtlRestoreContext (c, NULL); RtlRestoreContext (c, NULL);
} }
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
#endif
/* Main exception handler. */ /* Main exception handler. */
EXCEPTION_DISPOSITION EXCEPTION_DISPOSITION
exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, 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; static int NO_COPY debugging = 0;
_cygtls& me = _my_tls; _cygtls& me = _my_tls;
#ifdef __i386__
if (me.andreas)
me.andreas->leave (); /* Return from a "san" caught fault */
#endif
if (debugging && ++debugging < 500000) if (debugging && ++debugging < 500000)
{ {
SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL); SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
@ -759,7 +682,6 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
handling. */ handling. */
return ExceptionContinueExecution; return ExceptionContinueExecution;
#ifdef __x86_64__
case STATUS_GCC_THROW: case STATUS_GCC_THROW:
case STATUS_GCC_UNWIND: case STATUS_GCC_UNWIND:
case STATUS_GCC_FORCED: 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 _Unwind_RaiseException(), GCC expects us to continue all the
(continuable) GCC exceptions that reach us. */ (continuable) GCC exceptions that reach us. */
return ExceptionContinueExecution; return ExceptionContinueExecution;
#endif
default: default:
/* If we don't recognize the exception, we have to assume that /* 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", 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", 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->_CX_framePtr;
PUINT_PTR framep = (PUINT_PTR) in->Rbp; /* Sometimes, when a stack is screwed up, the frame pointer tends to be NULL.
/* Sometimes, when a stack is screwed up, Rbp tends to be NULL. In that In that case, base the stacktrace on the stack pointer. In most cases,
case, base the stacktrace on Rsp. In most cases, it allows to generate it allows to generate useful stack trace. */
useful stack trace. */
if (!framep) if (!framep)
framep = (PUINT_PTR) in->Rsp; framep = (PUINT_PTR) in->_CX_stackPtr;
#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
if (exit_state >= ES_SIGNAL_EXIT if (exit_state >= ES_SIGNAL_EXIT
&& (NTSTATUS) e->ExceptionCode != STATUS_CONTROL_C_EXIT) && (NTSTATUS) e->ExceptionCode != STATUS_CONTROL_C_EXIT)
api_fatal ("Exception during process exit"); api_fatal ("Exception during process exit");
else if (!try_to_debug ()) else if (try_to_debug ())
rtl_unwind (frame, e);
else
{ {
debugging = 1; debugging = 1;
return ExceptionContinueExecution; 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 be the address of the faulting instruction. Other signals are apparently
undefined so we just set those to the faulting instruction too. */ undefined so we just set those to the faulting instruction too. */
si.si_addr = (si.si_signo == SIGSEGV || si.si_signo == SIGBUS) 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++; me.incyg++;
sig_send (NULL, si, &me); /* Signal myself */ sig_send (NULL, si, &me); /* Signal myself */
if ((NTSTATUS) e->ExceptionCode == STATUS_STACK_OVERFLOW) if ((NTSTATUS) e->ExceptionCode == STATUS_STACK_OVERFLOW)
@ -928,7 +830,7 @@ _cygtls::interrupt_now (CONTEXT *cx, siginfo_t& si, void *handler,
interrupted = false; interrupted = false;
else else
{ {
_ADDR &ip = cx->_GR(ip); DWORD64 &ip = cx->_CX_instPtr;
push (ip); push (ip);
interrupt_setup (si, handler, siga); interrupt_setup (si, handler, siga);
ip = pop (); ip = pop ();
@ -1364,13 +1266,8 @@ signal_exit (int sig, siginfo_t *si, void *)
{ {
CONTEXT c; CONTEXT c;
c.ContextFlags = CONTEXT_FULL; c.ContextFlags = CONTEXT_FULL;
#ifdef __x86_64__
RtlCaptureContext (&c); RtlCaptureContext (&c);
cygwin_exception exc ((PUINT_PTR) __builtin_frame_address (0), &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 (); exc.dumpstack ();
} }
break; break;
@ -1691,7 +1588,7 @@ _cygtls::call_signal_handler ()
{ {
/* Software-generated signal. We're fetching the current /* Software-generated signal. We're fetching the current
context, unwind to the caller and in case we're called 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; context.uc_mcontext.ctxflags = CONTEXT_FULL;
RtlCaptureContext ((PCONTEXT) &context.uc_mcontext); RtlCaptureContext ((PCONTEXT) &context.uc_mcontext);
__unwind_single_frame ((PCONTEXT) &context.uc_mcontext); __unwind_single_frame ((PCONTEXT) &context.uc_mcontext);
@ -1700,7 +1597,7 @@ _cygtls::call_signal_handler ()
#ifdef __x86_64__ #ifdef __x86_64__
context.uc_mcontext.rip = retaddr (); context.uc_mcontext.rip = retaddr ();
#else #else
context.uc_mcontext.eip = retaddr (); #error unimplemented for this target
#endif #endif
} }
} }
@ -1808,39 +1705,7 @@ _cygtls::call_signal_handler ()
[WRAPPER] "o" (altstack_wrapper) [WRAPPER] "o" (altstack_wrapper)
: "memory"); : "memory");
#else #else
/* Clobbered regs: ecx, edx, ebp, esp */ #error unimplemented for this target
__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");
#endif #endif
} }
else else
@ -1885,11 +1750,7 @@ _cygtls::signal_debugger (siginfo_t& si)
if (GetThreadContext (th, &c)) if (GetThreadContext (th, &c))
{ {
if (incyg) if (incyg)
#ifdef __x86_64__ c._CX_instPtr = retaddr ();
c.Rip = retaddr ();
#else
c.Eip = retaddr ();
#endif
memcpy (&context.uc_mcontext, &c, sizeof (CONTEXT)); memcpy (&context.uc_mcontext, &c, sizeof (CONTEXT));
/* Enough space for 32/64 bit addresses */ /* Enough space for 32/64 bit addresses */
char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING
@ -1908,20 +1769,12 @@ setcontext (const ucontext_t *ucp)
PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext; PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext;
set_signal_mask (_my_tls.sigmask, ucp->uc_sigmask); set_signal_mask (_my_tls.sigmask, ucp->uc_sigmask);
_my_tls.incyg = true; _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); RtlRestoreContext (ctx, NULL);
#else
NtContinue (ctx, FALSE);
#endif
/* If we got here, something was wrong. */ /* If we got here, something was wrong. */
set_errno (EINVAL); set_errno (EINVAL);
return -1; return -1;
} }
#ifdef __x86_64__
extern "C" int extern "C" int
getcontext (ucontext_t *ucp) getcontext (ucontext_t *ucp)
{ {
@ -1929,14 +1782,13 @@ getcontext (ucontext_t *ucp)
ctx->ContextFlags = CONTEXT_FULL; ctx->ContextFlags = CONTEXT_FULL;
RtlCaptureContext (ctx); RtlCaptureContext (ctx);
__unwind_single_frame (ctx); __unwind_single_frame (ctx);
/* Successful getcontext is supposed to return 0. If we don't set rax to 0 /* Successful getcontext is supposed to return 0. If we don't set the
here, there's a chance that code like this: return register to 0 here, there's a chance that code like this:
if (getcontext (&ctx) != 0) if (getcontext (&ctx) != 0)
assumes that getcontext failed after calling setcontext (&ctx). assumes that getcontext failed after calling setcontext (&ctx). */
Same goes for eax on 32 bit, see assembler implementation below. */ ucp->uc_mcontext._MC_retReg = 0;
ucp->uc_mcontext.rax = 0;
ucp->uc_sigmask = ucp->uc_mcontext.oldmask = _my_tls.sigmask; ucp->uc_sigmask = ucp->uc_mcontext.oldmask = _my_tls.sigmask;
/* Do not touch any other member of ucontext_t. */ /* Do not touch any other member of ucontext_t. */
return 0; return 0;
@ -1950,14 +1802,16 @@ swapcontext (ucontext_t *oucp, const ucontext_t *ucp)
RtlCaptureContext (ctx); RtlCaptureContext (ctx);
__unwind_single_frame (ctx); __unwind_single_frame (ctx);
/* See comment in getcontext. */ /* 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; oucp->uc_sigmask = oucp->uc_mcontext.oldmask = _my_tls.sigmask;
return setcontext (ucp); return setcontext (ucp);
} }
/* Trampoline function to set the context to uc_link. The pointer to the /* 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 address of uc_link is stored in a callee-saved register, referenced by
is NULL, call exit. */ _MC_uclinkReg from the C code. If uc_link is NULL, call exit. */
#ifdef __x86_64__
/* _MC_uclinkReg == %rbx */
__asm__ (" \n\ __asm__ (" \n\
.global __cont_link_context \n\ .global __cont_link_context \n\
.seh_proc __cont_link_context \n\ .seh_proc __cont_link_context \n\
@ -1979,77 +1833,7 @@ __cont_link_context: \n\
"); ");
#else #else
#error unimplemented for this target
/* 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\
");
#endif #endif
/* makecontext is modelled after GLibc's makecontext. The stack from uc_stack /* 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; sp[0] = (uintptr_t) __cont_link_context;
/* Fetch arguments and store them on the stack. /* Fetch arguments and store them on the stack.
x86_64 only: x86_64:
- Store first four args in the AMD64 ABI arg registers. - Store first four args in the AMD64 ABI arg registers.
@ -2114,23 +1898,18 @@ makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
break; break;
} }
#else #else
sp[i + 1] = va_arg (ap, uintptr_t); #error unimplemented for this target
#endif #endif
va_end (ap); va_end (ap);
/* Store pointer to uc_link at the top of the stack. */ /* Store pointer to uc_link at the top of the stack. */
sp[argc + 1] = (uintptr_t) ucp->uc_link; sp[argc + 1] = (uintptr_t) ucp->uc_link;
/* Last but not least set the register in the context at ucp so that a /* Last but not least set the register in the context at ucp so that a
subsequent setcontext or swapcontext picks up the right values: subsequent setcontext or swapcontext picks up the right values:
- Set rip/eip to the target function. - Set instruction pointer to the target function.
- Set rsp/esp to the just computed stack pointer value. - Set stack pointer to the just computed stack pointer value.
- Set rbx/ebx to the address of the pointer to uc_link. */ - Set Cygwin-specific uclink register to the address of the pointer
#ifdef __x86_64__ to uc_link. */
ucp->uc_mcontext.rip = (uint64_t) func; ucp->uc_mcontext._MC_instPtr = (uint64_t) func;
ucp->uc_mcontext.rsp = (uint64_t) sp; ucp->uc_mcontext._MC_stackPtr = (uint64_t) sp;
ucp->uc_mcontext.rbx = (uint64_t) (sp + argc + 1); ucp->uc_mcontext._MC_uclinkReg = (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
} }