summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <vinschen@redhat.com>2008-02-13 09:42:24 +0000
committerCorinna Vinschen <vinschen@redhat.com>2008-02-13 09:42:24 +0000
commitdc54d58651f6c589e0ab8b38f2380352ee3ab380 (patch)
treeecb6b5d5eab91f6926d12342f7109e9f36d8b81c
parent927cf6453fec6225ff59be4ce5efb4f03445fa44 (diff)
downloadgdb-dc54d58651f6c589e0ab8b38f2380352ee3ab380.tar.gz
* cygtls.cc (_cygtls::init_exception_handler): Revert patch
from 2005-12-02. * exceptions.cc (stack_info::walk): Add workaround for NT 5.2 64 bit OSes. * wincap.h (wincaps::has_restricted_stack_args): New element. * wincap.cc: Implement above element throughout. (wincapc::init): Reset has_restricted_stack_args if not running under WOW64.
-rw-r--r--winsup/cygwin/ChangeLog11
-rw-r--r--winsup/cygwin/cygtls.cc288
-rw-r--r--winsup/cygwin/exceptions.cc17
-rw-r--r--winsup/cygwin/wincap.cc18
-rw-r--r--winsup/cygwin/wincap.h2
5 files changed, 332 insertions, 4 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog
index b7572d2ed0b..66da517b136 100644
--- a/winsup/cygwin/ChangeLog
+++ b/winsup/cygwin/ChangeLog
@@ -1,3 +1,14 @@
+2008-02-13 Corinna Vinschen <corinna@vinschen.de>
+
+ * cygtls.cc (_cygtls::init_exception_handler): Revert patch
+ from 2005-12-02.
+ * exceptions.cc (stack_info::walk): Add workaround for NT 5.2
+ 64 bit OSes.
+ * wincap.h (wincaps::has_restricted_stack_args): New element.
+ * wincap.cc: Implement above element throughout.
+ (wincapc::init): Reset has_restricted_stack_args if not running
+ under WOW64.
+
2008-01-24 Corinna Vinschen <corinna@vinschen.de>
* path.cc (fs_info::update): Fix Samba test to support recent as well
diff --git a/winsup/cygwin/cygtls.cc b/winsup/cygwin/cygtls.cc
new file mode 100644
index 00000000000..0b37ed6b379
--- /dev/null
+++ b/winsup/cygwin/cygtls.cc
@@ -0,0 +1,288 @@
+/* cygtls.cc
+
+ Copyright 2003, 2004, 2005, 2006 Red Hat, Inc.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <sys/time.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "thread.h"
+#include "cygtls.h"
+#include "assert.h"
+#include <syslog.h>
+#include <signal.h>
+#include <malloc.h>
+#include "exceptions.h"
+#include "sync.h"
+#include "cygerrno.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "sigproc.h"
+
+class sentry
+{
+ static muto lock;
+ int destroy;
+public:
+ void init ();
+ bool acquired () {return lock.acquired ();}
+ sentry () {destroy = 0;}
+ sentry (DWORD wait) {destroy = lock.acquire (wait);}
+ ~sentry () {if (destroy) lock.release ();}
+ friend void _cygtls::init ();
+};
+
+muto NO_COPY sentry::lock;
+
+static size_t NO_COPY nthreads;
+
+#define THREADLIST_CHUNK 256
+
+void
+_cygtls::init ()
+{
+ if (cygheap->threadlist)
+ memset (cygheap->threadlist, 0, cygheap->sthreads * sizeof (cygheap->threadlist[0]));
+ else
+ {
+ cygheap->sthreads = THREADLIST_CHUNK;
+ cygheap->threadlist = (_cygtls **) ccalloc (HEAP_TLS, cygheap->sthreads,
+ sizeof (cygheap->threadlist[0]));
+ }
+ sentry::lock.init ("sentry_lock");
+}
+
+/* Two calls to get the stack right... */
+void
+_cygtls::call (DWORD (*func) (void *, void *), void *arg)
+{
+ char buf[CYGTLS_PADSIZE];
+ _my_tls.call2 (func, arg, buf);
+}
+
+void
+_cygtls::call2 (DWORD (*func) (void *, void *), void *arg, void *buf)
+{
+ init_thread (buf, func);
+ DWORD res = func (arg, buf);
+ remove (INFINITE);
+ /* Don't call ExitThread on the main thread since we may have been
+ dynamically loaded. */
+ if ((void *) func != (void *) dll_crt0_1
+ && (void *) func != (void *) dll_dllcrt0_1)
+ ExitThread (res);
+}
+
+void
+_cygtls::init_thread (void *x, DWORD (*func) (void *, void *))
+{
+ if (x)
+ {
+ memset (this, 0, sizeof (*this));
+ stackptr = stack;
+ if (_GLOBAL_REENT)
+ {
+ local_clib._stdin = _GLOBAL_REENT->_stdin;
+ local_clib._stdout = _GLOBAL_REENT->_stdout;
+ local_clib._stderr = _GLOBAL_REENT->_stderr;
+ local_clib.__sdidinit = _GLOBAL_REENT->__sdidinit ? -1 : 0;
+ local_clib.__cleanup = _GLOBAL_REENT->__cleanup;
+ local_clib.__sglue._niobs = 3;
+ local_clib.__sglue._iobs = &_GLOBAL_REENT->__sf[0];
+ }
+ local_clib._current_locale = "C";
+ locals.process_logmask = LOG_UPTO (LOG_DEBUG);
+ /* Initialize this thread's ability to respond to things like
+ SIGSEGV or SIGFPE. */
+ init_exception_handler (handle_exceptions);
+ }
+
+ thread_id = GetCurrentThreadId ();
+ initialized = CYGTLS_INITIALIZED;
+ locals.exitsock = INVALID_SOCKET;
+ errno_addr = &(local_clib._errno);
+
+ if ((void *) func == (void *) cygthread::stub
+ || (void *) func == (void *) cygthread::simplestub)
+ return;
+
+ if (wincap.has_security ())
+ cygheap->user.reimpersonate ();
+
+ sentry here (INFINITE);
+ if (nthreads >= cygheap->sthreads)
+ {
+ cygheap->threadlist = (_cygtls **)
+ crealloc (cygheap->threadlist, (cygheap->sthreads += THREADLIST_CHUNK)
+ * sizeof (cygheap->threadlist[0]));
+ memset (cygheap->threadlist + nthreads, 0, THREADLIST_CHUNK * sizeof (cygheap->threadlist[0]));
+ }
+
+ cygheap->threadlist[nthreads++] = this;
+}
+
+void
+_cygtls::fixup_after_fork ()
+{
+ if (sig)
+ {
+ pop ();
+ sig = 0;
+ }
+ stacklock = spinning = 0;
+ locals.exitsock = INVALID_SOCKET;
+ wq.thread_ev = NULL;
+}
+
+#define free_local(x) \
+ if (locals.x) \
+ { \
+ free (locals.x); \
+ locals.x = NULL; \
+ }
+
+void
+_cygtls::remove (DWORD wait)
+{
+ initialized = 0;
+ if (!locals.exitsock || exit_state >= ES_FINAL)
+ return;
+
+ debug_printf ("wait %p", wait);
+ if (wait)
+ {
+ /* FIXME: Need some sort of atthreadexit function to allow things like
+ select to control this themselves. */
+ if (locals.exitsock != INVALID_SOCKET)
+ {
+ closesocket (locals.exitsock);
+ locals.exitsock = (SOCKET) NULL;
+ }
+ free_local (process_ident);
+ free_local (ntoa_buf);
+ free_local (protoent_buf);
+ free_local (servent_buf);
+ free_local (hostent_buf);
+ }
+
+ do
+ {
+ sentry here (wait);
+ if (here.acquired ())
+ {
+ for (size_t i = 0; i < nthreads; i++)
+ if (this == cygheap->threadlist[i])
+ {
+ if (i < --nthreads)
+ cygheap->threadlist[i] = cygheap->threadlist[nthreads];
+ debug_printf ("removed %p element %d", this, i);
+ break;
+ }
+ }
+ } while (0);
+ remove_wq (wait);
+}
+
+void
+_cygtls::push (__stack_t addr)
+{
+ *stackptr++ = (__stack_t) addr;
+}
+
+#define BAD_IX ((size_t) -1)
+static size_t NO_COPY threadlist_ix = BAD_IX;
+
+_cygtls *
+_cygtls::find_tls (int sig)
+{
+ debug_printf ("sig %d\n", sig);
+ sentry here (INFINITE);
+ __asm__ volatile (".equ _threadlist_exception_return,.");
+ _cygtls *res = NULL;
+ for (threadlist_ix = 0; threadlist_ix < nthreads; threadlist_ix++)
+ if (sigismember (&(cygheap->threadlist[threadlist_ix]->sigwait_mask), sig))
+ {
+ res = cygheap->threadlist[threadlist_ix];
+ break;
+ }
+ threadlist_ix = BAD_IX;
+ return res;
+}
+
+void
+_cygtls::set_siginfo (sigpacket *pack)
+{
+ infodata = pack->si;
+}
+
+extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
+int
+_cygtls::handle_threadlist_exception (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *c, void *)
+{
+ if (e->ExceptionCode != STATUS_ACCESS_VIOLATION)
+ {
+ system_printf ("unhandled exception %p at %p", e->ExceptionCode, c->Eip);
+ return 1;
+ }
+
+ sentry here;
+ if (threadlist_ix == BAD_IX)
+ {
+ api_fatal ("called with threadlist_ix %d", BAD_IX);
+ return 1;
+ }
+
+ if (!here.acquired ())
+ {
+ system_printf ("couldn't aquire muto");
+ return 1;
+ }
+
+ extern void *threadlist_exception_return;
+ cygheap->threadlist[threadlist_ix]->remove (INFINITE);
+ threadlist_ix = 0;
+ RtlUnwind (frame, threadlist_exception_return, e, 0);
+ return 0;
+}
+
+/* Set up the exception handler for the current thread. The x86 uses segment
+ register fs, offset 0 to point to the current exception handler. */
+
+extern exception_list *_except_list asm ("%fs:0");
+
+void
+_cygtls::init_exception_handler (exception_handler *eh)
+{
+ el.handler = eh;
+ /* Apparently Windows stores some information about an exception and tries
+ to figure out if the SEH which returned 0 last time actually solved the
+ problem, or if the problem still persists (e.g. same exception at same
+ address). In this case Windows seems to decide that it can't trust
+ that SEH and calls the next handler in the chain instead.
+
+ At one point this was a loop (el.prev = &el;). This outsmarted the
+ above behaviour. Unfortunately this trick doesn't work anymore with
+ Windows 2008, which irremediably gets into an endless loop, taking 100%
+ CPU. That's why we reverted to a normal SEH chain.
+
+ On the bright side, Windows' behaviour is covered by POSIX:
+
+ "If and when the function returns, if the value of sig was SIGFPE,
+ SIGILL, or SIGSEGV or any other implementation-defined value
+ corresponding to a computational exception, the behavior is undefined." */
+ el.prev = _except_list;
+ _except_list = &el;
+}
+
+void
+_cygtls::init_threadlist_exceptions ()
+{
+ init_exception_handler (handle_threadlist_exception);
+}
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 2280f82b1a1..d4c14e29563 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -254,9 +254,20 @@ stack_info::walk ()
sf.AddrReturn.Offset = (DWORD) *++ebp;
if (needargs)
- /* The arguments follow the return address */
- for (unsigned i = 0; i < NPARAMS; i++)
- sf.Params[i] = (DWORD) *++ebp;
+ {
+ unsigned nparams = NPARAMS;
+
+ /* The arguments follow the return address */
+ sf.Params[0] = (DWORD) *++ebp;
+ /* Hack for XP/2K3 WOW64. If the first stack param points to the
+ application entry point, we can only fetch one additional
+ parameter. Accessing anything beyond this address results in
+ a SEGV. This is fixed in Vista/2K8 WOW64. */
+ if (wincap.has_restricted_stack_args () && sf.Params[0] == 0x401000)
+ nparams = 2;
+ for (unsigned i = 1; i < nparams; i++)
+ sf.Params[i] = (DWORD) *++ebp;
+ }
return 1;
}
diff --git a/winsup/cygwin/wincap.cc b/winsup/cygwin/wincap.cc
index a8b94fc51b5..3a968803bc4 100644
--- a/winsup/cygwin/wincap.cc
+++ b/winsup/cygwin/wincap.cc
@@ -69,6 +69,7 @@ static NO_COPY wincaps wincap_unknown = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_95 = {
@@ -129,6 +130,7 @@ static NO_COPY wincaps wincap_95 = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_95osr2 = {
@@ -189,6 +191,7 @@ static NO_COPY wincaps wincap_95osr2 = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_98 = {
@@ -249,6 +252,7 @@ static NO_COPY wincaps wincap_98 = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_98se = {
@@ -309,6 +313,7 @@ static NO_COPY wincaps wincap_98se = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_me = {
@@ -369,6 +374,7 @@ static NO_COPY wincaps wincap_me = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_nt3 = {
@@ -429,6 +435,7 @@ static NO_COPY wincaps wincap_nt3 = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_nt4 = {
@@ -489,6 +496,7 @@ static NO_COPY wincaps wincap_nt4 = {
has_exclusiveaddruse:false,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_nt4sp4 = {
@@ -549,6 +557,7 @@ static NO_COPY wincaps wincap_nt4sp4 = {
has_exclusiveaddruse:true,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_2000 = {
@@ -609,6 +618,7 @@ static NO_COPY wincaps wincap_2000 = {
has_exclusiveaddruse:true,
has_buggy_restart_scan:true,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_xp = {
@@ -669,6 +679,7 @@ static NO_COPY wincaps wincap_xp = {
has_exclusiveaddruse:true,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:false,
};
static NO_COPY wincaps wincap_2003 = {
@@ -729,6 +740,7 @@ static NO_COPY wincaps wincap_2003 = {
has_exclusiveaddruse:true,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:false,
+ has_restricted_stack_args:true,
};
static NO_COPY wincaps wincap_vista = {
@@ -789,6 +801,7 @@ static NO_COPY wincaps wincap_vista = {
has_exclusiveaddruse:true,
has_buggy_restart_scan:false,
needs_count_in_si_lpres2:true,
+ has_restricted_stack_args:false,
};
wincapc wincap __attribute__((section (".cygwin_dll_common"), shared));
@@ -902,7 +915,10 @@ wincapc::init ()
if (IsWow64Process (GetCurrentProcess (), &is_wow64_proc))
wow64 = is_wow64_proc;
else
- ((wincaps *)this->caps)->needs_count_in_si_lpres2 = false;
+ {
+ ((wincaps *)this->caps)->needs_count_in_si_lpres2 = false;
+ ((wincaps *)this->caps)->has_restricted_stack_args = false;
+ }
__small_sprintf (osnam, "%s-%d.%d", os, version.dwMajorVersion,
version.dwMinorVersion);
diff --git a/winsup/cygwin/wincap.h b/winsup/cygwin/wincap.h
index f9b86a70d55..1a9227c725f 100644
--- a/winsup/cygwin/wincap.h
+++ b/winsup/cygwin/wincap.h
@@ -70,6 +70,7 @@ struct wincaps
unsigned has_exclusiveaddruse : 1;
unsigned has_buggy_restart_scan : 1;
unsigned needs_count_in_si_lpres2 : 1;
+ unsigned has_restricted_stack_args : 1;
};
class wincapc
@@ -146,6 +147,7 @@ public:
bool IMPLEMENT (has_exclusiveaddruse)
bool IMPLEMENT (has_buggy_restart_scan)
bool IMPLEMENT (needs_count_in_si_lpres2)
+ bool IMPLEMENT (has_restricted_stack_args)
#undef IMPLEMENT
};