📄 mmap.cc
字号:
/* mmap.cc Copyright 1996, 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.This file is part of Cygwin.This software is a copyrighted work licensed under the terms of theCygwin license. Please consult the file "CYGWIN_LICENSE" fordetails. */#include "winsup.h"#include <unistd.h>#include <stdlib.h>#include <stddef.h>#include <sys/mman.h>#include <errno.h>#include "security.h"#include "fhandler.h"#include "path.h"#include "dtable.h"#include "cygerrno.h"#include "cygheap.h"#include "pinfo.h"#include "sys/cygwin.h"#define PAGE_CNT(bytes) howmany((bytes),getpagesize())#define PGBITS (sizeof (DWORD)*8)#define MAPSIZE(pages) howmany ((pages), PGBITS)#define MAP_SET(n) (map_map_[(n)/PGBITS] |= (1L << ((n) % PGBITS)))#define MAP_CLR(n) (map_map_[(n)/PGBITS] &= ~(1L << ((n) % PGBITS)))#define MAP_ISSET(n) (map_map_[(n)/PGBITS] & (1L << ((n) % PGBITS)))/* * 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: int fdesc_; HANDLE mapping_handle_; int devtype_; DWORD access_mode_; __off64_t offset_; DWORD size_to_map_; caddr_t base_address_; DWORD *map_map_; public: mmap_record (int fd, HANDLE h, DWORD ac, __off64_t o, DWORD s, caddr_t b) : fdesc_ (fd), mapping_handle_ (h), devtype_ (0), access_mode_ (ac), offset_ (o), size_to_map_ (s), base_address_ (b), map_map_ (NULL) { if (fd >= 0 && !cygheap->fdtab.not_open (fd)) devtype_ = cygheap->fdtab[fd]->get_device (); } /* Default Copy constructor/operator=/destructor are ok */ /* Simple accessors */ int get_fd () const { return fdesc_; } HANDLE get_handle () const { return mapping_handle_; } DWORD get_device () const { return devtype_; } DWORD get_access () const { return access_mode_; } DWORD get_offset () const { return offset_; } DWORD get_size () const { return size_to_map_; } caddr_t get_address () const { return base_address_; } DWORD *get_map () const { return map_map_; } void alloc_map () { /* Allocate one bit per page */ map_map_ = (DWORD *) calloc (MAPSIZE (PAGE_CNT (size_to_map_)), sizeof (DWORD)); if (wincap.virtual_protect_works_on_shared_pages ()) { DWORD old_prot; if (!VirtualProtect (base_address_, size_to_map_, PAGE_NOACCESS, &old_prot)) syscall_printf ("-1 = alloc_map (): %E"); } } void free_map () { if (map_map_) free (map_map_); } DWORD find_empty (DWORD pages); __off64_t map_map (__off64_t off, DWORD len); BOOL unmap_map (caddr_t addr, DWORD len); void fixup_map (void); int access (char *address); fhandler_base *alloc_fh (); void free_fh (fhandler_base *fh);};DWORDmmap_record::find_empty (DWORD pages){ DWORD mapped_pages = PAGE_CNT (size_to_map_); DWORD start; if (pages > mapped_pages) return (DWORD)-1; for (start = 0; start <= mapped_pages - pages; ++start) if (!MAP_ISSET (start)) { DWORD cnt; for (cnt = 0; cnt < pages; ++cnt) if (MAP_ISSET (start + cnt)) break; if (cnt >= pages) return start; } return (DWORD)-1;}__off64_tmmap_record::map_map (__off64_t off, DWORD len){ DWORD prot, old_prot; switch (access_mode_) { case FILE_MAP_WRITE: prot = PAGE_READWRITE; break; case FILE_MAP_READ: prot = PAGE_READONLY; break; default: prot = PAGE_WRITECOPY; break; } debug_printf ("map_map (fd=%d, off=%D, len=%d)", fdesc_, off, len); len = PAGE_CNT (len); if (fdesc_ == -1 && !off) { off = find_empty (len); if (off != (DWORD)-1) { if (wincap.virtual_protect_works_on_shared_pages () && !VirtualProtect (base_address_ + off * getpagesize (), len * getpagesize (), prot, &old_prot)) { __seterrno (); return (__off64_t)-1; } while (len-- > 0) MAP_SET (off + len); return off * getpagesize (); } return 0L; } off -= offset_; DWORD start = off / getpagesize (); if (wincap.virtual_protect_works_on_shared_pages () && !VirtualProtect (base_address_ + start * getpagesize (), len * getpagesize (), prot, &old_prot)) { __seterrno (); return (__off64_t)-1; } for (; len-- > 0; ++start) MAP_SET (start); return off;}BOOLmmap_record::unmap_map (caddr_t addr, DWORD len){ DWORD old_prot; DWORD off = addr - base_address_; off /= getpagesize (); len = PAGE_CNT (len); if (wincap.virtual_protect_works_on_shared_pages () && !VirtualProtect (base_address_ + off * getpagesize (), len * getpagesize (), PAGE_NOACCESS, &old_prot)) syscall_printf ("-1 = unmap_map (): %E"); for (; len-- > 0; ++off) MAP_CLR (off); /* Return TRUE if all pages are free'd which may result in unmapping the whole chunk. */ for (len = MAPSIZE (PAGE_CNT (size_to_map_)); len > 0; ) if (map_map_[--len]) return FALSE; return TRUE;}voidmmap_record::fixup_map (){ if (!wincap.virtual_protect_works_on_shared_pages ()) return; DWORD prot, old_prot; switch (access_mode_) { case FILE_MAP_WRITE: prot = PAGE_READWRITE; break; case FILE_MAP_READ: prot = PAGE_READONLY; break; default: prot = PAGE_WRITECOPY; break; } for (DWORD off = PAGE_CNT (size_to_map_); off > 0; --off) VirtualProtect (base_address_ + off * getpagesize (), getpagesize (), MAP_ISSET (off - 1) ? prot : PAGE_NOACCESS, &old_prot);}intmmap_record::access (char *address){ if (address < base_address_ || address >= base_address_ + size_to_map_) return 0; DWORD off = (address - base_address_) / getpagesize (); return MAP_ISSET (off);}static fhandler_disk_file fh_paging_file;fhandler_base *mmap_record::alloc_fh (){ if (get_fd () == -1) { fh_paging_file.set_io_handle (INVALID_HANDLE_VALUE); return &fh_paging_file; } /* The file descriptor could have been closed or, even worse, could have been reused for another file before the call to fork(). This requires creating a fhandler of the correct type to be sure to call the method of the correct class. */ return cygheap->fdtab.build_fhandler (-1, get_device ());}voidmmap_record::free_fh (fhandler_base *fh){ if (get_fd () != -1) cfree (fh);}class list {public: mmap_record *recs; int nrecs, maxrecs; int fd; DWORD hash; list (); ~list (); mmap_record *add_record (mmap_record r); void erase (int i); void erase (); mmap_record *match (__off64_t off, DWORD len); long match (caddr_t addr, DWORD len, long start);};list::list (): nrecs (0), maxrecs (10), fd (0), hash (0){ recs = (mmap_record *) malloc (10 * sizeof (mmap_record));}list::~list (){ for (mmap_record *rec = recs; nrecs-- > 0; ++rec) rec->free_map (); free (recs);}mmap_record *list::add_record (mmap_record r){ if (nrecs == maxrecs) { maxrecs += 5; recs = (mmap_record *) realloc (recs, maxrecs * sizeof (mmap_record)); } recs[nrecs] = r; recs[nrecs].alloc_map (); return recs + nrecs++;}/* Used in mmap() */mmap_record *list::match (__off64_t off, DWORD len){ if (fd == -1 && !off) { len = PAGE_CNT (len); for (int i = 0; i < nrecs; ++i) if (recs[i].find_empty (len) != (DWORD)-1) return recs + i; } else { for (int i = 0; i < nrecs; ++i) if (off >= recs[i].get_offset () && off + len <= recs[i].get_offset () + (PAGE_CNT (recs[i].get_size ()) * getpagesize ())) return recs + i; } return NULL;}/* Used in munmap() */longlist::match (caddr_t addr, DWORD len, __off32_t start){ for (int i = start + 1; i < nrecs; ++i) if (addr >= recs[i].get_address () && addr + len <= recs[i].get_address () + (PAGE_CNT (recs[i].get_size ()) * getpagesize ())) return i; return -1;}voidlist::erase (int i){ recs[i].free_map (); for (; i < nrecs-1; i++) recs[i] = recs[i+1]; nrecs--;}voidlist::erase (){ erase (nrecs-1);}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; i<nlists; i++)#if 0 /* The fd isn't sufficient since it could already be another file. */ if (lists[i]->fd == fd#else /* so we use the name hash value to identify the file unless it's not an anonymous mapping. */ if ((fd == -1 && lists[i]->fd == -1) || (fd != -1 && lists[i]->hash == cygheap->fdtab[fd]->get_namehash ()))#endif return lists[i]; return 0;}list *map::add_list (list *l, int fd){ l->fd = fd; if (fd != -1) l->hash = cygheap->fdtab[fd]->get_namehash (); if (nlists == maxlists) { maxlists += 5; lists = (list **) realloc (lists, maxlists * sizeof (list *)); } lists[nlists++] = l; return lists[nlists-1];}voidmap::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 map *mmapped_areas;extern "C"caddr_tmmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, __off64_t off){ syscall_printf ("addr %x, len %d, prot %x, flags %x, fd %d, off %D", addr, len, prot, flags, fd, off); static DWORD granularity; if (!granularity) { SYSTEM_INFO si; GetSystemInfo (&si); granularity = si.dwAllocationGranularity; } /* Error conditions according to SUSv2 */ if (off % getpagesize () || (!(flags & MAP_SHARED) && !(flags & MAP_PRIVATE)) || ((flags & MAP_SHARED) && (flags & MAP_PRIVATE)) || ((flags & MAP_FIXED) && ((DWORD)addr % granularity)) || !len) { set_errno (EINVAL); syscall_printf ("-1 = mmap(): EINVAL"); return MAP_FAILED; } SetResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); if (mmapped_areas == NULL) { /* First mmap call, create STL map */ mmapped_areas = new map; if (mmapped_areas == NULL) { set_errno (ENOMEM); syscall_printf ("-1 = mmap(): ENOMEM"); ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); return MAP_FAILED; } } if (flags & MAP_ANONYMOUS) fd = -1; /* Map always in multipliers of `granularity'-sized chunks. */ __off64_t gran_off = off & ~(granularity - 1); DWORD gran_len = howmany (off + len, granularity) * granularity - gran_off; fhandler_base *fh; caddr_t base = addr; HANDLE h; if (fd != -1) { /* Ensure that fd is open */ cygheap_fdget cfd (fd); if (cfd < 0) { syscall_printf ("-1 = mmap(): EBADF"); ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap"); return MAP_FAILED; } fh = cfd; if (fh->get_device () == FH_DISK) { DWORD high; DWORD low = GetFileSize (fh->get_handle (), &high); __off64_t fsiz = ((__off64_t)high << 32) + low; fsiz -= gran_off; if (gran_len > fsiz) gran_len = fsiz; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -