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:
parent
830a9b707c
commit
93bea9f538
|
@ -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:
|
||||
|
|
|
@ -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 = ⪙
|
||||
};
|
||||
~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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue