/* mmap.cc Copyright 1996, 1997, 1998, 2000 Cygnus Solutions. This file is part of Cygwin. This software is a copyrighted work licensed under the terms of the Cygwin license. Please consult the file "CYGWIN_LICENSE" for details. */ #include "winsup.h" #include #include #include #include #include "fhandler.h" #include "dtable.h" #include "cygerrno.h" #include "thread.h" #include "sync.h" #include "sigproc.h" #include "pinfo.h" #include "security.h" /* * 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_; } }; class list { public: mmap_record *recs; int nrecs, maxrecs; int fd; list (); ~list (); void add_record (mmap_record r); void erase (int i); }; list::list () { recs = (mmap_record *) malloc (10 * sizeof(mmap_record)); nrecs = 0; maxrecs = 10; fd = 0; } list::~list () { free (recs); } void list::add_record (mmap_record r) { if (nrecs == maxrecs) { maxrecs += 5; recs = (mmap_record *) realloc (recs, maxrecs * sizeof (mmap_record)); } recs[nrecs++] = r; } void list::erase (int i) { for (; i < nrecs-1; i++) recs[i] = recs[i+1]; nrecs--; } class map { public: list **lists; int nlists, maxlists; map (); ~map (); list *get_list_by_fd (int fd); list *add_list (list *l, int fd); void erase (int i); }; map::map () { lists = (list **) malloc (10 * sizeof(list *)); nlists = 0; maxlists = 10; } map::~map () { free (lists); } list * map::get_list_by_fd (int fd) { int i; for (i=0; ifd == fd) return lists[i]; return 0; } list * map::add_list (list *l, int fd) { l->fd = fd; if (nlists == maxlists) { maxlists += 5; lists = (list **) realloc (lists, maxlists * sizeof (list *)); } lists[nlists++] = l; return lists[nlists-1]; } void map::erase (int i) { for (; i < nlists-1; i++) lists[i] = lists[i+1]; nlists--; } /* * Code to keep a record of all mmap'ed areas 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 NO_COPY map *mmapped_areas; extern "C" caddr_t mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t off) { syscall_printf ("addr %x, len %d, prot %x, flags %x, fd %d, off %d", addr, len, prot, flags, fd, off); SetResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); /* Windows 95 does not have fixed addresses */ if ((os_being_run != winNT) && (flags & MAP_FIXED)) { set_errno (EINVAL); syscall_printf ("-1 = mmap(): win95 and MAP_FIXED"); return (caddr_t) -1; } if (mmapped_areas == 0) { /* First mmap call, create STL map */ mmapped_areas = new map; if (mmapped_areas == 0) { set_errno (ENOMEM); syscall_printf ("-1 = mmap(): ENOMEM"); 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; HANDLE hFile; if (fd == -1) hFile = (HANDLE) 0xFFFFFFFF; else { /* Ensure that fd is open */ if (fdtab.not_open (fd)) { set_errno (EBADF); syscall_printf ("-1 = mmap(): EBADF"); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); return (caddr_t) -1; } hFile = fdtab[fd]->get_handle (); } HANDLE h = CreateFileMapping (hFile, &sec_none, protect, 0, len, NULL); if (h == 0) { __seterrno (); syscall_printf ("-1 = mmap(): CreateFileMapping failed with %E"); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); 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 %E"); CloseHandle (h); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); return (caddr_t) -1; } } else { base = MapViewOfFile (h, access, 0, off, len); if (base == 0) { __seterrno (); syscall_printf ("-1 = mmap(): MapViewOfFile failed with %E"); CloseHandle (h); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); return (caddr_t) -1; } } /* Now we should have a successfully mmaped area. Need to save it so forked children can reproduce it. */ mmap_record mmap_rec (h, access, off, len, base); /* Get list of mmapped areas for this fd, create a new one if one does not exist yet. */ list *l = mmapped_areas->get_list_by_fd (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"); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); return (caddr_t) -1; } l = mmapped_areas->add_list (l, fd); } /* Insert into the list */ l->add_record (mmap_rec); syscall_printf ("%x = mmap() succeeded", base); ReleaseResourceLock(LOCK_MMAP_LIST,READ_LOCK|WRITE_LOCK," mmap"); return (caddr_t) base; } /* munmap () removes an mmapped area. It insists that base area requested is the same as that mmapped, error if not. */ extern "C" int munmap (caddr_t addr, size_t len) { syscall_printf ("munmap (addr %x, len %d)", addr, len); SetResourceLock(LOCK_MMAP_LIST,WRITE_LOCK|READ_LOCK," munmap"); /* Check if a mmap'ed area was ever created */ if (mmapped_areas == 0) { syscall_printf ("-1 = munmap(): mmapped_areas == 0"); set_errno (EINVAL); ReleaseResourceLock(LOCK_MMAP_LIST,WRITE_LOCK|READ_LOCK," munmap"); return -1; } /* Iterate through the map, looking for the mmapped area. Error if not found. */ int it; for (it = 0; it < mmapped_areas->nlists; ++it) { list *l = mmapped_areas->lists[it]; if (l != 0) { int li; for (li = 0; li < l->nrecs; ++li) { mmap_record rec = l->recs[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", addr); ReleaseResourceLock(LOCK_MMAP_LIST,WRITE_LOCK|READ_LOCK," munmap"); return 0; } } } } set_errno (EINVAL); syscall_printf ("-1 = munmap(): EINVAL"); ReleaseResourceLock(LOCK_MMAP_LIST,WRITE_LOCK|READ_LOCK," munmap"); return -1; } /* Sync file with memory. Ignore flags for now. */ extern "C" int msync (caddr_t addr, size_t len, int flags) { syscall_printf ("addr = %x, len = %d, flags = %x", addr, len, flags); if (FlushViewOfFile (addr, len) == 0) { syscall_printf ("-1 = msync: %E"); __seterrno (); return -1; } syscall_printf ("0 = msync"); return 0; } /* Set memory protection */ extern "C" 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)", 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"); set_errno (EINVAL); return -1; } } if (VirtualProtect (addr, len, new_prot, &old_prot) == 0) { __seterrno (); syscall_printf ("-1 = mprotect (): %E"); return -1; } syscall_printf ("0 = mprotect ()"); return 0; } /* * Call to re-create all the file mappings in a forked * child. Called from the child in initialization. At this * point we are passed a valid mmaped_areas map, and all the * HANDLE's are valid for the child, but none of the * mapped areas are in our address space. We need to iterate * through the map, doing the MapViewOfFile calls. */ int __stdcall recreate_mmaps_after_fork (void *param) { map *areas = (map *)param; void *base; debug_printf ("recreate_mmaps_after_fork, mmapped_areas %p", areas); /* Check if a mmapped area was ever created */ if (areas == 0) return 0; /* Iterate through the map */ int it; for (it = 0; it < areas->nlists; ++it) { list *l = areas->lists[it]; if (l != 0) { int li; for (li = 0; li < l->nrecs; ++li) { mmap_record rec = l->recs[li]; debug_printf ("h %x, access %x, offset %d, size %d, address %p", rec.get_handle (), rec.get_access (), rec.get_offset (), rec.get_size (), rec.get_address ()); /* Now re-create the MapViewOfFileEx call */ base = MapViewOfFileEx (rec.get_handle (), rec.get_access (), 0, rec.get_offset (), rec.get_size (), rec.get_address ()); if (base != rec.get_address ()) { system_printf ("base address %p fails to match requested address %p", rec.get_address ()); return -1; } } } } /* Now set our mmap record in case the child forks. */ mmapped_areas = areas; debug_printf ("succeeded"); return 0; } /* Set a child mmap ptr from our static one. Used to set child mmap pointer for fork. */ void __stdcall set_child_mmap_ptr (_pinfo *child) { child->mmap_ptr = (void *) mmapped_areas; }