1 /* miscfuncs.cc: misc funcs that don't belong anywhere else
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #include "miscfuncs.h"
18 #include <sys/param.h>
30 #include "exception.h"
34 const char case_folded_lower[] NO_COPY = {
35 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
36 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
37 32, '!', '"', '#', '$', '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
38 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
39 '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
40 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', 92, ']', '^', '_',
41 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
42 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 127,
43 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
44 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
45 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
46 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
47 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
48 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
49 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
50 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
53 const char case_folded_upper[] NO_COPY = {
54 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
55 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
56 32, '!', '"', '#', '$', '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
57 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
58 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
59 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', 92, ']', '^', '_',
60 '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
61 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '{', '|', '}', '~', 127,
62 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
63 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
64 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
65 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
66 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
67 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
68 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
69 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
72 const char isalpha_array[] NO_COPY = {
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
78 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0, 0, 0, 0, 0,
79 0,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
80 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
91 extern "C" int __stdcall
92 cygwin_wcscasecmp (const wchar_t *ws, const wchar_t *wt)
94 UNICODE_STRING us, ut;
96 RtlInitUnicodeString (&us, ws);
97 RtlInitUnicodeString (&ut, wt);
98 return RtlCompareUnicodeString (&us, &ut, TRUE);
101 extern "C" int __stdcall
102 cygwin_wcsncasecmp (const wchar_t *ws, const wchar_t *wt, size_t n)
104 UNICODE_STRING us, ut;
105 size_t ls = 0, lt = 0;
107 while (ws[ls] && ls < n)
109 RtlInitCountedUnicodeString (&us, ws, ls * sizeof (WCHAR));
110 while (wt[lt] && lt < n)
112 RtlInitCountedUnicodeString (&ut, wt, lt * sizeof (WCHAR));
113 return RtlCompareUnicodeString (&us, &ut, TRUE);
116 extern "C" int __stdcall
117 cygwin_strcasecmp (const char *cs, const char *ct)
119 UNICODE_STRING us, ut;
122 len = (strlen (cs) + 1) * sizeof (WCHAR);
123 RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (len), len);
124 us.Length = sys_mbstowcs (us.Buffer, us.MaximumLength, cs) * sizeof (WCHAR);
125 len = (strlen (ct) + 1) * sizeof (WCHAR);
126 RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (len), len);
127 ut.Length = sys_mbstowcs (ut.Buffer, ut.MaximumLength, ct) * sizeof (WCHAR);
128 return RtlCompareUnicodeString (&us, &ut, TRUE);
131 extern "C" int __stdcall
132 cygwin_strncasecmp (const char *cs, const char *ct, size_t n)
134 UNICODE_STRING us, ut;
136 size_t ls = 0, lt = 0;
138 while (cs[ls] && ls < n)
140 len = (ls + 1) * sizeof (WCHAR);
141 RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (len), len);
142 us.Length = sys_mbstowcs (us.Buffer, ls + 1, cs, ls) * sizeof (WCHAR);
143 while (ct[lt] && lt < n)
145 len = (lt + 1) * sizeof (WCHAR);
146 RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (len), len);
147 ut.Length = sys_mbstowcs (ut.Buffer, lt + 1, ct, lt) * sizeof (WCHAR);
148 return RtlCompareUnicodeString (&us, &ut, TRUE);
151 extern "C" char * __stdcall
152 cygwin_strlwr (char *string)
155 size_t len = (strlen (string) + 1) * sizeof (WCHAR);
157 us.MaximumLength = len; us.Buffer = (PWCHAR) alloca (len);
158 us.Length = sys_mbstowcs (us.Buffer, len, string) * sizeof (WCHAR)
160 RtlDowncaseUnicodeString (&us, &us, FALSE);
161 sys_wcstombs (string, len / sizeof (WCHAR), us.Buffer);
165 extern "C" char * __stdcall
166 cygwin_strupr (char *string)
169 size_t len = (strlen (string) + 1) * sizeof (WCHAR);
171 us.MaximumLength = len; us.Buffer = (PWCHAR) alloca (len);
172 us.Length = sys_mbstowcs (us.Buffer, len, string) * sizeof (WCHAR)
174 RtlUpcaseUnicodeString (&us, &us, FALSE);
175 sys_wcstombs (string, len / sizeof (WCHAR), us.Buffer);
180 check_invalid_virtual_addr (const void *s, unsigned sz)
182 MEMORY_BASIC_INFORMATION mbuf;
185 for (end = (char *) s + sz; s < end;
186 s = (char *) mbuf.BaseAddress + mbuf.RegionSize)
187 if (!VirtualQuery (s, &mbuf, sizeof mbuf))
192 static char __attribute__ ((noinline))
193 dummytest (volatile char *p)
199 check_iovec (const struct iovec *iov, int iovcnt, bool forwrite)
201 if (iovcnt <= 0 || iovcnt > IOV_MAX)
208 if (efault.faulted (EFAULT))
215 if (iov->iov_len > SSIZE_MAX || (tot += iov->iov_len) > SSIZE_MAX)
221 volatile char *p = ((char *) iov->iov_base) + iov->iov_len - 1;
233 assert (tot <= SSIZE_MAX);
235 return (ssize_t) tot;
238 /* Try hard to schedule another thread. */
242 int prio = GetThreadPriority (GetCurrentThread ());
243 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
244 for (int i = 0; i < 2; i++)
246 /* MSDN implies that SleepEx(0,...) will force scheduling of other
247 threads. Unlike SwitchToThread() the documentation does not mention
248 other cpus so, presumably (hah!), this + using a lower priority will
249 stall this thread temporarily and cause another to run. */
252 SetThreadPriority (GetCurrentThread (), prio);
255 /* Get a default value for the nice factor. When changing these values,
256 have a look into the below function nice_to_winprio. The values must
257 match the layout of the static "priority" array. */
259 winprio_to_nice (DWORD prio)
263 case REALTIME_PRIORITY_CLASS:
265 case HIGH_PRIORITY_CLASS:
267 case ABOVE_NORMAL_PRIORITY_CLASS:
269 case NORMAL_PRIORITY_CLASS:
271 case BELOW_NORMAL_PRIORITY_CLASS:
273 case IDLE_PRIORITY_CLASS:
279 /* Get a Win32 priority matching the incoming nice factor. The incoming
280 nice is limited to the interval [-NZERO,NZERO-1]. */
282 nice_to_winprio (int &nice)
284 static const DWORD priority[] NO_COPY =
286 REALTIME_PRIORITY_CLASS, /* 0 */
287 HIGH_PRIORITY_CLASS, /* 1 */
293 HIGH_PRIORITY_CLASS, /* 7 */
294 ABOVE_NORMAL_PRIORITY_CLASS, /* 8 */
295 ABOVE_NORMAL_PRIORITY_CLASS,
296 ABOVE_NORMAL_PRIORITY_CLASS,
297 ABOVE_NORMAL_PRIORITY_CLASS,
298 ABOVE_NORMAL_PRIORITY_CLASS,
299 ABOVE_NORMAL_PRIORITY_CLASS,
300 ABOVE_NORMAL_PRIORITY_CLASS,
301 ABOVE_NORMAL_PRIORITY_CLASS, /* 15 */
302 NORMAL_PRIORITY_CLASS, /* 16 */
303 NORMAL_PRIORITY_CLASS,
304 NORMAL_PRIORITY_CLASS,
305 NORMAL_PRIORITY_CLASS,
306 NORMAL_PRIORITY_CLASS,
307 NORMAL_PRIORITY_CLASS,
308 NORMAL_PRIORITY_CLASS,
309 NORMAL_PRIORITY_CLASS, /* 23 */
310 BELOW_NORMAL_PRIORITY_CLASS, /* 24 */
311 BELOW_NORMAL_PRIORITY_CLASS,
312 BELOW_NORMAL_PRIORITY_CLASS,
313 BELOW_NORMAL_PRIORITY_CLASS,
314 BELOW_NORMAL_PRIORITY_CLASS,
315 BELOW_NORMAL_PRIORITY_CLASS,
316 BELOW_NORMAL_PRIORITY_CLASS,
317 BELOW_NORMAL_PRIORITY_CLASS, /* 31 */
318 IDLE_PRIORITY_CLASS, /* 32 */
325 IDLE_PRIORITY_CLASS /* 39 */
329 else if (nice > NZERO - 1)
331 DWORD prio = priority[nice + NZERO];
335 /* Minimal overlapped pipe I/O implementation for signal and commune stuff. */
338 CreatePipeOverlapped (PHANDLE hr, PHANDLE hw, LPSECURITY_ATTRIBUTES sa)
340 int ret = fhandler_pipe::create (sa, hr, hw, 0, NULL,
341 FILE_FLAG_OVERLAPPED);
348 ReadPipeOverlapped (HANDLE h, PVOID buf, DWORD len, LPDWORD ret_len,
354 memset (&ov, 0, sizeof ov);
355 ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
356 ret = ReadFile (h, buf, len, NULL, &ov);
357 if (ret || GetLastError () == ERROR_IO_PENDING)
359 if (!ret && WaitForSingleObject (ov.hEvent, timeout) != WAIT_OBJECT_0)
361 ret = GetOverlappedResult (h, &ov, ret_len, FALSE);
363 CloseHandle (ov.hEvent);
368 WritePipeOverlapped (HANDLE h, PCVOID buf, DWORD len, LPDWORD ret_len,
374 memset (&ov, 0, sizeof ov);
375 ov.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
376 ret = WriteFile (h, buf, len, NULL, &ov);
377 if (ret || GetLastError () == ERROR_IO_PENDING)
379 if (!ret && WaitForSingleObject (ov.hEvent, timeout) != WAIT_OBJECT_0)
381 ret = GetOverlappedResult (h, &ov, ret_len, FALSE);
383 CloseHandle (ov.hEvent);
387 /* backslashify: Convert all forward slashes in src path to back slashes
388 in dst path. Add a trailing slash to dst when trailing_slash_p arg
392 backslashify (const char *src, char *dst, bool trailing_slash_p)
394 const char *start = src;
406 && !isdirsep (src[-1]))
411 /* slashify: Convert all back slashes in src path to forward slashes
412 in dst path. Add a trailing slash to dst when trailing_slash_p arg
416 slashify (const char *src, char *dst, bool trailing_slash_p)
418 const char *start = src;
430 && !isdirsep (src[-1]))
435 /* CygwinCreateThread.
437 Replacement function for CreateThread. What we do here is to remove
438 parameters we don't use and instead to add parameters we need to make
439 the function pthreads compatible. */
441 struct thread_wrapper_arg
443 LPTHREAD_START_ROUTINE func;
451 thread_wrapper (VOID *arg)
453 /* Just plain paranoia. */
455 return ERROR_INVALID_PARAMETER;
457 /* Fetch thread wrapper info and free from cygheap. */
458 thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg;
461 /* Remove _cygtls from this stack since it won't be used anymore. */
464 /* Set stack values in TEB */
465 PTEB teb = NtCurrentTeb ();
466 teb->Tib.StackBase = wrapper_arg.stackbase;
467 teb->Tib.StackLimit = wrapper_arg.commitaddr ?: wrapper_arg.stackaddr;
468 /* Set DeallocationStack value. If we have an application-provided stack,
469 we set DeallocationStack to NULL, so NtTerminateThread does not deallocate
470 any stack. If we created the stack in CygwinCreateThread, we set
471 DeallocationStack to the stackaddr of our own stack, so it's automatically
472 deallocated when the thread is terminated. */
473 char *dealloc_addr = (char *) teb->DeallocationStack;
474 teb->DeallocationStack = wrapper_arg.commitaddr ? wrapper_arg.stackaddr
476 /* Store the OS-provided DeallocationStack address in wrapper_arg.stackaddr.
477 The below assembler code will release the OS stack after switching to our
479 wrapper_arg.stackaddr = dealloc_addr;
481 /* Initialize new _cygtls. */
482 _my_tls.init_thread (wrapper_arg.stackbase - CYGTLS_PADSIZE,
483 (DWORD (*)(void*, void*)) wrapper_arg.func);
485 /* Copy exception list over to new stack. I'm not quite sure how the
486 exception list is extended by Windows itself. What's clear is that it
487 always grows downwards and that it starts right at the stackbase.
488 Therefore we first count the number of exception records and place
489 the copy at the stackbase, too, so there's still a lot of room to
490 extend the list up to where our _cygtls region starts. */
491 _exception_list *old_start = (_exception_list *) teb->Tib.ExceptionList;
493 teb->Tib.ExceptionList = NULL;
494 for (_exception_list *e_ptr = old_start;
495 e_ptr && e_ptr != (_exception_list *) -1;
500 _exception_list *new_start = (_exception_list *) wrapper_arg.stackbase
502 teb->Tib.ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *)
506 new_start->handler = old_start->handler;
507 if (old_start->prev == (_exception_list *) -1)
509 new_start->prev = (_exception_list *) -1;
512 new_start->prev = new_start + 1;
513 new_start = new_start->prev;
514 old_start = old_start->prev;
519 movl %[WRAPPER_ARG], %%ebx # Load &wrapper_arg into ebx \n\
520 movl (%%ebx), %%eax # Load thread func into eax \n\
521 movl 4(%%ebx), %%ecx # Load thread arg into ecx \n\
522 movl 8(%%ebx), %%edx # Load stackaddr into edx \n\
523 movl 12(%%ebx), %%ebx # Load stackbase into ebx \n\
524 subl %[CYGTLS], %%ebx # Subtract CYGTLS_PADSIZE \n\
525 subl $4, %%ebx # Subtract another 4 bytes \n\
526 movl %%ebx, %%esp # Set esp \n\
527 xorl %%ebp, %%ebp # Set ebp to 0 \n\
528 # Now we moved to the new stack. Save thread func address\n\
529 # and thread arg on new stack \n\
530 pushl %%ecx # Push thread arg onto stack \n\
531 pushl %%eax # Push thread func onto stack \n\
532 # Now it's safe to release the OS stack. \n\
533 pushl $0x8000 # dwFreeType: MEM_RELEASE \n\
534 pushl $0x0 # dwSize: 0 \n\
535 pushl %%edx # lpAddress: stackaddr \n\
536 call _VirtualFree@12 # Shoot \n\
537 # All set. We can pop the thread function address from \n\
538 # the stack and call it. The thread arg is still on the \n\
539 # stack in the expected spot. \n\
540 popl %%eax # Pop thread_func address \n\
541 call *%%eax # Call thread func \n"
542 : : [WRAPPER_ARG] "r" (&wrapper_arg),
543 [CYGTLS] "i" (CYGTLS_PADSIZE));
544 /* Never return from here. */
548 /* FIXME: This should be settable via setrlimit (RLIMIT_STACK). */
549 #define DEFAULT_STACKSIZE (512 * 1024)
552 CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg,
553 PVOID stackaddr, ULONG stacksize, ULONG guardsize,
554 DWORD creation_flags, LPDWORD thread_id)
556 PVOID real_stackaddr = NULL;
557 ULONG real_stacksize = 0;
558 ULONG real_guardsize = 0;
559 thread_wrapper_arg *wrapper_arg;
560 HANDLE thread = NULL;
562 wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1,
563 sizeof *wrapper_arg);
566 SetLastError (ERROR_OUTOFMEMORY);
569 wrapper_arg->func = thread_func;
570 wrapper_arg->arg = thread_arg;
573 real_stacksize = stacksize ?: DEFAULT_STACKSIZE;
574 if (real_stacksize < PTHREAD_STACK_MIN)
575 real_stacksize = PTHREAD_STACK_MIN;
578 /* If the application provided the stack, just use it. */
579 wrapper_arg->stackaddr = (char *) stackaddr;
580 wrapper_arg->stackbase = (char *) stackaddr + real_stacksize;
584 /* If not, we have to create the stack here. */
585 real_stacksize = roundup2 (real_stacksize, wincap.page_size ());
586 /* If no guardsize has been specified by the application, use the
587 system pagesize as default. */
588 real_guardsize = (guardsize != (ULONG) -1)
589 ? guardsize : wincap.page_size ();
591 real_guardsize = roundup2 (real_guardsize, wincap.page_size ());
592 /* Add the guardsize to the stacksize, but only if the stacksize and
593 the guardsize have been explicitely specified. */
594 if (stacksize || guardsize != (ULONG) -1)
595 real_stacksize += real_guardsize;
596 /* Now roundup the result to the next allocation boundary. */
597 real_stacksize = roundup2 (real_stacksize,
598 wincap.allocation_granularity ());
600 FIXME? If the TOP_DOWN method tends to collide too much with
601 other stuff, we should provide our own mechanism to find a
602 suitable place for the stack. Top down from the start of
603 the Cygwin DLL comes to mind. */
604 real_stackaddr = VirtualAlloc (NULL, real_stacksize,
605 MEM_RESERVE | MEM_TOP_DOWN,
606 PAGE_EXECUTE_READWRITE);
609 /* Set up committed region. In contrast to the OS we commit 64K and
610 set up just a single guard page at the end. */
611 char *commitaddr = (char *) real_stackaddr
613 - wincap.allocation_granularity ();
614 if (!VirtualAlloc (commitaddr, wincap.page_size (), MEM_COMMIT,
615 PAGE_EXECUTE_READWRITE | PAGE_GUARD))
617 commitaddr += wincap.page_size ();
618 if (!VirtualAlloc (commitaddr, wincap.allocation_granularity ()
619 - wincap.page_size (), MEM_COMMIT,
620 PAGE_EXECUTE_READWRITE))
623 VirtualAlloc (real_stackaddr, real_guardsize, MEM_COMMIT,
625 wrapper_arg->stackaddr = (char *) real_stackaddr;
626 wrapper_arg->stackbase = (char *) real_stackaddr + real_stacksize;
627 wrapper_arg->commitaddr = commitaddr;
629 /* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter so only the
630 minimum size for a thread stack is reserved by the OS. This doesn't
631 work on Windows 2000, but we deallocate the OS stack in thread_wrapper
632 anyway, so this should be a problem only in a tight memory condition.
633 Note that we reserve a 256K stack, not 64K, otherwise the thread creation
634 might crash the process due to a stack overflow. */
635 thread = CreateThread (&sec_none_nih, 4 * PTHREAD_STACK_MIN,
636 thread_wrapper, wrapper_arg,
637 creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION,
641 if (!thread && real_stackaddr)
643 /* Don't report the wrong error even though VirtualFree is very unlikely
645 DWORD err = GetLastError ();
646 VirtualFree (real_stackaddr, 0, MEM_RELEASE);