* child_info.h (CURR_CHILD_INFO_MAGIC): Update.
(class child_info_fork): Remove stacksize, add stackaddr and guardsize members. * dcrt0.cc (child_info_fork::alloc_stack_hard_way): Partial rewrite to regenerate the stack exactly as in the parent. (child_info_fork::alloc_stack): Set stackaddr to 0, rather than stacksize. (dll_crt0_1): Check for stackaddr before changing the stack addresses in the TEB. * fork.cc (frok::child): Check for stackaddr here. (frok::parent): Set ch.stackaddr and ch.guardsize if not called from the main thread. * init.cc (dll_entry): Replace pointer to NT_TIB with pointer to TEB. Fix incorrectly changed address test before removing _my_tls. Set StackLimit to NULL on Windows 2000. Explain why. * miscfuncs.cc (struct thread_wrapper_arg): Store stackbase rather than stacksize, store commitaddr, remove guardsize. Store all pointers as char * for easier address arithmetic. (thread_wrapper): Rewrite to remove OS stack before calling thread function. Add lots of comments to explain what we do. (CygwinCreateThread): Reserve our own stack in case we got no application stack. Add comments. * ntdll.h (struct _TEB): Extend defintion up to DeallocationStack member. * thread.cc (pthread_attr::pthread_attr): Use "(size_t) -1" rather then 0xffffffff. * wincap.h (wincaps::has_stack_size_param_is_a_reservation): New element. * wincap.cc: Implement above element throughout.
This commit is contained in:
		| @@ -1,3 +1,35 @@ | |||||||
