From: Fuehrer AT seabase DOT com (Gary Fuehrer) Subject: RE: Bug with Fix: mmap(MAP_PRIVATE) region handling in fork() 30 Aug 1997 23:06:18 -0700 Approved: cygnus DOT gnu-win32 AT cygnus DOT com Distribution: cygnus Message-ID: <6CC63E2E4FC1D011A2A700609716117A266F16.cygnus.gnu-win32@seawolf> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="---- =_NextPart_000_01BCB240.03307650" Original-To: "'gnu-win32 AT cygnus DOT com'" X-Priority: 3 X-Mailer: Internet Mail Service (5.0.1458.49) Original-Sender: owner-gnu-win32 AT cygnus DOT com This message is in MIME format. Since your mail reader does not understand this format, some or all of this message may not be legible. ------ =_NextPart_000_01BCB240.03307650 Content-Type: text/plain > Download cdk-src.tar.gz from /pub/gnu-win32/gnu-win32-b18/ at > ftp.cygnus.com. After expanding it out, you will have a "cdk" > subdirectory. The "winsup" directory is in there. > > Attached are my copies of the files (except for "winsup.h" which was > trivial). My e-mail about the changes came out crappy because of all > the gratuitous line wrapping. So, maybe it would be easier to diff my > version with the original. > > > > > Gary > > > ---------- > From: Martin Schrape[SMTP:schrape AT atomika DOT com] > Sent: Tuesday, August 26, 1997 1:56 PM > To: Gary Fuehrer > Subject: RE: Bug with Fix: mmap(MAP_PRIVATE) region handling in > fork() > > > > -----Original Message----- > From: Gary Fuehrer [SMTP:Fuehrer AT seabase DOT com] > Sent: Friday, August 22, 1997 10:42 PM > To: 'gnu-win32 AT cygnus DOT com' > Subject: Bug with Fix: mmap(MAP_PRIVATE) region handling in > fork() > > > The following changes to three files (winsup.h, fork.cc, mmap.cc) will > fix this bug. > > ================ > winsup.h > ================ > > [schrape] Gary, > sorry but where did you get the winsup files from. I can't find them > on the cygnus ftp server. > --martin > ------ =_NextPart_000_01BCB240.03307650 Content-Type: application/octet-stream; name="fork.cc" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="fork.cc" /* fork.cc: fork for WIN32.=0A= =0A= Copyright 1996, 1997 Cygnus Solutions=0A= =0A= This program is free software; you can redistribute it and/or modify=0A= it under the terms of the GNU General Public License as published by=0A= the Free Software Foundation; either version 2 of the License, or=0A= (at your option) any later version.=0A= =0A= This program is distributed in the hope that it will be useful,=0A= but WITHOUT ANY WARRANTY; without even the implied warranty of=0A= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the=0A= GNU General Public License for more details.=0A= =0A= You should have received a copy of the GNU General Public License=0A= along with this program; if not, write to the Free Software=0A= Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, = USA. */=0A= =0A= #include =0A= #include =0A= #include "winsup.h"=0A= =0A= extern "C" struct _reent reent_data;=0A= =0A= /* Timeout to wait for child to start, parent to init child, etc. = */=0A= /* FIXME: Once things stabilize, bump up to a few minutes. */=0A= #define FORK_WAIT_TIMEOUT (120 * 1000 /* 120 seconds */)=0A= =0A= /* Error return code from WaitForSingleObject. */=0A= #define WAIT_ERROR_RC 0xffffffff=0A= =0A= /* Mutexes and events necessary for child startup. */=0A= static HANDLE forkee_stopped, forker_stopped;=0A= =0A= /* Initialize the fork mechanism. */=0A= =0A= void=0A= fork_init ()=0A= {=0A= SECURITY_ATTRIBUTES sa;=0A= sa.nLength =3D sizeof (sa);=0A= sa.lpSecurityDescriptor =3D get_null_sd();=0A= sa.bInheritHandle =3D 0;=0A= =0A= forker_stopped =3D CreateEventA (&sa, TRUE, TRUE, = "cygwin.forker-stopped");=0A= forkee_stopped =3D CreateEventA (&sa, TRUE, TRUE, = "cygwin.forkee-stopped");=0A= =0A= /* If we didn't obtain all the resources we need to fork, allow the = program=0A= to continue, but record the fact that fork won't work. */=0A= if (forker_stopped =3D=3D NULL=0A= || forkee_stopped =3D=3D NULL)=0A= {=0A= system_printf ("fork_init: unable to allocate fork() = resources.\n");=0A= system_printf ("fork_init: fork() disabled.\n");=0A= }=0A= }=0A= =0A= /* Undo what fork_init does.=0A= =0A= If a task is shutting down, this isn't really necessary, but maybe = one day=0A= we'll want to allow cygwin.dll to be attached to and detached from, = so we=0A= should provide a clean interface to release all resources we obtain. = */=0A= =0A= void=0A= fork_terminate ()=0A= {=0A= CloseHandle (forker_stopped);=0A= CloseHandle (forkee_stopped);=0A= }=0A= =0A= #if 0=0A= void=0A= print_checksum (int idx, register void *low, register void *high)=0A= {=0A= int pi;=0A= register int sum =3D 0;=0A= small_printf ("CK %d %x %x ", idx, low, high);=0A= =0A= for (int *pi =3D (int *)low; pi < (int *)high; pi++)=0A= {=0A= sum +=3D *pi;=0A= }=0A= small_printf ("%x\n", sum);=0A= }=0A= #endif=0A= =0A= /* Copy memory from parent to child.=0A= The result is a boolean indicating success. */=0A= =0A= int=0A= copy_memory_to_forkee (HANDLE child, void *low, void *high, int)=0A= {=0A= DWORD done;=0A= int res;=0A= =0A= debug_printf ("fork copy: child handle %d, low %p, high %p\n",=0A= child, low, high);=0A= =0A= int lump =3D 1024*64;=0A= for (char *p =3D (char *) low; p < (char *) high; p +=3D lump)=0A= {=0A= int todo =3D MIN ((char *)high - p, lump);=0A= =0A= res =3D WriteProcessMemory (child, p, p, todo, &done);=0A= if (!res || todo !=3D done)=0A= {=0A= if (!res)=0A= __seterrno ();=0A= /* This happens when it shouldn't so there's a bug in our fork=0A= implementation somewhere. Print a message on the console to=0A= call people's attention to the failure until we get it=0A= straightened out. */=0A= small_printf ("fork copy: failed, %p..%p, res is %d, done is = %d\n",=0A= low, high, res, done);=0A= /* Call debug_printf as well to make sure message gets in log=0A= file if there is one. */=0A= debug_printf ("fork copy: failed\n");=0A= return 0;=0A= }=0A= }=0A= =0A= #if 0=0A= print_checksum (idx, low, high);=0A= #endif=0A= debug_printf ("fork copy: done\n");=0A= return 1;=0A= }=0A= =0A= int copy_mmap_records_to_forkee(pinfo *child);=0A= int recreate_mmaps_after_fork(void *);=0A= int copy_copyonwrite_mmaps_to_forkee(HANDLE hChild);=0A= =0A= /* Main guts of fork implementation.=0A= The result is the standard result of fork. */=0A= =0A= static int=0A= cygwin_fork_helper1 (void *proc_data_start,=0A= void *proc_data_end,=0A= void *proc_bss_start,=0A= void *proc_bss_end)=0A= {=0A= int res;=0A= int x, rc;=0A= hinfo *child_hinfo =3D 0;=0A= =0A= if (u->self->split_heap_p)=0A= {=0A= small_printf ("The heap has been split, CYGWIN can't fork this = process.\n");=0A= small_printf ("Increase the heap_chunk_size in the registry and = try again.\n");=0A= set_errno (ENOMEM);=0A= syscall_printf ("-1 =3D fork (), split heap\n");=0A= return -1;=0A= }=0A= =0A= /* Don't start the fork until we have the lock. */=0A= rc =3D lock_pinfo_for_update(FORK_WAIT_TIMEOUT);=0A= switch (rc)=0A= {=0A= case WAIT_ERROR_RC:=0A= small_printf ("fork parent: WaitForSingleObject (mutex) failed, = win32 error %d\n",=0A= GetLastError ());=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork (), wait failed\n");=0A= return -1;=0A= case WAIT_TIMEOUT:=0A= small_printf ("fork parent: WaitForSingleObject (mutex) timed = out\n");=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork (), wait timed out\n");=0A= return -1;=0A= default:=0A= debug_printf ("fork_helper: %d =3D WaitForSingleObject (...)\n", = rc);=0A= break;=0A= }=0A= =0A= pinfo *child =3D s->p.allocate_pid ();=0A= =0A= if (!child)=0A= {=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork (), process table full\n");=0A= unlock_pinfo();=0A= return -1;=0A= }=0A= =0A= /*=0A= * We need to save this allocated pointer as the child=0A= * will be changing it, plus we need to delete [] it later.=0A= */=0A= child_hinfo =3D child->hmap.vec;=0A= =0A= /* This will help some of the confusion. */=0A= fflush (stdout);=0A= =0A= debug_printf ("fork_helper: parent pid is %d, child pid is %d\n",=0A= u->self->get_pid (),=0A= child->get_pid ());=0A= =0A= /* Initialize things that are done later in dll_crt0_1 that aren't = done=0A= for the forkee. */=0A= child->reent_save =3D reent_data;=0A= child->progname =3D u->self->progname;=0A= =0A= /* Copy all the handles we need in the child. */=0A= u->self->hmap.dup_for_fork (&child->hmap);=0A= =0A= PROCESS_INFORMATION pi =3D {0};=0A= =0A= STARTUPINFO si =3D {0};=0A= si.cb =3D sizeof (STARTUPINFO);=0A= =0A= int c_flags =3D GetPriorityClass (GetCurrentProcess ()) | = CREATE_SUSPENDED; =0A= =0A= syscall_printf ("CreateProcessA (%s, %s,0,0,1,%x, 0,0,%p,%p)\n",=0A= u->self->progname, u->self->progname, c_flags, &si, &pi);=0A= =0A= rc =3D CreateProcessA (u->self->progname, /* image to run */=0A= u->self->progname, /* what we send in arg0 */=0A= 0, /* process security attrs */=0A= 0, /* thread security attrs */=0A= TRUE, /* inherit handles from parent */=0A= c_flags,=0A= 0, /* use parent's environment */=0A= 0, /* use current drive/directory */=0A= &si,=0A= &pi);=0A= =0A= if (!rc)=0A= {=0A= __seterrno ();=0A= syscall_printf ("-1 =3D fork(), CreateProcessA failed\n");=0A= child->inuse_p =3D PID_NOT_IN_USE;=0A= delete [] child_hinfo;=0A= unlock_pinfo();=0A= return -1;=0A= }=0A= =0A= ResetEvent (forker_stopped);=0A= ResetEvent (forkee_stopped);=0A= =0A= debug_printf ("fork_helper: about to call setjmp\n");=0A= x =3D setjmp (u->self->restore);=0A= debug_printf ("fork_helper: setjmp returned %d\n", x);=0A= =0A= #if 0=0A= if (0)=0A= {=0A= int k;=0A= unsigned char *p =3D (unsigned char *)(u->restore[32/4]);=0A= for (k =3D 0; k < 11; k++)=0A= small_printf ("set reg %d %x\n", k *4 , u->restore[k]);=0A= for (k =3D 0; k < 11; k++)=0A= small_printf ("[%02x]", p[k]);=0A= small_printf ("the res was %d\n", x);=0A= }=0A= #endif=0A= =0A= if (x =3D=3D 0)=0A= {=0A= /* Parent. */=0A= =0A= dump_jmp_buf (u->self->restore);=0A= =0A= #if 0 /* for debugging */=0A= s->base[0][0] =3D proc_data_start;=0A= s->base[0][1] =3D proc_data_end;=0A= s->base[1][0] =3D proc_bss_start;=0A= s->base[1][1] =3D proc_bss_end;=0A= s->base[2][0] =3D u->base;=0A= s->base[2][1] =3D u->ptr;=0A= s->base[3][0] =3D &x;=0A= s->base[3][1] =3D u->initial_sp; =0A= #endif =0A= =0A= /* Tell the child it's being forked and its pid.=0A= Remember, *u gets copied to the child's address space. */=0A= u->forkee =3D child->get_pid ();=0A= =0A= /* Fill in fields in the child's process table entry. */=0A= child->ppid =3D u->self->get_pid ();=0A= child->hThread =3D pi.hThread; =0A= child->hProcess =3D pi.hProcess;=0A= child->dwProcessId =3D pi.dwProcessId;=0A= child->uid =3D u->self->uid;=0A= child->gid =3D u->self->gid;=0A= child->sigs =3D u->self->sigs;=0A= child->sig_mask =3D u->self->sig_mask;=0A= if (copy_mmap_records_to_forkee(child))=0A= {=0A= small_printf ("fork_helper: copy of mmap_records failed\n");=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork(), copy of mmap_records failed\n");=0A= TerminateProcess (child->hProcess, 1);=0A= u->forkee =3D 0;=0A= goto cleanup;=0A= }=0A= =0A= /* Initialize the child's .data and .bss. */=0A= rc =3D copy_memory_to_forkee (pi.hProcess, (char = *)proc_data_start, (char *)proc_data_end, 0);=0A= if (rc)=0A= rc =3D copy_memory_to_forkee (pi.hProcess, (char *)proc_bss_start, = (char *)proc_bss_end, 1);=0A= if (! rc)=0A= {=0A= small_printf ("fork_helper: copy of data/bss failed\n");=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork(), data/bss copy failed\n");=0A= TerminateProcess (child->hProcess, 1);=0A= u->forkee =3D 0;=0A= goto cleanup;=0A= }=0A= =0A= u->forkee =3D 0;=0A= =0A= /* Need to record that task was started by cygwin32 task.=0A= This info will then used by the exit() handling code to know = whether=0A= to reset inuse_p. Normally inuse_p isn't reset until wait() is = called=0A= but if the task wasn't started by a cygwin32 task, wait() will = never=0A= be called and the process table will fill up. We remove this=0A= flag if the parent exits. */=0A= =0A= child->inuse_p |=3D PID_TO_BE_WAITED_FOR;=0A= =0A= /* FIXME: Exit handling code also needs to record that the task = ended=0A= so the ps command will know about zombies. */=0A= =0A= /* Start thread, and wait for it to initialize itself. */=0A= rc =3D ResumeThread (child->hThread);=0A= if (rc !=3D 1)=0A= {=0A= /* Can't resume the thread. Not sure why this would happen = unless=0A= there's a bug in the system. Things seem to be working OK now=0A= though, so flag this with EAGAIN, but print a message on the=0A= console. */=0A= small_printf ("fork_helper: ResumeThread failed, rc =3D %d\n", = rc);=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork(), ResumeThread failed\n");=0A= TerminateProcess (child->hProcess, 1);=0A= goto cleanup;=0A= }=0A= debug_printf ("fork_helper: child started\n");=0A= =0A= /* We don't want to wait forever here. If there's a problem = somewhere=0A= it'll hang the entire system (since all forks are mutex'd). If we=0A= time out, set errno =3D EAGAIN and hope the app tries again. */=0A= =0A= /* We also add child->hProcess to the wait. If the child fails=0A= to initialize (eg. because of a missing dll). Then this=0A= handle will become signalled. This stops a *looong* timeout = wait.=0A= */=0A= HANDLE wait_array[2];=0A= wait_array[0] =3D child->hProcess;=0A= wait_array[1] =3D forkee_stopped;=0A= rc =3D WaitForMultipleObjects (2, wait_array, FALSE, = FORK_WAIT_TIMEOUT);=0A= if (rc =3D=3D WAIT_ERROR_RC || rc =3D=3D WAIT_TIMEOUT)=0A= {=0A= if (rc =3D=3D WAIT_ERROR_RC)=0A= small_printf ("fork_helper: WaitForMultipleObjects failed, win32 = error %d\n",=0A= GetLastError ());=0A= else=0A= small_printf ("fork_helper: WaitForMultipleObjects timed = out\n");=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork(), WaitForMultipleObjects = failed\n");=0A= TerminateProcess (child->hProcess, 1);=0A= goto cleanup;=0A= }=0A= else if(rc =3D=3D WAIT_OBJECT_0)=0A= {=0A= /* Child died. Clean up and exit. */=0A= DWORD errcode;=0A= GetExitCodeProcess(child->hProcess, &errcode);=0A= small_printf ("fork_helper: child died before initialization with = win32 error %d\n", errcode);=0A= set_errno (EAGAIN);=0A= syscall_printf ("fork_helper: Child died before = forkee_stopped signalled\n");=0A= goto cleanup;=0A= }=0A= SuspendThread (child->hThread);=0A= =0A= /* Now fill in the stack and heap - this has to be done after =0A= the child is started. */=0A= rc =3D copy_memory_to_forkee (child->hProcess, u->base, u->ptr, = 2);=0A= if (rc)=0A= rc =3D copy_memory_to_forkee (child->hProcess, &x, u->initial_sp, = 3);=0A= if (! rc)=0A= {=0A= small_printf ("fork_helper: copy of stack/heap failed\n");=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork(), stack/heap copy failed\n");=0A= TerminateProcess (child->hProcess, 1);=0A= goto cleanup;=0A= }=0A= =0A= if(copy_copyonwrite_mmaps_to_forkee(child->hProcess))=0A= {=0A= small_printf("fork_helper: copy_copyonwrite_mmaps_to_forkee = failed\n");=0A= set_errno (EAGAIN);=0A= syscall_printf ("-1 =3D fork(), copy of copyonwrite memory = failed\n");=0A= TerminateProcess (child->hProcess, 1);=0A= goto cleanup;=0A= }=0A= =0A= /*=0A= * Now we have started the child we can get rid of the=0A= * childs fd table from our address space.=0A= */=0A= delete [] child_hinfo;=0A= /* Start the child up again. */=0A= SetEvent (forker_stopped);=0A= ResumeThread (child->hThread);=0A= =0A= res =3D child->get_pid ();=0A= =0A= unlock_pinfo();=0A= }=0A= else=0A= {=0A= /* We arrive here via a longjmp from "crt0". */=0A= debug_printf ("fork_helper: child is running\n");=0A= =0A= u->self =3D s->p[x];=0A= reent_data =3D u->self->reent_save;=0A= debug_printf ("fork child: self %p, pid %d, ppid %d\n",=0A= u->self, x, u->self->ppid);=0A= =0A= if(recreate_mmaps_after_fork(u->self->mmap_ptr))=0A= {=0A= small_printf("fork child: = recreate_mmaps_after_fork_failed\n");=0A= ExitProcess (1);=0A= }=0A= =0A= /* Tell our parent we've started. */=0A= SetEvent (forkee_stopped);=0A= =0A= /* Wait for the parent to fill in our stack and heap.=0A= Don't wait forever here. If our parent dies we don't want to clog=0A= the system. If the wait fails, we really can't continue so exit. = */=0A= int rc =3D WaitForSingleObject (forker_stopped, = FORK_WAIT_TIMEOUT);=0A= switch (rc)=0A= {=0A= case WAIT_ERROR_RC:=0A= small_printf ("fork child: WaitForSingleObject failed, win32 error = %d\n",=0A= GetLastError ());=0A= ExitProcess (1);=0A= case WAIT_TIMEOUT:=0A= small_printf ("fork child: WaitForSingleObject timed out\n");=0A= ExitProcess (1);=0A= default:=0A= break;=0A= }=0A= =0A= /* Ensure winsock is enabled for the child. */=0A= socket_checkinit();=0A= #if 0=0A= print_checksum (4, s->base[0][0], s->base[0][1]);=0A= print_checksum (5, s->base[1][0], s->base[1][1]);=0A= print_checksum (6, s->base[2][0], s->base[2][1]);=0A= print_checksum (7, s->base[3][0], s->base[3][1]);=0A= #endif=0A= =0A= res =3D 0;=0A= }=0A= =0A= syscall_printf ("%d =3D fork()\n", res);=0A= return res;=0A= =0A= /* Common cleanup code for failure cases */=0A= cleanup:=0A= /* Remember to de-allocate the fd table. */=0A= delete [] child_hinfo;=0A= child->inuse_p =3D PID_NOT_IN_USE;=0A= unlock_pinfo();=0A= CloseHandle(child->hProcess);=0A= CloseHandle(child->hThread);=0A= return -1;=0A= }=0A= =0A= /* This hack uses setjmp/longjmp to ensure that the parent's=0A= registers are all available in the child. We could use=0A= GetThreadContext/SetThreadContext instead, but I'm sure this=0A= is much faster. */=0A= =0A= static int=0A= __fork ()=0A= {=0A= jmp_buf b;=0A= int r;=0A= =0A= if ((r =3D setjmp (b)) !=3D 0)=0A= {=0A= r =3D r =3D=3D -2 ? -1 : r =3D=3D -1 ? 0 : r;=0A= return r;=0A= }=0A= =0A= r =3D cygwin_fork_helper1 (u->data_start,=0A= u->data_end,=0A= u->bss_start,=0A= u->bss_end);=0A= =0A= /* Must convert result to get it through setjmp ok. */=0A= longjmp (b, r =3D=3D -1 ? -2 : r =3D=3D 0 ? -1 : r);=0A= }=0A= =0A= /* Utility to dump a setjmp buf. */=0A= =0A= void=0A= dump_jmp_buf (jmp_buf buf)=0A= {=0A= #ifdef __i386__=0A= debug_printf ("jmp_buf: eax 0x%x, ebx 0x%x, ecx 0x%x, edx 0x%x\n",=0A= buf[0], buf[1], buf[2], buf[3]);=0A= debug_printf ("jmp_buf: esi 0x%x, edi 0x%x, ebp 0x%x, esp 0x%x\n",=0A= buf[4], buf[5], buf[6], buf[7]);=0A= short *s =3D (short *) &buf[9];=0A= debug_printf ("jmp_buf: es 0x%x, fs 0x%x, gs 0x%x, ss 0x%x\n",=0A= s[0], s[1], s[2], s[3]);=0A= debug_printf ("jmp_buf: eip: 0x%x\n", buf[8]);=0A= #endif=0A= }=0A= =0A= extern "C"=0A= int=0A= vfork () =0A= {=0A= return fork ();=0A= }=0A= =0A= extern "C"=0A= int=0A= fork ()=0A= {=0A= /* FIXME: Was there a real reason for this? */=0A= alloca (100);=0A= =0A= int r =3D __fork ();=0A= return r;=0A= }=0A= ------ =_NextPart_000_01BCB240.03307650 Content-Type: application/octet-stream; name="mmap.cc" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="mmap.cc" /* mmap.cc=0A= =0A= Copyright 1996, 1997 Cygnus Solutions=0A= =0A= This program is free software; you can redistribute it and/or modify=0A= it under the terms of the GNU General Public License as published by=0A= the Free Software Foundation; either version 2 of the License, or=0A= (at your option) any later version.=0A= =0A= This program is distributed in the hope that it will be useful,=0A= but WITHOUT ANY WARRANTY; without even the implied warranty of=0A= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the=0A= GNU General Public License for more details.=0A= =0A= You should have received a copy of the GNU General Public License=0A= along with this program; if not, write to the Free Software=0A= Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, = USA. */=0A= =0A= #include =0A= #include =0A= #include =0A= #include =0A= #include =0A= /* STD defines/includes */=0A= #define times __hide_times=0A= #define __THROW_BAD_ALLOC return 0=0A= #include =0A= #include =0A= #undef times=0A= =0A= #include "winsup.h"=0A= #include "fhandler.h"=0A= =0A= /* STL includes */=0A= /*=0A= * Simple class used to keep a record of all current=0A= * mmap areas in a process. Needed so that=0A= * they can be duplicated after a fork().=0A= */=0A= =0A= class mmap_record {=0A= =0A= private:=0A= =0A= HANDLE mapping_handle_;=0A= DWORD access_mode_;=0A= DWORD offset_;=0A= DWORD size_to_map_;=0A= void *base_address_;=0A= =0A= public:=0A= =0A= mmap_record(HANDLE h, DWORD ac, DWORD o, DWORD s, void *b)=0A= : mapping_handle_(h), access_mode_(ac), offset_(o),=0A= size_to_map_(s), base_address_(b) { ; }=0A= =0A= /* Default Copy constructor/operator=3D/destructor are ok */=0A= =0A= /* Simple accessors */=0A= HANDLE get_handle() const { return mapping_handle_; }=0A= DWORD get_access() const { return access_mode_; }=0A= DWORD get_offset() const { return offset_; }=0A= DWORD get_size() const { return size_to_map_; }=0A= void *get_address() const { return base_address_; }=0A= };=0A= =0A= /*=0A= * Code to keep a record of all mmap'ed area's in a process.=0A= * Needed to duplicate tham in a child of fork().=0A= * mmap_record classes are kept in an STL list in an STL map, keyed=0A= * by file descriptor. This is *NOT* duplicated accross a fork(), it=0A= * needs to be specially handled by the fork code.=0A= */=0A= =0A= static map< int, list * > *mmapped_areas;=0A= =0A= extern "C" {=0A= =0A= caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, = off_t off)=0A= {=0A= syscall_printf ("mmap: addr =3D %x, len =3D %d, prot =3D %x, flags = =3D %x, fd =3D %d, off =3D %d\n",=0A= addr, len, prot, flags, fd, off);=0A= =0A= BOOL is_win95 =3D windows_95();=0A= =0A= /* No fixed addresses on windows 95 - loser OS */=0A= if(is_win95 && (flags & MAP_FIXED)) {=0A= set_errno(EINVAL);=0A= syscall_printf ("-1 =3D mmap(): win95 and MAP_FIXED\n");=0A= return (caddr_t)-1;=0A= }=0A= =0A= if(mmapped_areas =3D=3D 0) {=0A= /* First mmap call, create STL map */=0A= mmapped_areas =3D new map< int, list * >;=0A= if(mmapped_areas =3D=3D 0) {=0A= set_errno(ENOMEM);=0A= syscall_printf ("-1 =3D mmap(): ENOMEM\n");=0A= return (caddr_t)-1;=0A= }=0A= }=0A= =0A= DWORD access =3D (prot & PROT_WRITE) ? FILE_MAP_WRITE : = FILE_MAP_READ;=0A= if (flags & MAP_PRIVATE) access =3D FILE_MAP_COPY;=0A= DWORD protect;=0A= =0A= if( access & FILE_MAP_COPY )=0A= protect =3D PAGE_WRITECOPY;=0A= else if( access & FILE_MAP_WRITE)=0A= protect =3D PAGE_READWRITE;=0A= else=0A= protect =3D PAGE_READONLY;=0A= =0A= SECURITY_ATTRIBUTES sa;=0A= sa.nLength =3D sizeof (SECURITY_ATTRIBUTES);=0A= sa.bInheritHandle =3D TRUE;=0A= sa.lpSecurityDescriptor =3D 0;=0A= =0A= HANDLE hFile;=0A= if(fd =3D=3D -1)=0A= hFile =3D (HANDLE)0xFFFFFFFF;=0A= else {=0A= /* Ensure that fd is open */=0A= if(NOT_OPEN_FD (fd)) {=0A= set_errno(EBADF);=0A= syscall_printf ("-1 =3D mmap(): EBADF\n");=0A= return (caddr_t)-1;=0A= }=0A= hinfo_vec *hvec =3D &u->self->hmap;=0A= hFile =3D (*hvec)[fd].h->get_handle();=0A= }=0A= HANDLE h =3D CreateFileMapping( hFile, &sa, protect, 0, len, = NULL);=0A= if(h =3D=3D 0) {=0A= __seterrno();=0A= syscall_printf ("-1 =3D mmap(): CreateFileMapping failed with = %d\n", GetLastError());=0A= return (caddr_t)-1;=0A= }=0A= =0A= void *base;=0A= =0A= if(flags & MAP_FIXED) {=0A= base =3D MapViewOfFileEx( h, access, 0, off, len, addr);=0A= if(base !=3D addr) {=0A= __seterrno();=0A= syscall_printf ("-1 =3D mmap(): MapViewOfFileEx failed with = %d\n", GetLastError());=0A= CloseHandle(h);=0A= return (caddr_t)-1;=0A= }=0A= } else {=0A= base =3D MapViewOfFile( h, access, 0, off, len);=0A= if(base =3D=3D 0) {=0A= __seterrno();=0A= syscall_printf ("-1 =3D mmap(): MapViewOfFile failed with %d\n", = GetLastError());=0A= CloseHandle(h);=0A= return (caddr_t)-1;=0A= }=0A= }=0A= =0A= /* Ok, so we have a successfully mmap'ed area.=0A= Now save it so that a fork'ed child can reproduce it.=0A= */=0A= =0A= mmap_record mmap_rec(h, access, off, len, base);=0A= =0A= /* Get the list of mmapped areas for this fd, create a new=0A= one if one doesn't exist yet. =0A= */=0A= =0A= list *l =3D (*mmapped_areas)[fd];=0A= if(l =3D=3D 0) {=0A= /* Create a new one */=0A= l =3D new list;=0A= if(l =3D=3D 0) {=0A= UnmapViewOfFile(base);=0A= CloseHandle(h);=0A= set_errno(ENOMEM);=0A= syscall_printf ("-1 =3D mmap(): ENOMEM\n");=0A= return (caddr_t)-1;=0A= }=0A= (*mmapped_areas)[fd] =3D l;=0A= }=0A= =0A= /* Insert into the list */=0A= l->push_front(mmap_rec);=0A= =0A= syscall_printf ("%x =3D mmap() succeeded\n", base);=0A= return (caddr_t)base;=0A= }=0A= =0A= /*=0A= * Call to remove an mmap'ed area. Insists that base requested=0A= * is the same as that mmap'ed, error if not.=0A= */=0A= =0A= int munmap( caddr_t addr, size_t len)=0A= {=0A= syscall_printf ("munmap(addr =3D %x, len =3D %d)\n", addr, len);=0A= =0A= /*=0A= * Check if a mmap'ed area was ever created.=0A= */=0A= if(mmapped_areas =3D=3D 0) {=0A= syscall_printf ("-1 =3D munmap(): mmapped_areas =3D=3D 0\n");=0A= set_errno(EINVAL);=0A= return -1;=0A= }=0A= =0A= /*=0A= * Iterate through the map, looking for the mmap'ed area.=0A= * Error if not found.=0A= */=0A= =0A= map< int, list * >::iterator it;=0A= =0A= for( it =3D mmapped_areas->begin(); it !=3D mmapped_areas->end(); = ++it)=0A= {=0A= list *l =3D (*it).second;=0A= if(l !=3D 0)=0A= {=0A= list::iterator li;=0A= for( li =3D l->begin(); li !=3D l->end(); ++li)=0A= {=0A= mmap_record rec =3D *li;=0A= if(rec.get_address() =3D=3D addr)=0A= {=0A= /* Unmap the area */=0A= UnmapViewOfFile(addr);=0A= CloseHandle(rec.get_handle());=0A= /* Delete the entry. */=0A= l->erase(li);=0A= syscall_printf ("0 =3D munmap(): %x\n", addr);=0A= return 0;=0A= }=0A= }=0A= }=0A= }=0A= set_errno(EINVAL);=0A= =0A= syscall_printf ("-1 =3D munmap(): EINVAL\n");=0A= =0A= return -1;=0A= } =0A= =0A= /*=0A= * Sync file with memory. Ignore flags for now.=0A= */=0A= =0A= int msync( caddr_t addr, size_t len, int flags)=0A= {=0A= syscall_printf("msync: addr =3D %x, len =3D %d, flags =3D %x\n",=0A= addr, len, flags);=0A= =0A= if(FlushViewOfFile(addr, len) =3D=3D 0)=0A= {=0A= syscall_printf("-1 =3D msync: LastError =3D %x\n", = GetLastError());=0A= __seterrno();=0A= return -1;=0A= }=0A= syscall_printf("0 =3D msync\n");=0A= return 0;=0A= }=0A= =0A= /*=0A= * Set memory protection.=0A= */=0A= =0A= int mprotect(caddr_t addr, size_t len, int prot)=0A= {=0A= DWORD old_prot;=0A= DWORD new_prot =3D 0;=0A= =0A= syscall_printf("mprotect(addr =3D %x, len =3D %d, prot =3D %x)\n",=0A= addr, len, prot);=0A= =0A= if(prot & PROT_NONE)=0A= new_prot =3D PAGE_NOACCESS;=0A= else =0A= {=0A= switch(prot)=0A= {=0A= case PROT_READ|PROT_WRITE|PROT_EXEC:=0A= new_prot =3D PAGE_EXECUTE_READWRITE;=0A= break;=0A= case PROT_READ|PROT_WRITE:=0A= new_prot =3D PAGE_READWRITE;=0A= break;=0A= case PROT_READ|PROT_EXEC:=0A= new_prot =3D PAGE_EXECUTE_READ;=0A= break;=0A= case PROT_READ:=0A= new_prot =3D PAGE_READONLY;=0A= break;=0A= default:=0A= syscall_printf("-1 =3D mprotect(): invalid prot = value\n");=0A= set_errno(EINVAL);=0A= return -1;=0A= }=0A= }=0A= if(VirtualProtect(addr, len, prot, &old_prot)=3D=3D 0)=0A= {=0A= __seterrno();=0A= syscall_printf("-1 =3D mprotect(): lasterror =3D %x\n", = GetLastError());=0A= return -1;=0A= }=0A= syscall_printf("0 =3D mprotect()\n");=0A= return 0;=0A= }=0A= =0A= };=0A= =0A= =0A= /*=0A= * Called by the parent process durring a fork. At this point, the = child has=0A= * not yet run. When it does run and initialize, we want it to = re-establish=0A= * the same mmap regions that we have. Because our heap, where our=0A= * mmap_records are stored, will not have been copied to the child at = the time=0A= * it initializes, we can't simply pass it the "mmapped_areas" = pointer.=0A= * Instead, we must copy the contents of our "mmapped_areas" into an = array of=0A= * mmap_records where the child process can read them. The = "child->mmap_ptr"=0A= * is set to a handle for the memory where the the child will find the = array.=0A= * After we return, we will suspend and the child will initialize and = call=0A= * "recreate_mmaps_after_fork" below.=0A= */=0A= =0A= int copy_mmap_records_to_forkee(pinfo *child)=0A= {=0A= /*=0A= * Count the number of mmap_record entries are in the "mmapped_areas" = map.=0A= */=0A= unsigned count =3D 0;=0A= map< int, list * >::iterator it;=0A= if (mmapped_areas !=3D NULL)=0A= {=0A= for( it =3D mmapped_areas->begin(); it !=3D mmapped_areas->end(); = ++it)=0A= {=0A= list *l =3D (*it).second;=0A= if(l !=3D 0)=0A= {=0A= list::iterator li;=0A= for( li =3D l->begin(); li !=3D l->end(); ++li)=0A= count++;=0A= }=0A= }=0A= }=0A= =0A= /*=0A= * Return with "child->mmap_ptr =3D=3D NULL"; there are no mmap = regions.=0A= */=0A= if (count =3D=3D 0)=0A= {=0A= child->mmap_ptr =3D NULL;=0A= debug_printf("copy_mmap_records_to_forkee: succeeded\n");=0A= return 0;=0A= }=0A= =0A= /*=0A= * Create a file mapping that will be made available to the child = process=0A= * and will hold an array of all the mmap_records in = "mmapped_areas".=0A= */=0A= HANDLE h =3D CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, = 0,=0A= (count+1)*sizeof(mmap_record), NULL);=0A= if (h =3D=3D NULL)=0A= {=0A= syscall_printf("copy_mmap_records_to_forkee: CreateFileMapping = failed\n");=0A= return -1;=0A= }=0A= =0A= mmap_record* pRec =3D (mmap_record*)MapViewOfFile(h, FILE_MAP_WRITE, = 0, 0, 0);=0A= if (pRec =3D=3D NULL)=0A= {=0A= CloseHandle(h);=0A= syscall_printf("copy_mmap_records_to_forkee: MapViewOfFile = failed\n");=0A= return -1;=0A= }=0A= =0A= /*=0A= * Now copy all the mmap_records into the mapped memory array.=0A= * The count of elements in this array are in the first record=0A= * along with some other useful info.=0A= */=0A= *pRec =3D mmap_record(0, 0, (DWORD)mmapped_areas, count, pRec);=0A= if (mmapped_areas !=3D NULL)=0A= {=0A= for( it =3D mmapped_areas->begin(); it !=3D mmapped_areas->end(); = ++it)=0A= {=0A= list *l =3D (*it).second;=0A= if(l !=3D 0)=0A= {=0A= list::iterator li;=0A= for( li =3D l->begin(); li !=3D l->end(); ++li)=0A= *(++pRec) =3D *li;=0A= }=0A= }=0A= }=0A= =0A= /*=0A= * Duplicate the handle for use by the child process. The child = process=0A= * will use the duplicate handle in recreate_mmaps_after_fork.=0A= * We also unmap our view and close our handle, since we're done with = it.=0A= */=0A= UnmapViewOfFile(pRec - count);=0A= DuplicateHandle(GetCurrentProcess(), h, child->hProcess,=0A= (LPHANDLE)&child->mmap_ptr, FILE_MAP_READ,=0A= FALSE, DUPLICATE_CLOSE_SOURCE);=0A= =0A= debug_printf("copy_mmap_records_to_forkee: succeeded\n");=0A= return 0;=0A= }=0A= =0A= =0A= /*=0A= * Call to re-create all the file mappings in a fork()'ed child. = Called from=0A= * "cygwin_fork_helper1" by the child durring initialization. At this = point,=0A= * the parent has duplicated its .data and .bss, but not its stack or = heap.=0A= * We are passed a handle to memory via "u->self->mmap_ptr" where an = array of=0A= * mmap_records can be found. This was put there by our parent in its = call=0A= * to "copy_mmap_records_to_forkee". The first record in the array = contains=0A= * information like the count of records. All the HANDLE's in those = records=0A= * are valid for us (we inherited them), but none of the mapped = areas=0A= * are in our address space. We need to iterate through the array and = do the=0A= * MapViewOfFileEx calls. Initially, the "mmapped_areas" pointer is = assumed to=0A= * be NULL, since this process was just created. Before returning, = both=0A= * "mmapped_areas" and "u->self->mmap_ptr" are set to point to the mmap = data=0A= * structure in the heap. This pointer is also passed to us in the = first=0A= * mmap_record. Remember, that pointer refers to heap memory which the = parent=0A= * process hasn't yet copied to us. After we return, we will suspend = while we=0A= * wait for our parent to copy our heap (so that "mmaped_areas" is = valid).=0A= * All of our mmap regions, except the ones that the parent created = with=0A= * FILE_MAP_COPY, will refer to the same physical memory that the = cooresponding=0A= * region in our parent refers to. In the case of FILE_MAP_COPY = regions, we=0A= * will see the contents of the original file at the time the parent = mmapped it.=0A= * Any changes made by the parent to these regions will not be seen by = us. So,=0A= * the parent will also need to call "copy_copyonwrite_mmaps_to_forkee" = below so=0A= * that we are in sync with the parent immediately after the fork.=0A= */=0A= =0A= int recreate_mmaps_after_fork(void *param)=0A= {=0A= if (param !=3D NULL)=0A= {=0A= =0A= HANDLE h =3D (HANDLE)param;=0A= mmap_record* pRec =3D (mmap_record*)MapViewOfFile(h, FILE_MAP_READ, = 0, 0,=0A= sizeof(mmap_record));=0A= if (pRec =3D=3D NULL)=0A= {=0A= small_printf("-1 =3D recreate_mmaps_after_fork(): MapViewOfFile = failed "=0A= "with GetLastError =3D %x\n", GetLastError());=0A= return -1;=0A= }=0A= =0A= /*=0A= * Copy the information that was put in the first record=0A= * (see set_child_mmap_ptr).=0A= */=0A= void* pRecDesired =3D pRec->get_address();=0A= unsigned count =3D pRec->get_size();=0A= mmapped_areas =3D (map< int, list * > = *)pRec->get_offset();=0A= =0A= /*=0A= * We need to have the array of mmap_records at the address = "pRecDesired".=0A= * Otherwise, this array may be overlapping one of the mapped = regions=0A= * in the parent that we are trying to duplicate. It is unlikely = that=0A= * we are so lucky as to have "pRec" mapped correctly the first time, = so=0A= * unmap this view and re-map it using MapViewOfFileEx.=0A= */=0A= UnmapViewOfFile(pRec);=0A= =0A= if (count > 0)=0A= {=0A= pRec =3D (mmap_record*)MapViewOfFileEx(h, FILE_MAP_READ, 0, 0, 0, = pRecDesired);=0A= if (pRec =3D=3D NULL)=0A= {=0A= small_printf("-1 =3D recreate_mmaps_after_fork(): MapViewOfFileEx = failed "=0A= "with GetLastError =3D %x\n", GetLastError());=0A= return -1;=0A= }=0A= =0A= if (pRec !=3D pRecDesired)=0A= {=0A= small_printf("-1 =3D recreate_mmaps_after_fork(): MapViewOfFileEx = wrong address\n");=0A= return -1;=0A= }=0A= =0A= /*=0A= * Copy the "pRec" mmap_record array into the=0A= * "mmaped_areas" map of mmap_record lists.=0A= */=0A= while (count-- > 0)=0A= {=0A= ++pRec;=0A= =0A= /* Now re-create the MapViewOfFileEx call */=0A= void* base =3D MapViewOfFileEx(pRec->get_handle(), = pRec->get_access(),=0A= 0, pRec->get_offset(),=0A= pRec->get_size(), = pRec->get_address());=0A= if(base !=3D pRec->get_address())=0A= {=0A= small_printf("recreate_mmaps_after_fork: base address %x\=0A= fails to match requested address %x\n", base, = pRec->get_address());=0A= return -1;=0A= }=0A= =0A= debug_printf("recreate_mmaps_after_fork: h =3D %x, access =3D %x, = offset =3D %d, \=0A= size =3D %d, address =3D %x\n", pRec->get_handle(), = pRec->get_access(),=0A= pRec->get_offset(), pRec->get_size(), = pRec->get_address());=0A= }=0A= =0A= UnmapViewOfFile(pRecDesired);=0A= }=0A= CloseHandle(h);=0A= =0A= }=0A= =0A= /* Now set our mmap record in case the child forks. */=0A= u->self->mmap_ptr =3D mmapped_areas;=0A= =0A= debug_printf("recreate_mmaps_after_fork: succeeded\n");=0A= return 0;=0A= }=0A= =0A= =0A= /*=0A= * Called by the parent process durring a fork. At this point,=0A= * the address space of the child process has been established;=0A= * that is, the child has already called = "recreate_mmaps_after_fork".=0A= * Any mmap region created with "FILE_MAP_COPY" will need to be=0A= * copied to the cooresponding memory addresses in the child = process,=0A= * or else the parent and child may not see the same contents at = those=0A= * addresses. They will be different for those FILE_MAP_COPY = regions=0A= * that the parent has written to (unless the parent wrote what was=0A= * there originally).=0A= */=0A= =0A= extern int copy_memory_to_forkee (HANDLE child, void *low, void *high, = int);=0A= =0A= int copy_copyonwrite_mmaps_to_forkee(HANDLE hChild)=0A= {=0A= /*=0A= * Iterate through the array if mmap_records and copy those = entries=0A= * that have FILE_MAP_COPY set.=0A= */=0A= map< int, list * >::iterator it;=0A= if (mmapped_areas !=3D NULL)=0A= {=0A= for( it =3D mmapped_areas->begin(); it !=3D mmapped_areas->end(); = ++it)=0A= {=0A= list *l =3D (*it).second;=0A= if(l !=3D 0)=0A= {=0A= list::iterator li;=0A= for( li =3D l->begin(); li !=3D l->end(); ++li)=0A= {=0A= mmap_record& rec =3D *li;=0A= char* start =3D (char*)rec.get_address() + = rec.get_offset();=0A= =0A= if ((rec.get_access() & FILE_MAP_COPY) &&=0A= !copy_memory_to_forkee(hChild, start, start + = rec.get_size(), 0))=0A= {=0A= syscall_printf("copy_copyonwrite_mmaps_to_forkee: = copy_memory_to_forkee failed\n");=0A= return -1;=0A= }=0A= =0A= debug_printf("copy_copyonwrite_mmaps_to_forkee: h =3D %x, "=0A= "access =3D %x, offset =3D %d, size =3D %d, address =3D = %x\n",=0A= rec.get_handle(), rec.get_access(), = rec.get_offset(),=0A= rec.get_size(), rec.get_address());=0A= }=0A= }=0A= }=0A= }=0A= =0A= debug_printf("copy_copyonwrite_mmaps_to_forkee: succeeded\n");=0A= return 0;=0A= }=0A= ------ =_NextPart_000_01BCB240.03307650-- - For help on using this list (especially unsubscribing), send a message to "gnu-win32-request AT cygnus DOT com" with one line of text: "help".