2 * mono-mmap.c: Support for mapping code into the process address space
5 * Mono Team (mono-list@lists.ximian.com)
7 * Copyright 2001-2008 Novell, Inc.
16 #include <sys/types.h>
33 #include "mono-mmap.h"
34 #include "mono-mmap-internal.h"
35 #include "mono-proclib.h"
38 #define MAP_ANONYMOUS MAP_ANON
53 static void* malloced_shared_area = NULL;
56 malloc_shared_area (int pid)
58 int size = mono_pagesize ();
59 SAreaHeader *sarea = g_malloc0 (size);
62 sarea->stats_start = sizeof (SAreaHeader);
63 sarea->stats_end = sizeof (SAreaHeader);
69 aligned_address (char *mem, size_t size, size_t alignment)
71 char *aligned = (char*)((size_t)(mem + (alignment - 1)) & ~(alignment - 1));
72 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((size_t)aligned & (alignment - 1)));
82 static int saved_pagesize = 0;
84 return saved_pagesize;
85 GetSystemInfo (&info);
86 saved_pagesize = info.dwAllocationGranularity;
87 return saved_pagesize;
91 prot_from_flags (int flags)
93 int prot = flags & (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC);
95 case 0: prot = PAGE_NOACCESS; break;
96 case MONO_MMAP_READ: prot = PAGE_READONLY; break;
97 case MONO_MMAP_READ|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READ; break;
98 case MONO_MMAP_READ|MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
99 case MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
100 case MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
101 case MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
102 case MONO_MMAP_EXEC: prot = PAGE_EXECUTE; break;
104 g_assert_not_reached ();
110 mono_valloc (void *addr, size_t length, int flags)
113 int mflags = MEM_RESERVE|MEM_COMMIT;
114 int prot = prot_from_flags (flags);
115 /* translate the flags */
117 ptr = VirtualAlloc (addr, length, mflags, prot);
122 mono_valloc_aligned (size_t length, size_t alignment, int flags)
124 int prot = prot_from_flags (flags);
125 char *mem = VirtualAlloc (NULL, length + alignment, MEM_RESERVE, prot);
131 aligned = aligned_address (mem, length, alignment);
133 aligned = VirtualAlloc (aligned, length, MEM_COMMIT, prot);
139 #define HAVE_VALLOC_ALIGNED
142 mono_vfree (void *addr, size_t length)
144 MEMORY_BASIC_INFORMATION mbi;
145 SIZE_T query_result = VirtualQuery (addr, &mbi, sizeof (mbi));
148 g_assert (query_result);
150 res = VirtualFree (mbi.AllocationBase, 0, MEM_RELEASE);
158 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
162 HANDLE file, mapping;
163 int prot = prot_from_flags (flags);
164 /* translate the flags */
165 /*if (flags & MONO_MMAP_PRIVATE)
166 mflags |= MAP_PRIVATE;
167 if (flags & MONO_MMAP_SHARED)
168 mflags |= MAP_SHARED;
169 if (flags & MONO_MMAP_ANON)
170 mflags |= MAP_ANONYMOUS;
171 if (flags & MONO_MMAP_FIXED)
173 if (flags & MONO_MMAP_32BIT)
174 mflags |= MAP_32BIT;*/
176 mflags = FILE_MAP_READ;
177 if (flags & MONO_MMAP_WRITE)
178 mflags = FILE_MAP_COPY;
180 file = (HANDLE) _get_osfhandle (fd);
181 mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
184 ptr = MapViewOfFile (mapping, mflags, 0, offset, length);
186 CloseHandle (mapping);
189 *ret_handle = (void*)mapping;
194 mono_file_unmap (void *addr, void *handle)
196 UnmapViewOfFile (addr);
197 CloseHandle ((HANDLE)handle);
202 mono_mprotect (void *addr, size_t length, int flags)
205 int prot = prot_from_flags (flags);
207 if (flags & MONO_MMAP_DISCARD) {
208 VirtualFree (addr, length, MEM_DECOMMIT);
209 VirtualAlloc (addr, length, MEM_COMMIT, prot);
212 return VirtualProtect (addr, length, prot, &oldprot) == 0;
216 mono_shared_area (void)
218 if (!malloced_shared_area)
219 malloced_shared_area = malloc_shared_area (0);
220 /* get the pid here */
221 return malloced_shared_area;
225 mono_shared_area_remove (void)
227 if (malloced_shared_area)
228 g_free (malloced_shared_area);
229 malloced_shared_area = NULL;
233 mono_shared_area_for_pid (void *pid)
239 mono_shared_area_unload (void *area)
244 mono_shared_area_instances (void **array, int count)
250 #if defined(HAVE_MMAP)
254 * Get the page size in use on the system. Addresses and sizes in the
255 * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
258 * Returns: the page size in bytes.
263 static int saved_pagesize = 0;
265 return saved_pagesize;
266 saved_pagesize = getpagesize ();
267 return saved_pagesize;
271 prot_from_flags (int flags)
273 int prot = PROT_NONE;
274 /* translate the protection bits */
275 if (flags & MONO_MMAP_READ)
277 if (flags & MONO_MMAP_WRITE)
279 if (flags & MONO_MMAP_EXEC)
286 * @addr: memory address
287 * @length: memory area size
288 * @flags: protection flags
290 * Allocates @length bytes of virtual memory with the @flags
291 * protection. @addr can be a preferred memory address or a
292 * mandatory one if MONO_MMAP_FIXED is set in @flags.
293 * @addr must be pagesize aligned and can be NULL.
294 * @length must be a multiple of pagesize.
296 * Returns: NULL on failure, the address of the memory area otherwise
299 mono_valloc (void *addr, size_t length, int flags)
303 int prot = prot_from_flags (flags);
304 /* translate the flags */
305 if (flags & MONO_MMAP_FIXED)
307 if (flags & MONO_MMAP_32BIT)
310 mflags |= MAP_ANONYMOUS;
311 mflags |= MAP_PRIVATE;
313 ptr = mmap (addr, length, prot, mflags, -1, 0);
314 if (ptr == MAP_FAILED) {
315 int fd = open ("/dev/zero", O_RDONLY);
317 ptr = mmap (addr, length, prot, mflags, fd, 0);
320 if (ptr == MAP_FAILED)
328 * @addr: memory address returned by mono_valloc ()
329 * @length: size of memory area
331 * Remove the memory mapping at the address @addr.
333 * Returns: 0 on success.
336 mono_vfree (void *addr, size_t length)
338 return munmap (addr, length);
343 * @length: size of data to map
344 * @flags: protection flags
345 * @fd: file descriptor
346 * @offset: offset in the file
347 * @ret_handle: pointer to storage for returning a handle for the map
349 * Map the area of the file pointed to by the file descriptor @fd, at offset
350 * @offset and of size @length in memory according to the protection flags
352 * @offset and @length must be multiples of the page size.
353 * @ret_handle must point to a void*: this value must be used when unmapping
354 * the memory area using mono_file_unmap ().
358 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
362 int prot = prot_from_flags (flags);
363 /* translate the flags */
364 if (flags & MONO_MMAP_PRIVATE)
365 mflags |= MAP_PRIVATE;
366 if (flags & MONO_MMAP_SHARED)
367 mflags |= MAP_SHARED;
368 if (flags & MONO_MMAP_FIXED)
370 if (flags & MONO_MMAP_32BIT)
373 ptr = mmap (0, length, prot, mflags, fd, offset);
374 if (ptr == MAP_FAILED)
376 *ret_handle = (void*)length;
382 * @addr: memory address returned by mono_file_map ()
383 * @handle: handle of memory map
385 * Remove the memory mapping at the address @addr.
386 * @handle must be the value returned in ret_handle by mono_file_map ().
388 * Returns: 0 on success.
391 mono_file_unmap (void *addr, void *handle)
393 return munmap (addr, (size_t)handle);
398 * @addr: memory address
399 * @length: size of memory area
400 * @flags: new protection flags
402 * Change the protection for the memory area at @addr for @length bytes
403 * to matche the supplied @flags.
404 * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
405 * and the area is cleared to zero.
406 * @addr must be aligned to the page size.
407 * @length must be a multiple of the page size.
409 * Returns: 0 on success.
411 #if defined(__native_client__)
413 mono_mprotect (void *addr, size_t length, int flags)
415 int prot = prot_from_flags (flags);
418 if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
420 new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
421 if (new_addr == addr) return 0;
426 mono_mprotect (void *addr, size_t length, int flags)
428 int prot = prot_from_flags (flags);
430 if (flags & MONO_MMAP_DISCARD) {
431 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
433 if (madvise (addr, length, MADV_DONTNEED))
434 memset (addr, 0, length);
436 memset (addr, 0, length);
438 madvise (addr, length, MADV_DONTNEED);
439 madvise (addr, length, MADV_FREE);
441 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
445 return mprotect (addr, length, prot);
447 #endif // __native_client__
451 /* dummy malloc-based implementation */
459 mono_valloc (void *addr, size_t length, int flags)
461 return malloc (length);
465 mono_valloc_aligned (size_t length, size_t alignment, int flags)
467 g_assert_not_reached ();
470 #define HAVE_VALLOC_ALIGNED
473 mono_vfree (void *addr, size_t length)
480 mono_mprotect (void *addr, size_t length, int flags)
482 if (flags & MONO_MMAP_DISCARD) {
483 memset (addr, 0, length);
490 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
492 static int use_shared_area;
495 shared_area_disabled (void)
497 if (!use_shared_area) {
498 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
499 use_shared_area = -1;
503 return use_shared_area == -1;
507 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
512 gpointer *processes = mono_process_list (&num);
513 for (i = 0; i < num; ++i) {
514 data = mono_shared_area_for_pid (processes [i]);
517 mono_shared_area_unload (data);
520 array [j++] = processes [i];
530 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
534 int curpid = getpid ();
535 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
537 return mono_shared_area_instances_slow (array, count, cleanup);
538 while ((name = g_dir_read_name (dir))) {
541 if (strncmp (name, "mono.", 5))
543 pid = strtol (name + 5, &nend, 10);
544 if (pid <= 0 || nend == name + 5 || *nend)
548 array [i++] = GINT_TO_POINTER (pid);
552 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
554 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
563 mono_shared_area (void)
567 /* we should allow the user to configure the size */
568 int size = mono_pagesize ();
573 if (shared_area_disabled ()) {
574 if (!malloced_shared_area)
575 malloced_shared_area = malloc_shared_area (0);
576 /* get the pid here */
577 return malloced_shared_area;
580 /* perform cleanup of segments left over from dead processes */
581 mono_shared_area_instances_helper (NULL, 0, TRUE);
583 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
585 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
586 if (fd == -1 && errno == EEXIST) {
589 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
591 /* in case of failure we try to return a memory area anyway,
592 * even if it means the data can't be read by other processes
595 return malloc_shared_area (pid);
596 if (ftruncate (fd, size) != 0) {
600 res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
601 if (res == MAP_FAILED) {
604 return malloc_shared_area (pid);
606 /* we don't need the file descriptor anymore */
611 header->stats_start = sizeof (SAreaHeader);
612 header->stats_end = sizeof (SAreaHeader);
614 mono_atexit (mono_shared_area_remove);
619 mono_shared_area_remove (void)
623 if (shared_area_disabled ()) {
624 if (malloced_shared_area)
625 g_free (malloced_shared_area);
629 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
631 if (malloced_shared_area)
632 g_free (malloced_shared_area);
636 mono_shared_area_for_pid (void *pid)
639 /* we should allow the user to configure the size */
640 int size = mono_pagesize ();
644 if (shared_area_disabled ())
647 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
649 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
652 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
653 if (res == MAP_FAILED) {
657 /* FIXME: validate the area */
658 /* we don't need the file descriptor anymore */
664 mono_shared_area_unload (void *area)
666 /* FIXME: currently we load only a page */
667 munmap (area, mono_pagesize ());
671 mono_shared_area_instances (void **array, int count)
673 return mono_shared_area_instances_helper (array, count, FALSE);
677 mono_shared_area (void)
679 if (!malloced_shared_area)
680 malloced_shared_area = malloc_shared_area (getpid ());
681 /* get the pid here */
682 return malloced_shared_area;
686 mono_shared_area_remove (void)
688 if (malloced_shared_area)
689 g_free (malloced_shared_area);
690 malloced_shared_area = NULL;
694 mono_shared_area_for_pid (void *pid)
700 mono_shared_area_unload (void *area)
705 mono_shared_area_instances (void **array, int count)
710 #endif // HAVE_SHM_OPEN
714 #ifndef HAVE_VALLOC_ALIGNED
716 mono_valloc_aligned (size_t size, size_t alignment, int flags)
718 /* Allocate twice the memory to be able to put the block on an aligned address */
719 char *mem = mono_valloc (NULL, size + alignment, flags);
725 aligned = aligned_address (mem, size, alignment);
728 mono_vfree (mem, aligned - mem);
729 if (aligned + size < mem + size + alignment)
730 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size));
737 mono_pages_not_faulted (void *addr, size_t size)
742 int pagesize = mono_pagesize ();
743 int npages = (size + pagesize - 1) / pagesize;
744 char *faulted = g_malloc0 (sizeof (char*) * npages);
746 if (mincore (addr, size, faulted) != 0) {
750 for (i = 0; i < npages; ++i) {
751 if (faulted [i] != 0)