summaryrefslogtreecommitdiffstats
path: root/win
diff options
context:
space:
mode:
authorKevin B Kenny <kennykb@acm.org>2007-11-10 17:24:01 (GMT)
committerKevin B Kenny <kennykb@acm.org>2007-11-10 17:24:01 (GMT)
commit590f17c72d0353c75007c62962737dc5d553884a (patch)
treed0c17e227db581a3992b3ca262ce23d611203282 /win
parent0e7fa3edd23cef23c43109db2656ccc52978c99b (diff)
downloadtcl-590f17c72d0353c75007c62962737dc5d553884a.zip
tcl-590f17c72d0353c75007c62962737dc5d553884a.tar.gz
tcl-590f17c72d0353c75007c62962737dc5d553884a.tar.bz2
2007-11-10 Kevin Kenny <kennykb@acm.org>
* win/tclWin32Dll.c: Rewrote the Windows stack checking algorithm to use information from VirtualQuery to determine the bound of the stack. This change fixes a bug where the guard page of the stack was never restored after an overflow. It also eliminates a nasty piece of assembly code for structured exception handling on mingw. It introduces an assumption that the stack is a single memory arena returned from VirtualAlloc, but the code in MSVCRT makes the same assumption, so it should be fairly safe.
Diffstat (limited to 'win')
-rw-r--r--win/tclWin32Dll.c198
1 files changed, 40 insertions, 158 deletions
diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c
index 8ffb697..49c6f14 100644
--- a/win/tclWin32Dll.c
+++ b/win/tclWin32Dll.c
@@ -10,7 +10,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclWin32Dll.c,v 1.49 2007/11/10 16:08:10 msofer Exp $
+ * RCS: @(#) $Id: tclWin32Dll.c,v 1.50 2007/11/10 17:24:02 kennykb Exp $
*/
#include "tclWinInt.h"
@@ -21,9 +21,13 @@
*/
typedef struct ThreadSpecificData {
int *stackBound; /* The current stack boundary */
+ SYSTEM_INFO si; /* The system information, used to
+ * determine the page size */
+ MEMORY_BASIC_INFORMATION mbi;
+ /* The information about the memory
+ * area in which the stack resides */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
-static int * CheckStackSpace(void)
#endif /* TCL_NO_STACK_CHECK */
/*
@@ -522,138 +526,6 @@ TclWinNoBackslash(
return path;
}
-#ifndef TCL_NO_STACK_CHECK
-/*
- *----------------------------------------------------------------------
- *
- * CheckStackSpace --
- *
- * Detect if we are about to blow the stack. Called before an evaluation
- * can happen when nesting depth is checked.
- *
- * Results:
- * A pointer to the deepest safe stack location that was determined, or
- * NULL if we are about to blow the stack.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-static int *
-CheckStackSpace(void)
-{
-
-#ifdef HAVE_NO_SEH
- EXCEPTION_REGISTRATION registration;
-#endif
- int retval = 0;
-
- /*
- * We can recurse only if there is at least TCL_WIN_STACK_THRESHOLD bytes
- * of stack space left. alloca() is cheap on windows; basically it just
- * subtracts from the stack pointer causing the OS to throw an exception
- * if the stack pointer is set below the bottom of the stack.
- */
-
-#ifdef HAVE_NO_SEH
- __asm__ __volatile__ (
-
- /*
- * Construct an EXCEPTION_REGISTRATION to protect the call to __alloca
- */
-
- "leal %[registration], %%edx" "\n\t"
- "movl %%fs:0, %%eax" "\n\t"
- "movl %%eax, 0x0(%%edx)" "\n\t" /* link */
- "leal 1f, %%eax" "\n\t"
- "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */
- "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */
- "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */
- "movl %[error], 0x10(%%edx)" "\n\t" /* status */
-
- /*
- * Link the EXCEPTION_REGISTRATION on the chain
- */
-
- "movl %%edx, %%fs:0" "\n\t"
-
- /*
- * Attempt a call to __alloca, to determine whether there's sufficient
- * memory to be had.
- */
-
- "movl %[size], %%eax" "\n\t"
- "pushl %%eax" "\n\t"
- "call __alloca" "\n\t"
-
- /*
- * Come here on a normal exit. Recover the EXCEPTION_REGISTRATION and
- * store a TCL_OK status
- */
-
- "movl %%fs:0, %%edx" "\n\t"
- "movl %[ok], %%eax" "\n\t"
- "movl %%eax, 0x10(%%edx)" "\n\t"
- "jmp 2f" "\n"
-
- /*
- * Come here on an exception. Get the EXCEPTION_REGISTRATION that we
- * previously put on the chain.
- */
-
- "1:" "\t"
- "movl %%fs:0, %%edx" "\n\t"
- "movl 0x8(%%edx), %%edx" "\n\t"
-
- /*
- * Come here however we exited. Restore context from the
- * EXCEPTION_REGISTRATION in case the stack is unbalanced.
- */
-
- "2:" "\t"
- "movl 0xc(%%edx), %%esp" "\n\t"
- "movl 0x8(%%edx), %%ebp" "\n\t"
- "movl 0x0(%%edx), %%eax" "\n\t"
- "movl %%eax, %%fs:0" "\n\t"
-
- :
- /* No outputs */
- :
- [registration] "m" (registration),
- [ok] "i" (TCL_OK),
- [error] "i" (TCL_ERROR),
- [size] "i" (TCL_WIN_STACK_THRESHOLD)
- :
- "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"
- );
- retval = (registration.status == TCL_OK);
-
-#else /* !HAVE_NO_SEH */
- __try {
-#ifdef HAVE_ALLOCA_GCC_INLINE
- __asm__ __volatile__ (
- "movl %0, %%eax" "\n\t"
- "call __alloca" "\n\t"
- :
- : "i"(TCL_WIN_STACK_THRESHOLD)
- : "%eax");
-#else
- alloca(TCL_WIN_STACK_THRESHOLD);
-#endif /* HAVE_ALLOCA_GCC_INLINE */
- retval = 1;
- } __except (EXCEPTION_EXECUTE_HANDLER) {}
-#endif /* HAVE_NO_SEH */
-
- if (retval) {
- return (int *) ((char *)&retval - TCL_WIN_STACK_THRESHOLD);
- } else {
- return NULL;
- }
-}
-#endif
-
/*
*----------------------------------------------------------------------
*
@@ -679,41 +551,51 @@ int
TclpGetCStackParams(
int ***stackBoundPtr)
{
- int localVar, *stackBound;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
/* Most variables are actually in a
* thread-specific data block to minimise the
* impact on the stack. */
- if (!tsdPtr->stackBound || (&localVar < tsdPtr->stackBound)) {
- /*
- * First time through in this thread or apparent stack failure. If we
- * didn't already blow up, we are within the safety area. Recheck with
- * the OS to get a new estimate.
+ if (!tsdPtr->stackBound
+ || ((DWORD_PTR)&tsdPtr < (DWORD_PTR)tsdPtr->stackBound)) {
+
+ /*
+ * Either we haven't determined the stack bound in this thread,
+ * or else we've overflowed the bound that we previously
+ * determined. We need to find a new stack bound from
+ * Windows.
*/
-
- stackBound = CheckStackSpace();
- if (!stackBound) {
- /*
- * We are really blowing the stack! Do not update the estimate we
- * already had, just return.
- */
-
+
+ GetSystemInfo(&tsdPtr->si);
+ if (VirtualQuery((LPCVOID) &tsdPtr,
+ &(tsdPtr->mbi), sizeof(tsdPtr->mbi)) == 0) {
+
+ /* For some reason, the system didn't let us query the
+ * stack size. Nevertheless, we got here and haven't
+ * blown up yet. Don't update the calculated stack bound.
+ * If there is no calculated stack bound yet, set it to
+ * the base of the current page of stack. */
+
if (!tsdPtr->stackBound) {
- /*
- * ??? First time around: this thread was born with a stack
- * smaller than TCL_WIN_STACK_THRESHOLD. What do we do now?
- * Initialise to something that will always fail? Choose for
- * instance 1K ints above the current depth.
- */
-
- tsdPtr->stackBound = (&localVar + 1024);
+ tsdPtr->stackBound =
+ (int*) ((DWORD_PTR)(&tsdPtr)
+ & ~ (DWORD_PTR)(tsdPtr->si.dwPageSize - 1));
}
+
} else {
- tsdPtr->stackBound = stackBound;
+
+ /* The allocation base of the stack segment has to be advanced
+ * by one page (to allow for the guard page maintained in the
+ * C runtime) and then by TCL_WIN_STACK_THRESHOLD (to allow
+ * for the amount of stack that Tcl needs).
+ */
+
+ tsdPtr->stackBound =
+ (int*) ((DWORD_PTR)(tsdPtr->mbi.AllocationBase)
+ + (DWORD_PTR)(tsdPtr->si.dwPageSize)
+ + TCL_WIN_STACK_THRESHOLD);
}
}
-
*stackBoundPtr = &(tsdPtr->stackBound);
return 1;
}