/* mmap.cc Copyright 1996, 1997 Cygnus Solutions This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include /* STD defines/includes */ #define times __hide_times #define __THROW_BAD_ALLOC return 0 #include #include #undef times #include "winsup.h" #include "fhandler.h" /* STL includes */ /* * Simple class used to keep a record of all current * mmap areas in a process. Needed so that * they can be duplicated after a fork(). */ class mmap_record { private: HANDLE mapping_handle_; DWORD access_mode_; DWORD offset_; DWORD size_to_map_; void *base_address_; public: mmap_record(HANDLE h, DWORD ac, DWORD o, DWORD s, void *b) : mapping_handle_(h), access_mode_(ac), offset_(o), size_to_map_(s), base_address_(b) { ; } /* Default Copy constructor/operator=/destructor are ok */ /* Simple accessors */ HANDLE get_handle() const { return mapping_handle_; } DWORD get_access() const { return access_mode_; } DWORD get_offset() const { return offset_; } DWORD get_size() const { return size_to_map_; } void *get_address() const { return base_address_; } }; /* * Code to keep a record of all mmap'ed area's in a process. * Needed to duplicate tham in a child of fork(). * mmap_record classes are kept in an STL list in an STL map, keyed * by file descriptor. This is *NOT* duplicated accross a fork(), it * needs to be specially handled by the fork code. */ static map< int, list * > *mmapped_areas; extern "C" { caddr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) { syscall_printf ("mmap: addr = %x, len = %d, prot = %x, flags = %x, fd = %d, off = %d\n", addr, len, prot, flags, fd, off); BOOL is_win95 = windows_95(); /* No fixed addresses on windows 95 - loser OS */ if(is_win95 && (flags & MAP_FIXED)) { set_errno(EINVAL); syscall_printf ("-1 = mmap(): win95 and MAP_FIXED\n"); return (caddr_t)-1; } if(mmapped_areas == 0) { /* First mmap call, create STL map */ mmapped_areas = new map< int, list * >; if(mmapped_areas == 0) { set_errno(ENOMEM); syscall_printf ("-1 = mmap(): ENOMEM\n"); return (caddr_t)-1; } } DWORD access = (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ; if (flags & MAP_PRIVATE) access = FILE_MAP_COPY; DWORD protect; if( access & FILE_MAP_COPY ) protect = PAGE_WRITECOPY; else if( access & FILE_MAP_WRITE) protect = PAGE_READWRITE; else protect = PAGE_READONLY; SECURITY_ATTRIBUTES sa; sa.nLength = sizeof (SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = 0; HANDLE hFile; if(fd == -1) hFile = (HANDLE)0xFFFFFFFF; else { /* Ensure that fd is open */ if(NOT_OPEN_FD (fd)) { set_errno(EBADF); syscall_printf ("-1 = mmap(): EBADF\n"); return (caddr_t)-1; } hinfo_vec *hvec = &u->self->hmap; hFile = (*hvec)[fd].h->get_handle(); } HANDLE h = CreateFileMapping( hFile, &sa, protect, 0, len, NULL); if(h == 0) { __seterrno(); syscall_printf ("-1 = mmap(): CreateFileMapping failed with %d\n", GetLastError()); return (caddr_t)-1; } void *base; if(flags & MAP_FIXED) { base = MapViewOfFileEx( h, access, 0, off, len, addr); if(base != addr) { __seterrno(); syscall_printf ("-1 = mmap(): MapViewOfFileEx failed with %d\n", GetLastError()); CloseHandle(h); return (caddr_t)-1; } } else { base = MapViewOfFile( h, access, 0, off, len); if(base == 0) { __seterrno(); syscall_printf ("-1 = mmap(): MapViewOfFile failed with %d\n", GetLastError()); CloseHandle(h); return (caddr_t)-1; } } /* Ok, so we have a successfully mmap'ed area. Now save it so that a fork'ed child can reproduce it. */ mmap_record mmap_rec(h, access, off, len, base); /* Get the list of mmapped areas for this fd, create a new one if one doesn't exist yet. */ list *l = (*mmapped_areas)[fd]; if(l == 0) { /* Create a new one */ l = new list; if(l == 0) { UnmapViewOfFile(base); CloseHandle(h); set_errno(ENOMEM); syscall_printf ("-1 = mmap(): ENOMEM\n"); return (caddr_t)-1; } (*mmapped_areas)[fd] = l; } /* Insert into the list */ l->push_front(mmap_rec); syscall_printf ("%x = mmap() succeeded\n", base); return (caddr_t)base; } /* * Call to remove an mmap'ed area. Insists that base requested * is the same as that mmap'ed, error if not. */ int munmap( caddr_t addr, size_t len) { syscall_printf ("munmap(addr = %x, len = %d)\n", addr, len); /* * Check if a mmap'ed area was ever created. */ if(mmapped_areas == 0) { syscall_printf ("-1 = munmap(): mmapped_areas == 0\n"); set_errno(EINVAL); return -1; } /* * Iterate through the map, looking for the mmap'ed area. * Error if not found. */ map< int, list * >::iterator it; for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it) { list *l = (*it).second; if(l != 0) { list::iterator li; for( li = l->begin(); li != l->end(); ++li) { mmap_record rec = *li; if(rec.get_address() == addr) { /* Unmap the area */ UnmapViewOfFile(addr); CloseHandle(rec.get_handle()); /* Delete the entry. */ l->erase(li); syscall_printf ("0 = munmap(): %x\n", addr); return 0; } } } } set_errno(EINVAL); syscall_printf ("-1 = munmap(): EINVAL\n"); return -1; } /* * Sync file with memory. Ignore flags for now. */ int msync( caddr_t addr, size_t len, int flags) { syscall_printf("msync: addr = %x, len = %d, flags = %x\n", addr, len, flags); if(FlushViewOfFile(addr, len) == 0) { syscall_printf("-1 = msync: LastError = %x\n", GetLastError()); __seterrno(); return -1; } syscall_printf("0 = msync\n"); return 0; } /* * Set memory protection. */ int mprotect(caddr_t addr, size_t len, int prot) { DWORD old_prot; DWORD new_prot = 0; syscall_printf("mprotect(addr = %x, len = %d, prot = %x)\n", addr, len, prot); if(prot & PROT_NONE) new_prot = PAGE_NOACCESS; else { switch(prot) { case PROT_READ|PROT_WRITE|PROT_EXEC: new_prot = PAGE_EXECUTE_READWRITE; break; case PROT_READ|PROT_WRITE: new_prot = PAGE_READWRITE; break; case PROT_READ|PROT_EXEC: new_prot = PAGE_EXECUTE_READ; break; case PROT_READ: new_prot = PAGE_READONLY; break; default: syscall_printf("-1 = mprotect(): invalid prot value\n"); set_errno(EINVAL); return -1; } } if(VirtualProtect(addr, len, prot, &old_prot)== 0) { __seterrno(); syscall_printf("-1 = mprotect(): lasterror = %x\n", GetLastError()); return -1; } syscall_printf("0 = mprotect()\n"); return 0; } }; /* * Called by the parent process durring a fork. At this point, the child has * not yet run. When it does run and initialize, we want it to re-establish * the same mmap regions that we have. Because our heap, where our * mmap_records are stored, will not have been copied to the child at the time * it initializes, we can't simply pass it the "mmapped_areas" pointer. * Instead, we must copy the contents of our "mmapped_areas" into an array of * mmap_records where the child process can read them. The "child->mmap_ptr" * is set to a handle for the memory where the the child will find the array. * After we return, we will suspend and the child will initialize and call * "recreate_mmaps_after_fork" below. */ int copy_mmap_records_to_forkee(pinfo *child) { /* * Count the number of mmap_record entries are in the "mmapped_areas" map. */ unsigned count = 0; map< int, list * >::iterator it; if (mmapped_areas != NULL) { for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it) { list *l = (*it).second; if(l != 0) { list::iterator li; for( li = l->begin(); li != l->end(); ++li) count++; } } } /* * Return with "child->mmap_ptr == NULL"; there are no mmap regions. */ if (count == 0) { child->mmap_ptr = NULL; debug_printf("copy_mmap_records_to_forkee: succeeded\n"); return 0; } /* * Create a file mapping that will be made available to the child process * and will hold an array of all the mmap_records in "mmapped_areas". */ HANDLE h = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, (count+1)*sizeof(mmap_record), NULL); if (h == NULL) { syscall_printf("copy_mmap_records_to_forkee: CreateFileMapping failed\n"); return -1; } mmap_record* pRec = (mmap_record*)MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 0); if (pRec == NULL) { CloseHandle(h); syscall_printf("copy_mmap_records_to_forkee: MapViewOfFile failed\n"); return -1; } /* * Now copy all the mmap_records into the mapped memory array. * The count of elements in this array are in the first record * along with some other useful info. */ *pRec = mmap_record(0, 0, (DWORD)mmapped_areas, count, pRec); if (mmapped_areas != NULL) { for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it) { list *l = (*it).second; if(l != 0) { list::iterator li; for( li = l->begin(); li != l->end(); ++li) *(++pRec) = *li; } } } /* * Duplicate the handle for use by the child process. The child process * will use the duplicate handle in recreate_mmaps_after_fork. * We also unmap our view and close our handle, since we're done with it. */ UnmapViewOfFile(pRec - count); DuplicateHandle(GetCurrentProcess(), h, child->hProcess, (LPHANDLE)&child->mmap_ptr, FILE_MAP_READ, FALSE, DUPLICATE_CLOSE_SOURCE); debug_printf("copy_mmap_records_to_forkee: succeeded\n"); return 0; } /* * Call to re-create all the file mappings in a fork()'ed child. Called from * "cygwin_fork_helper1" by the child durring initialization. At this point, * the parent has duplicated its .data and .bss, but not its stack or heap. * We are passed a handle to memory via "u->self->mmap_ptr" where an array of * mmap_records can be found. This was put there by our parent in its call * to "copy_mmap_records_to_forkee". The first record in the array contains * information like the count of records. All the HANDLE's in those records * are valid for us (we inherited them), but none of the mapped areas * are in our address space. We need to iterate through the array and do the * MapViewOfFileEx calls. Initially, the "mmapped_areas" pointer is assumed to * be NULL, since this process was just created. Before returning, both * "mmapped_areas" and "u->self->mmap_ptr" are set to point to the mmap data * structure in the heap. This pointer is also passed to us in the first * mmap_record. Remember, that pointer refers to heap memory which the parent * process hasn't yet copied to us. After we return, we will suspend while we * wait for our parent to copy our heap (so that "mmaped_areas" is valid). * All of our mmap regions, except the ones that the parent created with * FILE_MAP_COPY, will refer to the same physical memory that the cooresponding * region in our parent refers to. In the case of FILE_MAP_COPY regions, we * will see the contents of the original file at the time the parent mmapped it. * Any changes made by the parent to these regions will not be seen by us. So, * the parent will also need to call "copy_copyonwrite_mmaps_to_forkee" below so * that we are in sync with the parent immediately after the fork. */ int recreate_mmaps_after_fork(void *param) { if (param != NULL) { HANDLE h = (HANDLE)param; mmap_record* pRec = (mmap_record*)MapViewOfFile(h, FILE_MAP_READ, 0, 0, sizeof(mmap_record)); if (pRec == NULL) { small_printf("-1 = recreate_mmaps_after_fork(): MapViewOfFile failed " "with GetLastError = %x\n", GetLastError()); return -1; } /* * Copy the information that was put in the first record * (see set_child_mmap_ptr). */ void* pRecDesired = pRec->get_address(); unsigned count = pRec->get_size(); mmapped_areas = (map< int, list * > *)pRec->get_offset(); /* * We need to have the array of mmap_records at the address "pRecDesired". * Otherwise, this array may be overlapping one of the mapped regions * in the parent that we are trying to duplicate. It is unlikely that * we are so lucky as to have "pRec" mapped correctly the first time, so * unmap this view and re-map it using MapViewOfFileEx. */ UnmapViewOfFile(pRec); if (count > 0) { pRec = (mmap_record*)MapViewOfFileEx(h, FILE_MAP_READ, 0, 0, 0, pRecDesired); if (pRec == NULL) { small_printf("-1 = recreate_mmaps_after_fork(): MapViewOfFileEx failed " "with GetLastError = %x\n", GetLastError()); return -1; } if (pRec != pRecDesired) { small_printf("-1 = recreate_mmaps_after_fork(): MapViewOfFileEx wrong address\n"); return -1; } /* * Copy the "pRec" mmap_record array into the * "mmaped_areas" map of mmap_record lists. */ while (count-- > 0) { ++pRec; /* Now re-create the MapViewOfFileEx call */ void* base = MapViewOfFileEx(pRec->get_handle(), pRec->get_access(), 0, pRec->get_offset(), pRec->get_size(), pRec->get_address()); if(base != pRec->get_address()) { small_printf("recreate_mmaps_after_fork: base address %x\ fails to match requested address %x\n", base, pRec->get_address()); return -1; } debug_printf("recreate_mmaps_after_fork: h = %x, access = %x, offset = %d, \ size = %d, address = %x\n", pRec->get_handle(), pRec->get_access(), pRec->get_offset(), pRec->get_size(), pRec->get_address()); } UnmapViewOfFile(pRecDesired); } CloseHandle(h); } /* Now set our mmap record in case the child forks. */ u->self->mmap_ptr = mmapped_areas; debug_printf("recreate_mmaps_after_fork: succeeded\n"); return 0; } /* * Called by the parent process durring a fork. At this point, * the address space of the child process has been established; * that is, the child has already called "recreate_mmaps_after_fork". * Any mmap region created with "FILE_MAP_COPY" will need to be * copied to the cooresponding memory addresses in the child process, * or else the parent and child may not see the same contents at those * addresses. They will be different for those FILE_MAP_COPY regions * that the parent has written to (unless the parent wrote what was * there originally). */ extern int copy_memory_to_forkee (HANDLE child, void *low, void *high, int); int copy_copyonwrite_mmaps_to_forkee(HANDLE hChild) { /* * Iterate through the array if mmap_records and copy those entries * that have FILE_MAP_COPY set. */ map< int, list * >::iterator it; if (mmapped_areas != NULL) { for( it = mmapped_areas->begin(); it != mmapped_areas->end(); ++it) { list *l = (*it).second; if(l != 0) { list::iterator li; for( li = l->begin(); li != l->end(); ++li) { mmap_record& rec = *li; char* start = (char*)rec.get_address() + rec.get_offset(); if ((rec.get_access() & FILE_MAP_COPY) && !copy_memory_to_forkee(hChild, start, start + rec.get_size(), 0)) { syscall_printf("copy_copyonwrite_mmaps_to_forkee: copy_memory_to_forkee failed\n"); return -1; } debug_printf("copy_copyonwrite_mmaps_to_forkee: h = %x, " "access = %x, offset = %d, size = %d, address = %x\n", rec.get_handle(), rec.get_access(), rec.get_offset(), rec.get_size(), rec.get_address()); } } } } debug_printf("copy_copyonwrite_mmaps_to_forkee: succeeded\n"); return 0; }