OSDN Git Service

* dcrt0.cc (_dll_crt0): Fix formatting.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / dcrt0.cc
index 6c13eff..8b69939 100644 (file)
@@ -37,6 +37,7 @@ details. */
 #include "cygxdr.h"
 #include "fenv.h"
 #include "ntdll.h"
+#include "wow64.h"
 
 #define MAX_AT_FILE_LEVEL 10
 
@@ -385,7 +386,7 @@ check_sanity_and_sync (per_process *p)
 
 child_info NO_COPY *child_proc_info = NULL;
 
-#define CYGWIN_GUARD (PAGE_EXECUTE_READWRITE | PAGE_GUARD)
+#define CYGWIN_GUARD (PAGE_READWRITE | PAGE_GUARD)
 
 void
 child_info_fork::alloc_stack_hard_way (volatile char *b)
@@ -408,8 +409,7 @@ child_info_fork::alloc_stack_hard_way (volatile char *b)
     api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
               stackaddr, stackbottom);
   stacksize = (char *) stackbottom - (char *) stacktop;
-  stack_ptr = VirtualAlloc (stacktop, stacksize, MEM_COMMIT,
-                           PAGE_EXECUTE_READWRITE);
+  stack_ptr = VirtualAlloc (stacktop, stacksize, MEM_COMMIT, PAGE_READWRITE);
   if (!stack_ptr)
     abort ("can't commit memory for stack %p(%d), %E", stacktop, stacksize);
   if (guardsize != (size_t) -1)
@@ -447,7 +447,17 @@ child_info_fork::alloc_stack ()
 {
   volatile char * volatile esp;
   __asm__ volatile ("movl %%esp,%0": "=r" (esp));
-  if (_tlsbase != stackbottom)
+  /* Make sure not to try a hard allocation if we have been forked off from
+     the main thread of a Cygwin process which has been started from a 64 bit
+     parent.  In that case the _tlsbase of the forked child is not the same
+     as the _tlsbase of the parent (== stackbottom), but only because the
+     stack of the parent has been slightly rearranged.  See comment in
+     wow64_revert_to_original_stack for details. We check here if the
+     parent stack fits into the child stack. */
+  if (_tlsbase != stackbottom
+      && (!wincap.is_wow64 ()
+         || stacktop < (char *) NtCurrentTeb ()->DeallocationStack
+         || stackbottom > _tlsbase))
     alloc_stack_hard_way (esp);
   else
     {
@@ -455,6 +465,11 @@ child_info_fork::alloc_stack ()
       while (_tlstop >= st)
        esp = getstack (esp);
       stackaddr = 0;
+      /* This only affects forked children of a process started from a native
+         64 bit process, but it doesn't hurt to do it unconditionally.  Fix
+        StackBase in the child to be the same as in the parent, so that the
+        computation of _my_tls is correct. */
+      _tlsbase = (char *) stackbottom;
     }
 }
 
@@ -638,12 +653,23 @@ child_info_spawn::handle_spawn ()
 static void
 init_windows_system_directory ()
 {
-  windows_system_directory_length =
-       GetSystemDirectoryW (windows_system_directory, MAX_PATH);
-  if (windows_system_directory_length == 0)
-    api_fatal ("can't find windows system directory");
-  windows_system_directory[windows_system_directory_length++] = L'\\';
-  windows_system_directory[windows_system_directory_length] = L'\0';
+  if (!windows_system_directory_length)
+    {
+      windows_system_directory_length =
+           GetSystemDirectoryW (windows_system_directory, MAX_PATH);
+      if (windows_system_directory_length == 0)
+       api_fatal ("can't find windows system directory");
+      windows_system_directory[windows_system_directory_length++] = L'\\';
+      windows_system_directory[windows_system_directory_length] = L'\0';
+
+      system_wow64_directory_length =
+       GetSystemWow64DirectoryW (system_wow64_directory, MAX_PATH);
+      if (system_wow64_directory_length)
+       {
+         system_wow64_directory[system_wow64_directory_length++] = L'\\';
+         system_wow64_directory[system_wow64_directory_length] = L'\0';
+       }
+    }
 }
 
 void
@@ -677,7 +703,14 @@ dll_crt0_0 ()
   cygthread::init ();
 
   if (!child_proc_info)
-    memory_init (true);
+    {
+      memory_init (true);
+      /* WOW64 process?  Check if we have been started from 64 bit process
+         and if our stack is at an unusual address.  Set wow64_has_64bit_parent
+        if so.  Problem description in wow64_test_for_64bit_parent. */
+      if (wincap.is_wow64 ())
+       wow64_has_64bit_parent = wow64_test_for_64bit_parent ();
+    }
   else
     {
       cygwin_user_h = child_proc_info->user_h;
@@ -912,6 +945,33 @@ __cygwin_exit_return:                      \n\
 extern "C" void __stdcall
 _dll_crt0 ()
 {
+  /* Handle WOW64 process started from native 64 bit process.  See comment
+     in wow64_test_for_64bit_parent for a full problem description. */
+  if (wow64_has_64bit_parent && !dynamically_loaded)
+    {
+      /* Must be static since it's referenced after the stack pointers have
+        been moved. */
+      static PVOID allocationbase = 0;
+
+      /* Check if we just move the stack.  See comment in
+        wow64_revert_to_original_stack for the gory details. */
+      PVOID stackaddr = wow64_revert_to_original_stack (allocationbase);
+      if (stackaddr)
+       {
+         /* 2nd half of the stack move.  Set stack pointers to new address. */
+         __asm__ ("\n\
+                  movl  %[ADDR], %%esp \n\
+                  movl  %%esp, %%ebp   \n"
+                  : : [ADDR] "r" (stackaddr));
+         /* Now we're back on the original stack.  Free up space taken by the
+            former main thread stack and set DeallocationStack correctly. */
+         VirtualFree (NtCurrentTeb ()->DeallocationStack, 0, MEM_RELEASE);
+         NtCurrentTeb ()->DeallocationStack = allocationbase;
+       }
+      else
+       /* Fall back to respawn if wow64_revert_to_original_stack fails. */
+       wow64_respawn_process ();
+    }
 #ifdef __i386__
   _feinitialise ();
 #endif