Mail Archives: cygwin/1997/08/30/23:06:18
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 <stdio.h>=0A=
#include <unistd.h>=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 <stdlib.h>=0A=
#include <stddef.h>=0A=
#include <sys/types.h>=0A=
#include <sys/mman.h>=0A=
#include <errno.h>=0A=
/* STD defines/includes */=0A=
#define times __hide_times=0A=
#define __THROW_BAD_ALLOC return 0=0A=
#include <list.h>=0A=
#include <map.h>=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<mmap_record> * > *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<mmap_record> * >;=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<mmap_record> *l =3D (*mmapped_areas)[fd];=0A=
if(l =3D=3D 0) {=0A=
/* Create a new one */=0A=
l =3D new list<mmap_record>;=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<mmap_record> * >::iterator it;=0A=
=0A=
for( it =3D mmapped_areas->begin(); it !=3D mmapped_areas->end(); =
++it)=0A=
{=0A=
list<mmap_record> *l =3D (*it).second;=0A=
if(l !=3D 0)=0A=
{=0A=
list<mmap_record>::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<mmap_record> * >::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<mmap_record> *l =3D (*it).second;=0A=
if(l !=3D 0)=0A=
{=0A=
list<mmap_record>::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<mmap_record> *l =3D (*it).second;=0A=
if(l !=3D 0)=0A=
{=0A=
list<mmap_record>::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<mmap_record> * > =
*)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<mmap_record> * >::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<mmap_record> *l =3D (*it).second;=0A=
if(l !=3D 0)=0A=
{=0A=
list<mmap_record>::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".
- Raw text -