|  | 2011-05-20  Corinna Vinschen  <corinna@vinschen.de> | ||||||
|  |  | ||||||
|  | 	* child_info.h (CURR_CHILD_INFO_MAGIC): Update. | ||||||
|  | 	(class child_info_fork): Remove stacksize, add stackaddr and guardsize | ||||||
|  | 	members. | ||||||
|  | 	* dcrt0.cc (child_info_fork::alloc_stack_hard_way): Partial rewrite | ||||||
|  | 	to regenerate the stack exactly as in the parent. | ||||||
|  | 	(child_info_fork::alloc_stack): Set stackaddr to 0, rather than | ||||||
|  | 	stacksize. | ||||||
|  | 	(dll_crt0_1): Check for stackaddr before changing the stack addresses | ||||||
|  | 	in the TEB. | ||||||
|  | 	* fork.cc (frok::child): Check for stackaddr here. | ||||||
|  | 	(frok::parent): Set ch.stackaddr and ch.guardsize if not called from | ||||||
|  | 	the main thread. | ||||||
|  | 	* init.cc (dll_entry): Replace pointer to NT_TIB with pointer to TEB. | ||||||
|  | 	Fix incorrectly changed address test before removing _my_tls. | ||||||
|  | 	Set StackLimit to NULL on Windows 2000.  Explain why. | ||||||
|  | 	* miscfuncs.cc (struct thread_wrapper_arg): Store stackbase rather | ||||||
|  | 	than stacksize, store commitaddr, remove guardsize.  Store all pointers | ||||||
|  | 	as char * for easier address arithmetic. | ||||||
|  | 	(thread_wrapper): Rewrite to remove OS stack before calling thread | ||||||
|  | 	function.  Add lots of comments to explain what we do. | ||||||
|  | 	(CygwinCreateThread): Reserve our own stack in case we got no | ||||||
|  | 	application stack.  Add comments. | ||||||
|  | 	* ntdll.h (struct _TEB): Extend defintion up to DeallocationStack | ||||||
|  | 	member. | ||||||
|  | 	* thread.cc (pthread_attr::pthread_attr): Use "(size_t) -1" | ||||||
|  | 	rather then 0xffffffff. | ||||||
|  | 	* wincap.h (wincaps::has_stack_size_param_is_a_reservation): New | ||||||
|  | 	element. | ||||||
|  | 	* wincap.cc: Implement above element throughout. | ||||||
|  |  | ||||||
| 2011-05-19  Yaakov Selkowitz  <yselkowitz@users.sourceforge.net> | 2011-05-19  Yaakov Selkowitz  <yselkowitz@users.sourceforge.net> | ||||||
|  |  | ||||||
| 	* thread.cc: Mark psiginfo and psignal as available in list of | 	* thread.cc: Mark psiginfo and psignal as available in list of | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ enum child_status | |||||||
| #define EXEC_MAGIC_SIZE sizeof(child_info) | #define EXEC_MAGIC_SIZE sizeof(child_info) | ||||||
|  |  | ||||||
| /* Change this value if you get a message indicating that it is out-of-sync. */ | /* Change this value if you get a message indicating that it is out-of-sync. */ | ||||||
| #define CURR_CHILD_INFO_MAGIC 0xe850717aU | #define CURR_CHILD_INFO_MAGIC 0xbdf5842aU | ||||||
|  |  | ||||||
| /* NOTE: Do not make gratuitous changes to the names or organization of the | /* NOTE: Do not make gratuitous changes to the names or organization of the | ||||||
|    below class.  The layout is checksummed to determine compatibility between |    below class.  The layout is checksummed to determine compatibility between | ||||||
| @@ -80,10 +80,12 @@ class child_info_fork: public child_info | |||||||
| { | { | ||||||
| public: | public: | ||||||
|   HANDLE forker_finished;// for synchronization with child |   HANDLE forker_finished;// for synchronization with child | ||||||
|   DWORD stacksize;	// size of parent stack |  | ||||||
|   jmp_buf jmp;		// where child will jump to |   jmp_buf jmp;		// where child will jump to | ||||||
|  |   void *stackaddr;	// address of parent stack | ||||||
|   void *stacktop;	// location of top of parent stack |   void *stacktop;	// location of top of parent stack | ||||||
|   void *stackbottom;	// location of bottom of parent stack |   void *stackbottom;	// location of bottom of parent stack | ||||||
|  |   size_t guardsize;     // size of POSIX guard region or (size_t) -1 if | ||||||
|  | 			// user stack | ||||||
|   char filler[4]; |   char filler[4]; | ||||||
|   child_info_fork (); |   child_info_fork (); | ||||||
|   void handle_fork () __attribute__ ((regparm (1)));; |   void handle_fork () __attribute__ ((regparm (1)));; | ||||||
|   | |||||||
| @@ -392,14 +392,11 @@ child_info NO_COPY *child_proc_info = NULL; | |||||||
| void | void | ||||||
| child_info_fork::alloc_stack_hard_way (volatile char *b) | child_info_fork::alloc_stack_hard_way (volatile char *b) | ||||||
| { | { | ||||||
|   void *new_stack_pointer; |   void *stack_ptr; | ||||||
|   MEMORY_BASIC_INFORMATION m; |   DWORD stacksize; | ||||||
|   void *newbase; |  | ||||||
|   int newlen; |  | ||||||
|   bool guard; |  | ||||||
|  |  | ||||||
|   /* First check if the requested stack area is part of the user heap |   /* First check if the requested stack area is part of the user heap | ||||||
|      or part of a mmaped region.  If so, we have been started from a |      or part of a mmapped region.  If so, we have been started from a | ||||||
|      pthread with an application-provided stack, and the stack has just |      pthread with an application-provided stack, and the stack has just | ||||||
|      to be used as is. */ |      to be used as is. */ | ||||||
|   if ((stacktop >= cygheap->user_heap.base |   if ((stacktop >= cygheap->user_heap.base | ||||||
| @@ -407,46 +404,33 @@ child_info_fork::alloc_stack_hard_way (volatile char *b) | |||||||
|       || is_mmapped_region ((caddr_t) stacktop, (caddr_t) stackbottom)) |       || is_mmapped_region ((caddr_t) stacktop, (caddr_t) stackbottom)) | ||||||
|     return; |     return; | ||||||
|  |  | ||||||
|   if (!VirtualQuery ((LPCVOID) &b, &m, sizeof m)) |   /* First, try to reserve the entire stack. */ | ||||||
|     api_fatal ("fork: couldn't get stack info, %E"); |   stacksize = (char *) stackbottom - (char *) stackaddr; | ||||||
|  |   if (!VirtualAlloc (stackaddr, stacksize, MEM_RESERVE, PAGE_NOACCESS)) | ||||||
|   LPBYTE curbot = (LPBYTE) m.BaseAddress + m.RegionSize; |  | ||||||
|  |  | ||||||
|   if (stacktop > (LPBYTE) m.AllocationBase && stacktop < curbot) |  | ||||||
|     { |  | ||||||
|       newbase = curbot; |  | ||||||
|       newlen = (LPBYTE) stackbottom - (LPBYTE) curbot; |  | ||||||
|       guard = false; |  | ||||||
|     } |  | ||||||
|   else |  | ||||||
|     { |  | ||||||
|       newbase = (LPBYTE) stacktop - (128 * 1024); |  | ||||||
|       newlen = (LPBYTE) stackbottom - (LPBYTE) newbase; |  | ||||||
|       guard = true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|   if (!VirtualAlloc (newbase, newlen, MEM_RESERVE, PAGE_NOACCESS)) |  | ||||||
|     api_fatal ("fork: can't reserve memory for stack %p - %p, %E", |     api_fatal ("fork: can't reserve memory for stack %p - %p, %E", | ||||||
| 		stacktop, stackbottom); | 	       stackaddr, stackbottom); | ||||||
|   new_stack_pointer = (void *) ((LPBYTE) stackbottom - (stacksize += 8192)); |   stacksize = (char *) stackbottom - (char *) stacktop; | ||||||
|   if (!VirtualAlloc (new_stack_pointer, stacksize, MEM_COMMIT, |   stack_ptr = VirtualAlloc (stacktop, stacksize, MEM_COMMIT, | ||||||
| 		     PAGE_EXECUTE_READWRITE)) | 			    PAGE_EXECUTE_READWRITE); | ||||||
|  |   if (!stack_ptr) | ||||||
|     api_fatal ("fork: can't commit memory for stack %p(%d), %E", |     api_fatal ("fork: can't commit memory for stack %p(%d), %E", | ||||||
| 	       new_stack_pointer, stacksize); | 	       stacktop, stacksize); | ||||||
|   if (!VirtualQuery ((LPCVOID) new_stack_pointer, &m, sizeof m)) |   if (guardsize != (size_t) -1) | ||||||
|     api_fatal ("fork: couldn't get new stack info, %E"); |  | ||||||
|  |  | ||||||
|   if (guard) |  | ||||||
|     { |     { | ||||||
|       m.BaseAddress = (LPBYTE) m.BaseAddress - 1; |       /* Allocate PAGE_GUARD page if it still fits. */ | ||||||
|       if (!VirtualAlloc ((LPVOID) m.BaseAddress, 1, MEM_COMMIT, |       if (stack_ptr > stackaddr) | ||||||
| 			 CYGWIN_GUARD)) | 	{ | ||||||
| 	api_fatal ("fork: couldn't allocate new stack guard page %p, %E", | 	  stack_ptr = (void *) ((LPBYTE) stack_ptr | ||||||
| 		   m.BaseAddress); | 					- wincap.page_size ()); | ||||||
|  | 	  if (!VirtualAlloc (stack_ptr, wincap.page_size (), MEM_COMMIT, | ||||||
|  | 			     CYGWIN_GUARD)) | ||||||
|  | 	    api_fatal ("fork: couldn't allocate new stack guard page %p, %E", | ||||||
|  | 		       stack_ptr); | ||||||
|  | 	} | ||||||
|  |       /* Allocate POSIX guard pages. */ | ||||||
|  |       if (guardsize > 0) | ||||||
|  | 	VirtualAlloc (stackaddr, guardsize, MEM_COMMIT, PAGE_NOACCESS); | ||||||
|     } |     } | ||||||
|   if (!VirtualQuery ((LPCVOID) m.BaseAddress, &m, sizeof m)) |  | ||||||
|     api_fatal ("fork: couldn't get new stack info, %E"); |  | ||||||
|   stacktop = m.BaseAddress; |  | ||||||
|   b[0] = '\0'; |   b[0] = '\0'; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -473,7 +457,7 @@ child_info_fork::alloc_stack () | |||||||
|       char *st = (char *) stacktop - 4096; |       char *st = (char *) stacktop - 4096; | ||||||
|       while (_tlstop >= st) |       while (_tlstop >= st) | ||||||
| 	esp = getstack (esp); | 	esp = getstack (esp); | ||||||
|       stacksize = 0; |       stackaddr = 0; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -811,7 +795,7 @@ dll_crt0_1 (void *) | |||||||
|  |  | ||||||
| 	 NOTE: Don't do anything that involves the stack until you've completed | 	 NOTE: Don't do anything that involves the stack until you've completed | ||||||
| 	 this step. */ | 	 this step. */ | ||||||
|       if (fork_info->stacksize) |       if (fork_info->stackaddr) | ||||||
| 	{ | 	{ | ||||||
| 	  _tlsbase = (char *) fork_info->stackbottom; | 	  _tlsbase = (char *) fork_info->stackbottom; | ||||||
| 	  _tlstop = (char *) fork_info->stacktop; | 	  _tlstop = (char *) fork_info->stacktop; | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ details. */ | |||||||
| #include "tls_pbuf.h" | #include "tls_pbuf.h" | ||||||
| #include "dll_init.h" | #include "dll_init.h" | ||||||
| #include "cygmalloc.h" | #include "cygmalloc.h" | ||||||
|  | #include "ntdll.h" | ||||||
|  |  | ||||||
| #define NPIDS_HELD 4 | #define NPIDS_HELD 4 | ||||||
|  |  | ||||||
| @@ -178,7 +179,7 @@ frok::child (volatile char * volatile here) | |||||||
|   /* If we've played with the stack, stacksize != 0.  That means that |   /* If we've played with the stack, stacksize != 0.  That means that | ||||||
|      fork() was invoked from other than the main thread.  Make sure that |      fork() was invoked from other than the main thread.  Make sure that | ||||||
|      the threadinfo information is properly set up.  */ |      the threadinfo information is properly set up.  */ | ||||||
|   if (fork_info->stacksize) |   if (fork_info->stackaddr) | ||||||
|     { |     { | ||||||
|       _main_tls = &_my_tls; |       _main_tls = &_my_tls; | ||||||
|       _main_tls->init_thread (NULL, NULL); |       _main_tls->init_thread (NULL, NULL); | ||||||
| @@ -327,10 +328,33 @@ frok::parent (volatile char * volatile stack_here) | |||||||
|   ch.forker_finished = forker_finished; |   ch.forker_finished = forker_finished; | ||||||
|  |  | ||||||
|   ch.stackbottom = _tlsbase; |   ch.stackbottom = _tlsbase; | ||||||
|   ch.stacktop = (void *) stack_here; |   ch.stacktop = (void *) _tlstop; | ||||||
|   ch.stacksize = (char *) ch.stackbottom - (char *) stack_here; |   ch.stackaddr = 0; | ||||||
|   debug_printf ("stack - bottom %p, top %p, size %d", |   ch.guardsize = 0; | ||||||
| 		ch.stackbottom, ch.stacktop, ch.stacksize); |   if (&_my_tls != _main_tls) | ||||||
|  |     { | ||||||
|  |       /* We have not been started from the main thread.  Fetch the | ||||||
|  | 	 information required to set up the thread stack identically | ||||||
|  | 	 in the child. */ | ||||||
|  |       PTEB teb = NtCurrentTeb (); | ||||||
|  |       if (!teb->DeallocationStack) | ||||||
|  |       	{ | ||||||
|  | 	  /* Pthread with application-provided stack.  Don't set up a | ||||||
|  | 	     PAGE_GUARD page.  guardsize == -1 is used in alloc_stack_hard_way | ||||||
|  | 	     to recognize this type of stack. */ | ||||||
|  | 	  ch.stackaddr = _my_tls.tid->attr.stackaddr; | ||||||
|  | 	  ch.guardsize = (size_t) -1; | ||||||
|  | 	} | ||||||
|  |       else | ||||||
|  | 	{ | ||||||
|  | 	  ch.stackaddr = teb->DeallocationStack; | ||||||
|  | 	  /* If it's a pthread, fetch guardsize from thread attributes. */ | ||||||
|  | 	  if (_my_tls.tid) | ||||||
|  | 	    ch.guardsize = _my_tls.tid->attr.guardsize; | ||||||
|  | 	} | ||||||
|  |     } | ||||||
|  |   debug_printf ("stack - bottom %p, top %p, addr %p, guardsize %p", | ||||||
|  | 		ch.stackbottom, ch.stacktop, ch.stackaddr, ch.guardsize); | ||||||
|  |  | ||||||
|   PROCESS_INFORMATION pi; |   PROCESS_INFORMATION pi; | ||||||
|   STARTUPINFOW si; |   STARTUPINFOW si; | ||||||
|   | |||||||
| @@ -115,7 +115,7 @@ extern void __stdcall dll_crt0_0 (); | |||||||
| extern "C" BOOL WINAPI | extern "C" BOOL WINAPI | ||||||
| dll_entry (HANDLE h, DWORD reason, void *static_load) | dll_entry (HANDLE h, DWORD reason, void *static_load) | ||||||
| { | { | ||||||
|   PNT_TIB tib; |   PTEB teb; | ||||||
|  |  | ||||||
|   switch (reason) |   switch (reason) | ||||||
|     { |     { | ||||||
| @@ -131,10 +131,10 @@ dll_entry (HANDLE h, DWORD reason, void *static_load) | |||||||
| 	 the auto load address of DLLs? | 	 the auto load address of DLLs? | ||||||
| 	 Check if we're running in WOW64 on a 64 bit machine *and* are | 	 Check if we're running in WOW64 on a 64 bit machine *and* are | ||||||
| 	 spawned by a genuine 64 bit process.  If so, respawn. */ | 	 spawned by a genuine 64 bit process.  If so, respawn. */ | ||||||
|       tib = &NtCurrentTeb ()->Tib; |       teb = NtCurrentTeb (); | ||||||
|       if (wincap.is_wow64 () |       if (wincap.is_wow64 () | ||||||
| 	  && tib->StackLimit >= (PBOOL) 0x400000 | 	  && teb->Tib.StackLimit >= (PBOOL) 0x400000 | ||||||
| 	  && tib->StackBase <= (PBOOL) 0x10000000) | 	  && teb->Tib.StackBase <= (PBOOL) 0x10000000) | ||||||
| 	respawn_wow64_process (); | 	respawn_wow64_process (); | ||||||
|  |  | ||||||
|       dll_crt0_0 (); |       dll_crt0_0 (); | ||||||
| @@ -150,12 +150,19 @@ dll_entry (HANDLE h, DWORD reason, void *static_load) | |||||||
| 	munge_threadfunc (); | 	munge_threadfunc (); | ||||||
|       break; |       break; | ||||||
|     case DLL_THREAD_DETACH: |     case DLL_THREAD_DETACH: | ||||||
|       tib = &NtCurrentTeb ()->Tib; |       teb = NtCurrentTeb (); | ||||||
|       if (dll_finished_loading |       if (dll_finished_loading | ||||||
| 	  && (PVOID) &_my_tls >= tib->StackLimit | 	  && (PVOID) &teb >= teb->Tib.StackLimit | ||||||
| 	  && (PVOID) &_my_tls < tib->StackBase | 	  && (PVOID) &teb < teb->Tib.StackBase | ||||||
| 	  && _my_tls.isinitialized ()) | 	  && _my_tls.isinitialized ()) | ||||||
| 	_my_tls.remove (0); | 	_my_tls.remove (0); | ||||||
|  |       /* Windows 2000 has a bug in NtTerminateThread.  Instead of releasing | ||||||
|  | 	 the stack at teb->DeallocationStack it uses the value of | ||||||
|  | 	 teb->Tib.StackLimit to evaluate the stack address.  So we just claim | ||||||
|  | 	 there is no stack. */ | ||||||
|  |       if (teb->DeallocationStack == NULL | ||||||
|  | 	  && !wincap.has_stack_size_param_is_a_reservation ()) | ||||||
|  | 	teb->Tib.StackLimit = NULL; | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -26,6 +26,8 @@ details. */ | |||||||
| #include "fhandler.h" | #include "fhandler.h" | ||||||
| #include "dtable.h" | #include "dtable.h" | ||||||
| #include "cygheap.h" | #include "cygheap.h" | ||||||
|  | #include "pinfo.h" | ||||||
|  | #include "exception.h" | ||||||
|  |  | ||||||
| long tls_ix = -1; | long tls_ix = -1; | ||||||
|  |  | ||||||
| @@ -392,72 +394,109 @@ struct thread_wrapper_arg | |||||||
| { | { | ||||||
|   LPTHREAD_START_ROUTINE func; |   LPTHREAD_START_ROUTINE func; | ||||||
|   PVOID arg; |   PVOID arg; | ||||||
|   PVOID stackaddr; |   char *stackaddr; | ||||||
|   ULONG stacksize; |   char *stackbase; | ||||||
|   ULONG guardsize; |   char *commitaddr; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| DWORD WINAPI | DWORD WINAPI | ||||||
| thread_wrapper (VOID *arg) | thread_wrapper (VOID *arg) | ||||||
| { | { | ||||||
|  |   /* Just plain paranoia. */ | ||||||
|   if (!arg) |   if (!arg) | ||||||
|     return ERROR_INVALID_PARAMETER; |     return ERROR_INVALID_PARAMETER; | ||||||
|  |  | ||||||
|  |   /* Fetch thread wrapper info and free from cygheap. */ | ||||||
|   thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg; |   thread_wrapper_arg wrapper_arg = *(thread_wrapper_arg *) arg; | ||||||
|   cfree (arg); |   cfree (arg); | ||||||
|  |  | ||||||
|   if (wrapper_arg.stackaddr) |   /* Remove _cygtls from this stack since it won't be used anymore. */ | ||||||
|     { |   _cygtls *tls; | ||||||
|       /* If the application provided the stack, we must make sure that |   tls = &_my_tls; | ||||||
| 	 it's actually used by the thread function.  So what we do here is |   _my_tls.remove (0); | ||||||
| 	 to compute the stackbase of the application-provided stack, move |  | ||||||
| 	 _my_tls to the stackbase, and change the stack pointer accordingly. */ |  | ||||||
|       _my_tls.remove (0); |  | ||||||
|       wrapper_arg.stackaddr = (PVOID) ((PBYTE) wrapper_arg.stackaddr |  | ||||||
| 					       + wrapper_arg.stacksize); |  | ||||||
|       _tlsbase = (char *) wrapper_arg.stackaddr; |  | ||||||
|       wrapper_arg.stackaddr = (PVOID) ((PBYTE) wrapper_arg.stackaddr |  | ||||||
| 					       - CYGTLS_PADSIZE); |  | ||||||
|       _tlstop = (char *) wrapper_arg.stackaddr; |  | ||||||
|       _my_tls.init_thread ((char *) wrapper_arg.stackaddr, |  | ||||||
| 			   (DWORD (*)(void*, void*)) wrapper_arg.func); |  | ||||||
|       wrapper_arg.stackaddr = (PVOID) (_tlstop - sizeof (PVOID)); |  | ||||||
|       __asm__ ("\n\ |  | ||||||
| 	       movl  %[WRAPPER_ARG], %%ebx # Load &wrapper_arg into ebx  \n\ |  | ||||||
| 	       movl  (%%ebx), %%eax        # Load thread func into eax   \n\ |  | ||||||
| 	       movl  4(%%ebx), %%ecx       # Load thread arg into ecx    \n\ |  | ||||||
| 	       movl  8(%%ebx), %%edx       # Load stackbase into edx     \n\ |  | ||||||
| 	       xorl  %%ebp, %%ebp          # Set ebp to 0                \n\ |  | ||||||
| 	       movl  %%edx, %%esp          # Set esp to stackbase        \n\ |  | ||||||
| 	       pushl %%ecx                 # Push thread arg onto stack  \n\ |  | ||||||
| 	       pushl %%eax                 # Push thread func onto stack \n\ |  | ||||||
| 	       jmp  *%%eax                 # Jump to thread func         \n" |  | ||||||
| 	       : : [WRAPPER_ARG] "r" (&wrapper_arg)); |  | ||||||
|  |  | ||||||
|     } |   /* Set stack values in TEB */ | ||||||
|   if (wrapper_arg.guardsize) |   PTEB teb = NtCurrentTeb (); | ||||||
|  |   teb->Tib.StackBase = wrapper_arg.stackbase; | ||||||
|  |   teb->Tib.StackLimit = wrapper_arg.commitaddr ?: wrapper_arg.stackaddr; | ||||||
|  |   /* Set DeallocationStack value.  If we have an application-provided stack, | ||||||
|  |      we set DeallocationStack to NULL, so NtTerminateThread does not deallocate | ||||||
|  |      any stack.  If we created the stack in CygwinCreateThread, we set | ||||||
|  |      DeallocationStack to the stackaddr of our own stack, so it's automatically | ||||||
|  |      deallocated when the thread is terminated. */ | ||||||
|  |   char *dealloc_addr = (char *) teb->DeallocationStack; | ||||||
|  |   teb->DeallocationStack = wrapper_arg.commitaddr ? wrapper_arg.stackaddr | ||||||
|  | 						  : NULL; | ||||||
|  |   /* Store the OS-provided DeallocationStack address in wrapper_arg.stackaddr. | ||||||
|  |      The below assembler code will release the OS stack after switching to our | ||||||
|  |      new stack. */ | ||||||
|  |   wrapper_arg.stackaddr = dealloc_addr; | ||||||
|  |  | ||||||
|  |   /* Initialize new _cygtls. */ | ||||||
|  |   _my_tls.init_thread (wrapper_arg.stackbase - CYGTLS_PADSIZE, | ||||||
|  | 		       (DWORD (*)(void*, void*)) wrapper_arg.func); | ||||||
|  |  | ||||||
|  |   /* Copy exception list over to new stack.  I'm not quite sure how the | ||||||
|  |      exception list is extended by Windows itself.  What's clear is that it | ||||||
|  |      always grows downwards and that it starts right at the stackbase. | ||||||
|  |      Therefore we first count the number of exception records and place | ||||||
|  |      the copy at the stackbase, too, so there's still a lot of room to | ||||||
|  |      extend the list up to where our _cygtls region starts. */ | ||||||
|  |   _exception_list *old_start = (_exception_list *) teb->Tib.ExceptionList; | ||||||
|  |   unsigned count = 0; | ||||||
|  |   teb->Tib.ExceptionList = NULL; | ||||||
|  |   for (_exception_list *e_ptr = old_start; | ||||||
|  |        e_ptr && e_ptr != (_exception_list *) -1; | ||||||
|  |        e_ptr = e_ptr->prev) | ||||||
|  |     ++count; | ||||||
|  |   if (count) | ||||||
|     { |     { | ||||||
|       /* Set up POSIX guard pages.  Note that this is not the same as the |       _exception_list *new_start = (_exception_list *) wrapper_arg.stackbase | ||||||
| 	 PAGE_GUARD protection.  Rather, the POSIX guard pages are a | 						       - count; | ||||||
| 	 PAGE_NOACCESS protected area which is supposed to guard against |       teb->Tib.ExceptionList = (struct _EXCEPTION_REGISTRATION_RECORD *) | ||||||
| 	 stack overflow and to trigger a SIGSEGV if that happens. */ | 			       new_start; | ||||||
|       PNT_TIB tib = &NtCurrentTeb ()->Tib; |       while (true) | ||||||
|       wrapper_arg.stackaddr = (PVOID) ((PBYTE) tib->StackBase | 	{ | ||||||
| 				       - wrapper_arg.stacksize); | 	  new_start->handler = old_start->handler; | ||||||
|       if (!VirtualAlloc (wrapper_arg.stackaddr, wrapper_arg.guardsize, | 	  if (old_start->prev == (_exception_list *) -1) | ||||||
| 			 MEM_COMMIT, PAGE_NOACCESS)) | 	    { | ||||||
| 	system_printf ("VirtualAlloc, %E"); | 	      new_start->prev = (_exception_list *) -1; | ||||||
|  | 	      break; | ||||||
|  | 	    } | ||||||
|  | 	  new_start->prev = new_start + 1; | ||||||
|  | 	  new_start = new_start->prev; | ||||||
|  | 	  old_start = old_start->prev; | ||||||
|  | 	} | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   __asm__ ("\n\ |   __asm__ ("\n\ | ||||||
| 	   movl  %[WRAPPER_ARG], %%ebx     # Load &wrapper_arg into ebx  \n\ | 	   movl  %[WRAPPER_ARG], %%ebx # Load &wrapper_arg into ebx  \n\ | ||||||
| 	   movl  (%%ebx), %%eax            # Load thread func into eax   \n\ | 	   movl  (%%ebx), %%eax        # Load thread func into eax   \n\ | ||||||
| 	   movl  4(%%ebx), %%ecx           # Load thread arg into ecx    \n\ | 	   movl  4(%%ebx), %%ecx       # Load thread arg into ecx    \n\ | ||||||
| 	   pushl %%ecx                     # Push thread arg onto stack  \n\ | 	   movl  8(%%ebx), %%edx       # Load stackaddr into edx     \n\ | ||||||
| 	   pushl %%eax                     # Push thread func onto stack \n\ | 	   movl  12(%%ebx), %%ebx      # Load stackbase into ebx     \n\ | ||||||
| 	   jmp  *%%eax                     # Jump to thread func         \n" | 	   subl  %[CYGTLS], %%ebx      # Subtract CYGTLS_PADSIZE     \n\ | ||||||
| 	   : : [WRAPPER_ARG] "r" (&wrapper_arg)); | 	   subl  $4, %%ebx             # Subtract another 4 bytes    \n\ | ||||||
|   /* Never reached. */ | 	   movl  %%ebx, %%esp          # Set esp                     \n\ | ||||||
|   return ERROR_INVALID_FUNCTION; | 	   xorl  %%ebp, %%ebp          # Set ebp to 0                \n\ | ||||||
|  | 	   # Now we moved to the new stack.  Save thread func address\n\ | ||||||
|  | 	   # and thread arg on new stack                             \n\ | ||||||
|  | 	   pushl %%ecx                 # Push thread arg onto stack  \n\ | ||||||
|  | 	   pushl %%eax                 # Push thread func onto stack \n\ | ||||||
|  | 	   # Now it's safe to release the OS stack.                  \n\ | ||||||
|  | 	   pushl $0x8000               # dwFreeType: MEM_RELEASE     \n\ | ||||||
|  | 	   pushl $0x0                  # dwSize:     0               \n\ | ||||||
|  | 	   pushl %%edx                 # lpAddress:  stackaddr       \n\ | ||||||
|  | 	   call _VirtualFree@12        # Shoot                       \n\ | ||||||
|  | 	   # All set.  We can pop the thread function address from   \n\ | ||||||
|  | 	   # the stack and call it.  The thread arg is still on the  \n\ | ||||||
|  | 	   # stack in the expected spot.                             \n\ | ||||||
|  | 	   popl  %%eax                 # Pop thread_func address     \n\ | ||||||
|  | 	   call  *%%eax                # Call thread func            \n" | ||||||
|  | 	   : : [WRAPPER_ARG] "r" (&wrapper_arg), | ||||||
|  | 	       [CYGTLS] "i" (CYGTLS_PADSIZE)); | ||||||
|  |   /* Never return from here. */ | ||||||
|  |   ExitThread (0); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* FIXME: This should be settable via setrlimit (RLIMIT_STACK). */ | /* FIXME: This should be settable via setrlimit (RLIMIT_STACK). */ | ||||||
| @@ -468,9 +507,11 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg, | |||||||
| 		    PVOID stackaddr, ULONG stacksize, ULONG guardsize, | 		    PVOID stackaddr, ULONG stacksize, ULONG guardsize, | ||||||
| 		    DWORD creation_flags, LPDWORD thread_id) | 		    DWORD creation_flags, LPDWORD thread_id) | ||||||
| { | { | ||||||
|  |   PVOID real_stackaddr = NULL; | ||||||
|   ULONG real_stacksize = 0; |   ULONG real_stacksize = 0; | ||||||
|   ULONG real_guardsize = 0; |   ULONG real_guardsize = 0; | ||||||
|   thread_wrapper_arg *wrapper_arg; |   thread_wrapper_arg *wrapper_arg; | ||||||
|  |   HANDLE thread = NULL; | ||||||
|    |    | ||||||
|   wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1, |   wrapper_arg = (thread_wrapper_arg *) ccalloc (HEAP_STR, 1, | ||||||
| 						sizeof *wrapper_arg); | 						sizeof *wrapper_arg); | ||||||
| @@ -488,9 +529,8 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg, | |||||||
|     real_stacksize = PTHREAD_STACK_MIN; |     real_stacksize = PTHREAD_STACK_MIN; | ||||||
|   if (stackaddr) |   if (stackaddr) | ||||||
|     { |     { | ||||||
|       wrapper_arg->stackaddr = stackaddr; |       wrapper_arg->stackaddr = (char *) stackaddr; | ||||||
|       wrapper_arg->stacksize = real_stacksize; |       wrapper_arg->stackbase = (char *) stackaddr + real_stacksize; | ||||||
|       real_stacksize = PTHREAD_STACK_MIN; |  | ||||||
|     } |     } | ||||||
|   else |   else | ||||||
|     { |     { | ||||||
| @@ -498,25 +538,68 @@ CygwinCreateThread (LPTHREAD_START_ROUTINE thread_func, PVOID thread_arg, | |||||||
|       real_stacksize = roundup2 (real_stacksize, wincap.page_size ()); |       real_stacksize = roundup2 (real_stacksize, wincap.page_size ()); | ||||||
|       /* If no guardsize has been specified by the application, use the |       /* If no guardsize has been specified by the application, use the | ||||||
| 	 system pagesize as default. */ | 	 system pagesize as default. */ | ||||||
|       real_guardsize = (guardsize != 0xffffffff) |       real_guardsize = (guardsize != (ULONG) -1) | ||||||
| 		       ? guardsize : wincap.page_size (); | 		       ? guardsize : wincap.page_size (); | ||||||
|       if (real_guardsize) |       if (real_guardsize) | ||||||
| 	real_guardsize = roundup2 (real_guardsize, wincap.page_size ()); | 	real_guardsize = roundup2 (real_guardsize, wincap.page_size ()); | ||||||
|       /* If the default stacksize is used and guardsize has not been specified, |       /* If the default stacksize is used and guardsize has not been specified, | ||||||
| 	 don't add a guard page to the size. */ | 	 don't add a guard page to the size.  Same if stacksize is only | ||||||
|       if (stacksize && guardsize != 0xffffffff) | 	 PTHREAD_STACK_MIN. */ | ||||||
|  |       if (stacksize && guardsize != (ULONG) -1 | ||||||
|  | 	  && real_stacksize > PTHREAD_STACK_MIN) | ||||||
| 	real_stacksize += real_guardsize; | 	real_stacksize += real_guardsize; | ||||||
|       /* Now roundup the result to the next allocation boundary. */ |       /* Now roundup the result to the next allocation boundary. */ | ||||||
|       real_stacksize = roundup2 (real_stacksize, |       real_stacksize = roundup2 (real_stacksize, | ||||||
| 				 wincap.allocation_granularity ()); | 				 wincap.allocation_granularity ()); | ||||||
|  |       /* Reserve stack. | ||||||
|       wrapper_arg->stacksize = real_stacksize; |          FIXME? If the TOP_DOWN method tends to collide too much with | ||||||
|       wrapper_arg->guardsize = real_guardsize; | 	 other stuff, we should provide our own mechanism to find a  | ||||||
|  | 	 suitable place for the stack.  Top down from the start of  | ||||||
|  | 	 the Cygwin DLL comes to mind. */ | ||||||
|  |       real_stackaddr = VirtualAlloc (NULL, real_stacksize, | ||||||
|  | 				     MEM_RESERVE | MEM_TOP_DOWN, | ||||||
|  | 				     PAGE_EXECUTE_READWRITE); | ||||||
|  |       if (!real_stackaddr) | ||||||
|  | 	return NULL; | ||||||
|  |       /* Set up committed region.  In contrast to the OS we commit 64K and | ||||||
|  | 	 set up just a single guard page at the end. */ | ||||||
|  |       char *commitaddr = (char *) real_stackaddr | ||||||
|  | 				  + real_stacksize | ||||||
|  | 				  - wincap.allocation_granularity (); | ||||||
|  |       if (!VirtualAlloc (commitaddr, wincap.page_size (), MEM_COMMIT, | ||||||
|  | 			 PAGE_EXECUTE_READWRITE | PAGE_GUARD)) | ||||||
|  | 	goto err; | ||||||
|  |       commitaddr += wincap.page_size (); | ||||||
|  |       if (!VirtualAlloc (commitaddr, wincap.allocation_granularity () | ||||||
|  | 				     - wincap.page_size (), MEM_COMMIT, | ||||||
|  | 			 PAGE_EXECUTE_READWRITE)) | ||||||
|  | 	goto err; | ||||||
|  |       if (real_guardsize) | ||||||
|  | 	VirtualAlloc (real_stackaddr, real_guardsize, MEM_COMMIT, | ||||||
|  | 		      PAGE_NOACCESS); | ||||||
|  |       wrapper_arg->stackaddr = (char *) real_stackaddr; | ||||||
|  |       wrapper_arg->stackbase = (char *) real_stackaddr + real_stacksize; | ||||||
|  |       wrapper_arg->commitaddr = commitaddr; | ||||||
|     } |     } | ||||||
|   /* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter to make sure the |   /* Use the STACK_SIZE_PARAM_IS_A_RESERVATION parameter so only the | ||||||
|      stack size is exactly the size we want. */ |      minimum size for a thread stack is reserved by the OS.  This doesn't | ||||||
|   return CreateThread (&sec_none_nih, real_stacksize, thread_wrapper, |      work on Windows 2000, but we deallocate the OS stack in thread_wrapper | ||||||
| 		       wrapper_arg, |      anyway, so this should be a problem only in a tight memory condition. | ||||||
| 		       creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION, |      Note that we reserve a 256K stack, not 64K, otherwise the thread creation | ||||||
| 		       thread_id); |      might crash the process due to a stack overflow. */ | ||||||
|  |   thread = CreateThread (&sec_none_nih, 4 * PTHREAD_STACK_MIN, | ||||||
|  | 			 thread_wrapper, wrapper_arg, | ||||||
|  | 			 creation_flags | STACK_SIZE_PARAM_IS_A_RESERVATION, | ||||||
|  | 			 thread_id); | ||||||
|  |  | ||||||
|  | err: | ||||||
|  |   if (!thread && real_stackaddr) | ||||||
|  |     { | ||||||
|  |       /* Don't report the wrong error even though VirtualFree is very unlikely | ||||||
|  | 	 to fail. */ | ||||||
|  |       DWORD err = GetLastError (); | ||||||
|  |       VirtualFree (real_stackaddr, 0, MEM_RELEASE); | ||||||
|  |       SetLastError (err); | ||||||
|  |     } | ||||||
|  |   return thread; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -622,7 +622,7 @@ typedef struct _PEB | |||||||
|   ULONG SessionId; |   ULONG SessionId; | ||||||
| } PEB, *PPEB; | } PEB, *PPEB; | ||||||
|  |  | ||||||
| /* Simplified definition, just to get the TIB and the PEB pointer. */ | /* Simplified definition, just to get stuff we're interested in. */ | ||||||
| typedef struct _TEB | typedef struct _TEB | ||||||
| { | { | ||||||
|   NT_TIB Tib; |   NT_TIB Tib; | ||||||
| @@ -631,6 +631,32 @@ typedef struct _TEB | |||||||
|   PVOID ActiveRpcHandle; |   PVOID ActiveRpcHandle; | ||||||
|   PVOID ThreadLocalStoragePointer; |   PVOID ThreadLocalStoragePointer; | ||||||
|   PPEB Peb; |   PPEB Peb; | ||||||
|  |   ULONG LastErrorValue; | ||||||
|  |   ULONG CountOfOwnedCriticalSections; | ||||||
|  |   PVOID _reserved1[2]; | ||||||
|  |   ULONG _reserved2[31]; | ||||||
|  |   PVOID WOW32Reserved; | ||||||
|  |   ULONG CurrentLocale; | ||||||
|  |   ULONG FpSoftwareStatusRegister; | ||||||
|  |   PVOID SystemReserved1[54]; | ||||||
|  |   LONG ExceptionCode; | ||||||
|  |   PVOID ActivationContextStackPointer; | ||||||
|  |   UCHAR SpareBytes1[36]; | ||||||
|  |   ULONG TxFsContext; | ||||||
|  |   ULONG GdiTebBatch[312]; | ||||||
|  |   CLIENT_ID RealClientId; | ||||||
|  |   PVOID GdiCachedProcessHandle; | ||||||
|  |   ULONG GdiClientPID; | ||||||
|  |   ULONG GdiClientTID; | ||||||
|  |   PVOID GdiThreadLocalInfo; | ||||||
|  |   ULONG Win32ClientInfo[62]; | ||||||
|  |   PVOID glDispatchTable[233]; | ||||||
|  |   ULONG glReserved1[29]; | ||||||
|  |   PVOID glReserved2[6]; | ||||||
|  |   ULONG LastStatusValue; | ||||||
|  |   UNICODE_STRING StaticUnicodeString; | ||||||
|  |   WCHAR StaticUnicodeBuffer[261]; | ||||||
|  |   PVOID DeallocationStack; | ||||||
|   /* A lot more follows... */ |   /* A lot more follows... */ | ||||||
| } TEB, *PTEB; | } TEB, *PTEB; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1089,7 +1089,7 @@ pthread::resume () | |||||||
| pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC), | pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC), | ||||||
| joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS), | joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS), | ||||||
| inheritsched (PTHREAD_INHERIT_SCHED), stackaddr (NULL), stacksize (0), | inheritsched (PTHREAD_INHERIT_SCHED), stackaddr (NULL), stacksize (0), | ||||||
| guardsize (0xffffffff) | guardsize ((size_t) -1) | ||||||
| { | { | ||||||
|   schedparam.sched_priority = 0; |   schedparam.sched_priority = 0; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -51,6 +51,7 @@ wincaps wincap_2000 __attribute__((section (".cygwin_dll_common"), shared)) = { | |||||||
|   has_fast_cwd:false, |   has_fast_cwd:false, | ||||||
|   has_restricted_raw_disk_access:false, |   has_restricted_raw_disk_access:false, | ||||||
|   use_dont_resolve_hack:false, |   use_dont_resolve_hack:false, | ||||||
|  |   has_stack_size_param_is_a_reservation:false, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = { | wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = { | ||||||
| @@ -81,6 +82,7 @@ wincaps wincap_2000sp4 __attribute__((section (".cygwin_dll_common"), shared)) = | |||||||
|   has_fast_cwd:false, |   has_fast_cwd:false, | ||||||
|   has_restricted_raw_disk_access:false, |   has_restricted_raw_disk_access:false, | ||||||
|   use_dont_resolve_hack:false, |   use_dont_resolve_hack:false, | ||||||
|  |   has_stack_size_param_is_a_reservation:false, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = { | wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = { | ||||||
| @@ -111,6 +113,7 @@ wincaps wincap_xp __attribute__((section (".cygwin_dll_common"), shared)) = { | |||||||
|   has_fast_cwd:false, |   has_fast_cwd:false, | ||||||
|   has_restricted_raw_disk_access:false, |   has_restricted_raw_disk_access:false, | ||||||
|   use_dont_resolve_hack:true, |   use_dont_resolve_hack:true, | ||||||
|  |   has_stack_size_param_is_a_reservation:true, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = { | wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = { | ||||||
| @@ -141,6 +144,7 @@ wincaps wincap_xpsp1 __attribute__((section (".cygwin_dll_common"), shared)) = { | |||||||
|   has_fast_cwd:false, |   has_fast_cwd:false, | ||||||
|   has_restricted_raw_disk_access:false, |   has_restricted_raw_disk_access:false, | ||||||
|   use_dont_resolve_hack:true, |   use_dont_resolve_hack:true, | ||||||
|  |   has_stack_size_param_is_a_reservation:true, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { | wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { | ||||||
| @@ -171,6 +175,7 @@ wincaps wincap_xpsp2 __attribute__((section (".cygwin_dll_common"), shared)) = { | |||||||
|   has_fast_cwd:false, |   has_fast_cwd:false, | ||||||
|   has_restricted_raw_disk_access:false, |   has_restricted_raw_disk_access:false, | ||||||
|   use_dont_resolve_hack:true, |   use_dont_resolve_hack:true, | ||||||
|  |   has_stack_size_param_is_a_reservation:true, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { | wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { | ||||||
| @@ -201,6 +206,7 @@ wincaps wincap_2003 __attribute__((section (".cygwin_dll_common"), shared)) = { | |||||||
|   has_fast_cwd:false, |   has_fast_cwd:false, | ||||||
|   has_restricted_raw_disk_access:false, |   has_restricted_raw_disk_access:false, | ||||||
|   use_dont_resolve_hack:true, |   use_dont_resolve_hack:true, | ||||||
|  |   has_stack_size_param_is_a_reservation:true, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { | wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { | ||||||
| @@ -231,6 +237,7 @@ wincaps wincap_vista __attribute__((section (".cygwin_dll_common"), shared)) = { | |||||||
|   has_fast_cwd:true, |   has_fast_cwd:true, | ||||||
|   has_restricted_raw_disk_access:true, |   has_restricted_raw_disk_access:true, | ||||||
|   use_dont_resolve_hack:false, |   use_dont_resolve_hack:false, | ||||||
|  |   has_stack_size_param_is_a_reservation:true, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { | wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { | ||||||
| @@ -261,6 +268,7 @@ wincaps wincap_7 __attribute__((section (".cygwin_dll_common"), shared)) = { | |||||||
|   has_fast_cwd:true, |   has_fast_cwd:true, | ||||||
|   has_restricted_raw_disk_access:true, |   has_restricted_raw_disk_access:true, | ||||||
|   use_dont_resolve_hack:false, |   use_dont_resolve_hack:false, | ||||||
|  |   has_stack_size_param_is_a_reservation:true, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| wincapc wincap __attribute__((section (".cygwin_dll_common"), shared)); | wincapc wincap __attribute__((section (".cygwin_dll_common"), shared)); | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ struct wincaps | |||||||
|   unsigned has_fast_cwd					: 1; |   unsigned has_fast_cwd					: 1; | ||||||
|   unsigned has_restricted_raw_disk_access		: 1; |   unsigned has_restricted_raw_disk_access		: 1; | ||||||
|   unsigned use_dont_resolve_hack			: 1; |   unsigned use_dont_resolve_hack			: 1; | ||||||
|  |   unsigned has_stack_size_param_is_a_reservation	: 1; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class wincapc | class wincapc | ||||||
| @@ -90,6 +91,7 @@ public: | |||||||
|   bool	IMPLEMENT (has_fast_cwd) |   bool	IMPLEMENT (has_fast_cwd) | ||||||
|   bool	IMPLEMENT (has_restricted_raw_disk_access) |   bool	IMPLEMENT (has_restricted_raw_disk_access) | ||||||
|   bool	IMPLEMENT (use_dont_resolve_hack) |   bool	IMPLEMENT (use_dont_resolve_hack) | ||||||
|  |   bool	IMPLEMENT (has_stack_size_param_is_a_reservation) | ||||||
|  |  | ||||||
| #undef IMPLEMENT | #undef IMPLEMENT | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user