1/*$Header: /p/tcsh/cvsroot/tcsh/win32/fork.c,v 1.11 2008/08/31 14:09:01 amold Exp $*/ 2/*- 3 * Copyright (c) 1980, 1991 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31/* 32 * The fork() here is based on the ideas used by cygwin 33 * -amol 34 * 35 */ 36 37/* 38 * _M_ALPHA changes by Mark Tucker 39 */ 40 41#define WIN32_LEAN_AND_MEAN 42#include <windows.h> 43#include <fcntl.h> 44#include <io.h> 45#include <stdlib.h> 46#include <setjmp.h> 47#include <ntport.h> 48#include "forkdata.h" 49#include "sh.h" 50 51#pragma intrinsic("memcpy", "memset","memcmp") 52#pragma warning(push,3) // forget about W4 here 53 54typedef unsigned long u_long; 55typedef void *ptr_t; 56typedef unsigned char U_char; 57typedef unsigned int U_int; 58typedef unsigned short U_short; 59typedef unsigned long U_long; 60 61 62static void stack_probe(void *ptr) ; 63/*static void heap_init(void);*/ 64BOOL CreateWow64Events(DWORD , HANDLE *, HANDLE *, BOOL); 65 66// 67// This is exported from the user program. 68// It must return 0 for no error !!!! 69extern int fork_copy_user_mem(HANDLE ); 70 71/* 72 * Apparently , visual c++ on the alpha does not place the 73 * fork data contiguously. To work around that, Mark created 74 * this structure (see forkdata.h) 75 * -amol 76 */ 77ForkData gForkData = {0,0,0,0,0,{0},0,0,0}; 78 79 80#ifdef _M_IX86 81 82u_long _old_exr = 0; // Saved exception registration for longjmp 83 84#endif // _M_ALPHA 85/* 86 * This hack is an attempt at getting to the exception registration 87 * in an architecture-independent way. It's critical for longjmp in a 88 * code using __try/__except blocks. Microsoft Visual C++ does a global 89 * unwind during a longjmp, and that can cause havoc if the exception 90 * registration stored in longjmp is lower(address wise, indicating a jump 91 * from below of the stack upward.) in the stack than the current 92 * registration (returned by NtCurrentTeb). 93 * 94 * This works with VC++, because that's all I have. With other compilers, 95 * there might be minimal changes required, depending on where the 96 * exception registration record is stored in the longjmp structure. 97 * 98 * -amol 2/6/97 99 */ 100 101NT_TIB * (* myNtCurrentTeb)(void); 102 103#define GETEXCEPTIONREGIST() (((NT_TIB*)get_teb())->ExceptionList) 104#define GETSTACKBASE() (((NT_TIB*)get_teb())->StackBase) 105 106 107 108static NT_TIB *the_tib; 109 110#if !defined(_M_IA64) && !defined(_M_AMD64) 111void *get_teb(void) { 112 113 114 if (the_tib) 115 return the_tib; 116 117 myNtCurrentTeb = (void*)GetProcAddress(LoadLibrary("ntdll.dll"), 118 "NtCurrentTeb"); 119 if (!myNtCurrentTeb) 120 return NULL; 121 the_tib = myNtCurrentTeb(); 122 123 if (the_tib == NULL) 124 abort(); 125 return the_tib; 126} 127#else 128#define get_teb NtCurrentTeb 129#endif _M_IA64 130 131void set_stackbase(void*ptr){ 132 GETSTACKBASE() = ptr; 133} 134/* 135 * This must be called by the application as the first thing it does. 136 * -amol 2/6/97 137 * 138 * Well, maybe not the FIRST.. 139 * -amol 11/10/97 140 */ 141 142extern BOOL bIsWow64Process; 143 144int fork_init(void) { 145 146 147 //heap_init(); Now called as the very first thing in silly_entry(). 148 149 if (__forked) { 150 151 152 // stack_probe probes out a decent-sized stack for the child, 153 // since initially it has a very small stack (1 page). 154 // 155 156 /* not needed since default commit is set to 0.5MB in 157 * makefile.win32 158 * 159 * stack_probe((char *)__fork_stack_end - 64); 160 */ 161 162 // 163 // Save the old Exception registration record and jump 164 // off the cliff. 165 // 166#ifdef _M_IX86 167 _old_exr = __fork_context[6]; 168 __fork_context[6] =(int)GETEXCEPTIONREGIST();//tmp; 169#endif _M_ALPHA 170 // 171 // Whee ! 172 longjmp(__fork_context,1); 173 } 174 175 return 0; 176} 177int fork(void) { 178 179 size_t rc; 180 size_t stacksize; 181 char modname[512];/*FIXBUF*/ 182 HANDLE hProc,hThread, hArray[2]; 183 STARTUPINFO si; 184 PROCESS_INFORMATION pi; 185 SECURITY_ATTRIBUTES sa; 186 DWORD dwCreationflags; 187 unsigned int priority; 188 HANDLE h64Parent,h64Child; 189 190#ifndef _M_ALPHA 191 unsigned long fork_stack_end; 192#endif _M_ALPHA 193 194 __fork_stack_begin =GETSTACKBASE(); 195 196#ifndef _M_ALPHA 197 __fork_stack_end = &fork_stack_end; 198#else 199 __fork_stack_end = (unsigned long *)__asm("mov $sp, $0"); 200#endif /*_M_ALPHA*/ 201 202 h64Parent = h64Child = NULL; 203 // 204 // Create two inheritable events 205 // 206 sa.nLength = sizeof(sa); 207 sa.lpSecurityDescriptor =0; 208 sa.bInheritHandle = TRUE; 209 if (!__hforkchild) 210 __hforkchild = CreateEvent(&sa,TRUE,FALSE,NULL); 211 if (!__hforkparent) 212 __hforkparent = CreateEvent(&sa,TRUE,FALSE,NULL); 213 214 rc = setjmp(__fork_context); 215 216 if (rc) { // child 217#ifdef _M_IX86 218 // 219 // Restore old registration 220 // -amol 2/2/97 221 GETEXCEPTIONREGIST() = (struct _EXCEPTION_REGISTRATION_RECORD*)_old_exr; 222#endif // _M_ALPHA 223 SetEvent(__hforkchild); 224 225 dprintf("Child ready to rumble\n"); 226 if(WaitForSingleObject(__hforkparent,FORK_TIMEOUT) != WAIT_OBJECT_0) 227 ExitProcess(0xFFFF); 228 229 CloseHandle(__hforkchild); 230 CloseHandle(__hforkparent); 231 __hforkchild = __hforkparent=0; 232 233 //__asm { int 3}; 234 restore_fds(); 235 236 STR_environ = blk2short(environ); 237 environ = short2blk(STR_environ); /* So that we can free it */ 238 239 return 0; 240 } 241 copy_fds(); 242 memset(&si,0,sizeof(si)); 243 si.cb= sizeof(si); 244 245 /* 246 * This f!@#!@% function returns the old value even if the std handles 247 * have been closed. 248 * Skip this step, since we know tcsh will do the right thing later. 249 * 250 si.hStdInput= GetStdHandle(STD_INPUT_HANDLE); 251 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); 252 si.hStdError = GetStdHandle(STD_ERROR_HANDLE); 253 */ 254 255 if (!GetModuleFileName(GetModuleHandle(NULL),modname,512) ) { 256 rc = GetLastError(); 257 return -1; 258 } 259 dwCreationflags = GetPriorityClass(GetCurrentProcess()); 260 priority = GetThreadPriority(GetCurrentThread()); 261 rc = CreateProcess(NULL, 262 modname, 263 NULL, 264 NULL, 265 TRUE, 266 CREATE_SUSPENDED | dwCreationflags, 267 NULL, 268 NULL, 269 &si, 270 &pi); 271 if (!rc) { 272 rc = GetLastError(); 273 return -1; 274 } 275 276 ResetEvent(__hforkchild); 277 ResetEvent(__hforkparent); 278 279 hProc = pi.hProcess; 280 hThread = pi.hThread; 281 282 283 __forked=1; 284 /* 285 * Usage of events in the wow64 case: 286 * 287 * h64Parent : initially non-signalled 288 * h64Child : initially non-signalled 289 * 290 * 1. Create the events, resume the child thread. 291 * 2. Child opens h64Parent to see if it is a child process in wow64 292 * 3. Child opens and sets h64Child to tell parent it's running. (This 293 * step is needed because we can't copy to a process created in the 294 * suspended state on wow64.) 295 * 4. Copy gForkData and then set h64Parent. This tells the child 296 * that the parameters in the structure are trustworthy. 297 * 5. Wait for h64Child so that we know the child has created the stack 298 * in dynamic memory. 299 * 300 * The rest of the fork hack should now proceed as in x86 301 * 302 */ 303 if (bIsWow64Process) { 304 305 // allocate the heap for the child. this can be done even when 306 // the child is suspended. 307 // avoids inexplicable allocation failures in the child. 308 if (VirtualAllocEx(hProc, 309 __heap_base, 310 __heap_size, 311 MEM_RESERVE, 312 PAGE_READWRITE) == NULL) { 313 dprintf("virtual allocex failed %d\n",GetLastError()); 314 goto error; 315 } 316 if (VirtualAllocEx(hProc, 317 __heap_base, 318 __heap_size, 319 MEM_COMMIT, 320 PAGE_READWRITE) == NULL) { 321 dprintf("virtual allocex2 failed %d\n",GetLastError()); 322 goto error; 323 } 324 325 // Do NOT expect existing events 326 if (!CreateWow64Events(pi.dwProcessId,&h64Parent,&h64Child,FALSE)) { 327 goto error; 328 } 329 ResumeThread(hThread); 330 331 // wait for the child to tell us it is running 332 //if (WaitForSingleObject(h64Child,FORK_TIMEOUT) != WAIT_OBJECT_0) { 333 // rc = GetLastError(); 334 // goto error; 335 //} 336 hArray[0] = h64Child; 337 hArray[1] = hProc; 338 339 if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) != 340 WAIT_OBJECT_0){ 341 342 rc = GetLastError(); 343 goto error; 344 } 345 346 } 347 // 348 // Copy all the shared data 349 // 350 if (!WriteProcessMemory(hProc,&gForkData,&gForkData, 351 sizeof(ForkData),&rc)) { 352 goto error; 353 } 354 if (rc != sizeof(ForkData)) 355 goto error; 356 357 if (!bIsWow64Process) { 358 rc = ResumeThread(hThread); 359 } 360 // in the wow64 case, the child will be waiting on h64parent again. 361 // set it, and then wait for h64child. This will mean the child has 362 // a stack set up at the right location. 363 else { 364 SetEvent(h64Parent); 365 hArray[0] = h64Child; 366 hArray[1] = hProc; 367 368 if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) != 369 WAIT_OBJECT_0){ 370 371 rc = GetLastError(); 372 goto error; 373 } 374 CloseHandle(h64Parent); 375 CloseHandle(h64Child); 376 h64Parent = h64Child = NULL; 377 } 378 379 // 380 // Wait for the child to start and init itself. 381 // The timeout is so that we don't wait too long 382 // 383 hArray[0] = __hforkchild; 384 hArray[1] = hProc; 385 386 if (WaitForMultipleObjects(2,hArray,FALSE,FORK_TIMEOUT) != WAIT_OBJECT_0){ 387 388 int err = GetLastError(); // For debugging purposes 389 dprintf("wait failed err %d\n",err); 390 goto error; 391 } 392 393 // Stop the child again and copy the stack and heap 394 // 395 SuspendThread(hThread); 396 397 if (!SetThreadPriority(hThread,priority) ) { 398 priority =GetLastError(); 399 } 400 401 // stack 402 stacksize = (char*)__fork_stack_begin - (char*)__fork_stack_end; 403 if (!WriteProcessMemory(hProc,(char *)__fork_stack_end, 404 (char *)__fork_stack_end, 405 (u_long)stacksize, 406 &rc)){ 407 goto error; 408 } 409 // 410 // copy heap itself 411 if (!WriteProcessMemory(hProc, (void*)__heap_base,(void*)__heap_base, 412 (DWORD)((char*)__heap_top-(char*)__heap_base), 413 &rc)){ 414 goto error; 415 } 416 417 rc = fork_copy_user_mem(hProc); 418 419 if(rc) { 420 goto error; 421 } 422 423 // Release the child. 424 SetEvent(__hforkparent); 425 rc = ResumeThread(hThread); 426 427 __forked=0; 428 dprintf("forked process %d\n",pi.dwProcessId); 429 start_sigchild_thread(hProc,pi.dwProcessId); 430 close_copied_fds(); 431 432 CloseHandle(hThread); 433 // 434 // return process id to parent. 435 return pi.dwProcessId; 436 437error: 438 __forked=0; 439 SetEvent(__hforkparent); 440 ResumeThread(hThread); 441 CloseHandle(hProc); 442 CloseHandle(hThread); 443 if (h64Parent) { 444 SetEvent(h64Parent); // don't let child block forever 445 CloseHandle(h64Parent); 446 } 447 if (h64Child) 448 CloseHandle(h64Child); 449 return -1; 450} 451#pragma optimize("",off) 452// The damn optimizer will remove the recursion, resulting in an infinite 453// loop. -amol 4/17/97 454void stack_probe (void *ptr) { 455 char buf[1000]; 456 int x; 457 458 if (&x > (int *)ptr) 459 stack_probe(ptr); 460 (void)buf; 461} 462#pragma optimize("",on) 463// 464// This function basically reserves some heap space. 465// In the child it also commits the size committed in the parent. 466void heap_init(void) { 467 468 char * temp; 469 int err; 470 if (__forked) { 471 temp = (char *)VirtualAlloc((void*)__heap_base,__heap_size, MEM_RESERVE, 472 PAGE_READWRITE); 473 if (temp != (char*)__heap_base) { 474 if (!temp){ 475 err = GetLastError(); 476 if (bIsWow64Process) 477 ExitProcess(0); 478 abort(); 479 } 480 else 481 __heap_base = temp; 482 } 483 if (!VirtualAlloc(__heap_base,(char*)__heap_top -(char*)__heap_base, 484 MEM_COMMIT,PAGE_READWRITE)){ 485 err = GetLastError(); 486 if (bIsWow64Process) 487 ExitProcess(0); 488 abort(); 489 } 490 temp = (char*)__heap_base; 491 } 492 else { 493 SYSTEM_INFO sysinfo; 494 GetSystemInfo(&sysinfo); 495 __heap_size = sysinfo.dwPageSize * 1024; 496 __heap_base = VirtualAlloc(0 , __heap_size,MEM_RESERVE|MEM_TOP_DOWN, 497 PAGE_READWRITE); 498 499 if (__heap_base == 0) { 500 abort(); 501 } 502 503 __heap_top = __heap_base; 504 } 505 506} 507// 508// Implementation of sbrk() for the fmalloc family 509// 510void * sbrk(int delta) { 511 512 void *retval; 513 void *old_top=__heap_top; 514 char *b = (char*)__heap_top; 515 516 if (delta == 0) 517 return __heap_top; 518 if (delta > 0) { 519 520 retval =VirtualAlloc((void*)__heap_top,delta,MEM_COMMIT,PAGE_READWRITE); 521 522 if (retval == 0 ) 523 abort(); 524 525 b += delta; 526 __heap_top = (void*)b; 527 } 528 else { 529 retval = VirtualAlloc((void*)((char*)__heap_top - delta), 530 delta,MEM_DECOMMIT, PAGE_READWRITE); 531 532 if (retval == 0) 533 abort(); 534 535 b -= delta; 536 __heap_top = (void*)b; 537 } 538 539 return (void*) old_top; 540} 541/* 542 * Semantics of CreateWow64Events 543 * 544 * Try to open the events even if bOpenExisting is FALSE. This will help 545 * us detect name duplication. 546 * 547 * 1. If OpenEvent succeeds,and bOpenExisting is FALSE, fail. 548 * 549 * 2. If OpenEvent failed,and bOpenExisting is TRUE fail 550 * 551 * 3. else create the events anew 552 * 553 */ 554#define TCSH_WOW64_PARENT_EVENT_NAME "tcsh-wow64-parent-event" 555#define TCSH_WOW64_CHILD_EVENT_NAME "tcsh-wow64-child-event" 556BOOL CreateWow64Events(DWORD pid, HANDLE *hParent, HANDLE *hChild, 557 BOOL bOpenExisting) { 558 559 SECURITY_ATTRIBUTES sa; 560 char parentname[256],childname[256]; 561 562 *hParent = *hChild = NULL; 563 564 // make darn sure they're not inherited 565 sa.nLength = sizeof(sa); 566 sa.lpSecurityDescriptor =0; 567 sa.bInheritHandle = FALSE; 568 // 569 570#pragma warning(disable:4995) 571 572 // This event tells the child to hold for gForkData to be copied 573 wsprintfA(parentname, "Local\\%d-%s",pid, TCSH_WOW64_PARENT_EVENT_NAME); 574 575 wsprintfA(childname, "Local\\%d-%s",pid, TCSH_WOW64_CHILD_EVENT_NAME ); 576 577#pragma warning(default:4995) 578 579 *hParent = OpenEvent(EVENT_ALL_ACCESS,FALSE, parentname); 580 581 if(*hParent) { 582 if (bOpenExisting == FALSE) { // didn't expect to be a child process 583 CloseHandle(*hParent); 584 *hParent = NULL; 585 return FALSE; 586 } 587 588 *hChild = OpenEvent(EVENT_ALL_ACCESS,FALSE, childname); 589 if (!*hChild) { 590 CloseHandle(*hParent); 591 *hParent = NULL; 592 return FALSE; 593 } 594 595 return TRUE; 596 } 597 else { //event does not exist 598 if (bOpenExisting == TRUE) 599 return FALSE; 600 } 601 602 *hParent = CreateEvent(&sa,FALSE,FALSE,parentname); 603 if (!*hParent) 604 return FALSE; 605 606 607 *hChild = CreateEvent(&sa,FALSE,FALSE,childname); 608 if (!*hChild){ 609 CloseHandle(*hParent); 610 *hParent = NULL; 611 return FALSE; 612 } 613 return TRUE; 614} 615