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>
31 #include "mono-mmap.h"
32 #include "mono-mmap-internal.h"
33 #include "mono-proclib.h"
36 #define MAP_ANONYMOUS MAP_ANON
51 static void* malloced_shared_area = NULL;
54 malloc_shared_area (int pid)
56 int size = mono_pagesize ();
57 SAreaHeader *sarea = g_malloc0 (size);
60 sarea->stats_start = sizeof (SAreaHeader);
61 sarea->stats_end = sizeof (SAreaHeader);
67 aligned_address (char *mem, size_t size, size_t alignment)
69 char *aligned = (char*)((size_t)(mem + (alignment - 1)) & ~(alignment - 1));
70 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((size_t)aligned & (alignment - 1)));
80 static int saved_pagesize = 0;
82 return saved_pagesize;
83 GetSystemInfo (&info);
84 saved_pagesize = info.dwAllocationGranularity;
85 return saved_pagesize;
89 prot_from_flags (int flags)
91 int prot = flags & (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC);
93 case 0: prot = PAGE_NOACCESS; break;
94 case MONO_MMAP_READ: prot = PAGE_READONLY; break;
95 case MONO_MMAP_READ|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READ; break;
96 case MONO_MMAP_READ|MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
97 case MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
98 case MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
99 case MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
100 case MONO_MMAP_EXEC: prot = PAGE_EXECUTE; break;
102 g_assert_not_reached ();
108 mono_valloc (void *addr, size_t length, int flags)
111 int mflags = MEM_RESERVE|MEM_COMMIT;
112 int prot = prot_from_flags (flags);
113 /* translate the flags */
115 ptr = VirtualAlloc (addr, length, mflags, prot);
120 mono_valloc_aligned (size_t length, size_t alignment, int flags)
122 int prot = prot_from_flags (flags);
123 char *mem = VirtualAlloc (NULL, length + alignment, MEM_RESERVE, prot);
129 aligned = aligned_address (mem, length, alignment);
131 aligned = VirtualAlloc (aligned, length, MEM_COMMIT, prot);
137 #define HAVE_VALLOC_ALIGNED
140 mono_vfree (void *addr, size_t length)
142 MEMORY_BASIC_INFORMATION mbi;
143 SIZE_T query_result = VirtualQuery (addr, &mbi, sizeof (mbi));
146 g_assert (query_result);
148 res = VirtualFree (mbi.AllocationBase, 0, MEM_RELEASE);
156 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
160 HANDLE file, mapping;
161 int prot = prot_from_flags (flags);
162 /* translate the flags */
163 /*if (flags & MONO_MMAP_PRIVATE)
164 mflags |= MAP_PRIVATE;
165 if (flags & MONO_MMAP_SHARED)
166 mflags |= MAP_SHARED;
167 if (flags & MONO_MMAP_ANON)
168 mflags |= MAP_ANONYMOUS;
169 if (flags & MONO_MMAP_FIXED)
171 if (flags & MONO_MMAP_32BIT)
172 mflags |= MAP_32BIT;*/
174 mflags = FILE_MAP_READ;
175 if (flags & MONO_MMAP_WRITE)
176 mflags = FILE_MAP_COPY;
178 file = (HANDLE) _get_osfhandle (fd);
179 mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
182 ptr = MapViewOfFile (mapping, mflags, 0, offset, length);
184 CloseHandle (mapping);
187 *ret_handle = (void*)mapping;
192 mono_file_unmap (void *addr, void *handle)
194 UnmapViewOfFile (addr);
195 CloseHandle ((HANDLE)handle);
200 mono_mprotect (void *addr, size_t length, int flags)
203 int prot = prot_from_flags (flags);
205 if (flags & MONO_MMAP_DISCARD) {
206 VirtualFree (addr, length, MEM_DECOMMIT);
207 VirtualAlloc (addr, length, MEM_COMMIT, prot);
210 return VirtualProtect (addr, length, prot, &oldprot) == 0;
214 mono_shared_area (void)
216 if (!malloced_shared_area)
217 malloced_shared_area = malloc_shared_area (0);
218 /* get the pid here */
219 return malloced_shared_area;
223 mono_shared_area_remove (void)
225 if (malloced_shared_area)
226 g_free (malloced_shared_area);
227 malloced_shared_area = NULL;
231 mono_shared_area_for_pid (void *pid)
237 mono_shared_area_unload (void *area)
242 mono_shared_area_instances (void **array, int count)
248 mono_pages_not_faulted (void *addr, size_t length)
254 #if defined(HAVE_MMAP)
258 * Get the page size in use on the system. Addresses and sizes in the
259 * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
262 * Returns: the page size in bytes.
267 static int saved_pagesize = 0;
269 return saved_pagesize;
270 saved_pagesize = getpagesize ();
271 return saved_pagesize;
275 prot_from_flags (int flags)
277 int prot = PROT_NONE;
278 /* translate the protection bits */
279 if (flags & MONO_MMAP_READ)
281 if (flags & MONO_MMAP_WRITE)
283 if (flags & MONO_MMAP_EXEC)
290 * @addr: memory address
291 * @length: memory area size
292 * @flags: protection flags
294 * Allocates @length bytes of virtual memory with the @flags
295 * protection. @addr can be a preferred memory address or a
296 * mandatory one if MONO_MMAP_FIXED is set in @flags.
297 * @addr must be pagesize aligned and can be NULL.
298 * @length must be a multiple of pagesize.
300 * Returns: NULL on failure, the address of the memory area otherwise
303 mono_valloc (void *addr, size_t length, int flags)
307 int prot = prot_from_flags (flags);
308 /* translate the flags */
309 if (flags & MONO_MMAP_FIXED)
311 if (flags & MONO_MMAP_32BIT)
314 mflags |= MAP_ANONYMOUS;
315 mflags |= MAP_PRIVATE;
317 ptr = mmap (addr, length, prot, mflags, -1, 0);
318 if (ptr == MAP_FAILED) {
319 int fd = open ("/dev/zero", O_RDONLY);
321 ptr = mmap (addr, length, prot, mflags, fd, 0);
324 if (ptr == MAP_FAILED)
332 * @addr: memory address returned by mono_valloc ()
333 * @length: size of memory area
335 * Remove the memory mapping at the address @addr.
337 * Returns: 0 on success.
340 mono_vfree (void *addr, size_t length)
342 return munmap (addr, length);
347 * @length: size of data to map
348 * @flags: protection flags
349 * @fd: file descriptor
350 * @offset: offset in the file
351 * @ret_handle: pointer to storage for returning a handle for the map
353 * Map the area of the file pointed to by the file descriptor @fd, at offset
354 * @offset and of size @length in memory according to the protection flags
356 * @offset and @length must be multiples of the page size.
357 * @ret_handle must point to a void*: this value must be used when unmapping
358 * the memory area using mono_file_unmap ().
362 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
366 int prot = prot_from_flags (flags);
367 /* translate the flags */
368 if (flags & MONO_MMAP_PRIVATE)
369 mflags |= MAP_PRIVATE;
370 if (flags & MONO_MMAP_SHARED)
371 mflags |= MAP_SHARED;
372 if (flags & MONO_MMAP_FIXED)
374 if (flags & MONO_MMAP_32BIT)
377 ptr = mmap (0, length, prot, mflags, fd, offset);
378 if (ptr == MAP_FAILED)
380 *ret_handle = (void*)length;
386 * @addr: memory address returned by mono_file_map ()
387 * @handle: handle of memory map
389 * Remove the memory mapping at the address @addr.
390 * @handle must be the value returned in ret_handle by mono_file_map ().
392 * Returns: 0 on success.
395 mono_file_unmap (void *addr, void *handle)
397 return munmap (addr, (size_t)handle);
402 * @addr: memory address
403 * @length: size of memory area
404 * @flags: new protection flags
406 * Change the protection for the memory area at @addr for @length bytes
407 * to matche the supplied @flags.
408 * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
409 * and the area is cleared to zero.
410 * @addr must be aligned to the page size.
411 * @length must be a multiple of the page size.
413 * Returns: 0 on success.
415 #if defined(__native_client__)
417 mono_mprotect (void *addr, size_t length, int flags)
419 int prot = prot_from_flags (flags);
422 if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
424 new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
425 if (new_addr == addr) return 0;
430 mono_mprotect (void *addr, size_t length, int flags)
432 int prot = prot_from_flags (flags);
434 if (flags & MONO_MMAP_DISCARD) {
435 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
437 if (madvise (addr, length, MADV_DONTNEED))
438 memset (addr, 0, length);
440 memset (addr, 0, length);
442 madvise (addr, length, MADV_DONTNEED);
443 madvise (addr, length, MADV_FREE);
445 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
449 return mprotect (addr, length, prot);
451 #endif // __native_client__
454 mono_pages_not_faulted (void *addr, size_t size)
458 int pagesize = mono_pagesize ();
459 int npages = (size + pagesize - 1) / pagesize;
460 char *faulted = g_malloc0 (sizeof (char*) * npages);
462 if (mincore (addr, size, faulted) != 0) {
466 for (i = 0; i < npages; ++i) {
467 if (faulted [i] != 0)
479 /* dummy malloc-based implementation */
487 mono_valloc (void *addr, size_t length, int flags)
489 return malloc (length);
493 mono_valloc_aligned (size_t length, size_t alignment, int flags)
495 g_assert_not_reached ();
498 #define HAVE_VALLOC_ALIGNED
501 mono_vfree (void *addr, size_t length)
508 mono_mprotect (void *addr, size_t length, int flags)
510 if (flags & MONO_MMAP_DISCARD) {
511 memset (addr, 0, length);
517 mono_pages_not_faulted (void *addr, size_t length)
524 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
526 static int use_shared_area;
529 shared_area_disabled (void)
531 if (!use_shared_area) {
532 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
533 use_shared_area = -1;
537 return use_shared_area == -1;
541 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
546 gpointer *processes = mono_process_list (&num);
547 for (i = 0; i < num; ++i) {
548 data = mono_shared_area_for_pid (processes [i]);
551 mono_shared_area_unload (data);
554 array [j++] = processes [i];
564 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
568 int curpid = getpid ();
569 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
571 return mono_shared_area_instances_slow (array, count, cleanup);
572 while ((name = g_dir_read_name (dir))) {
575 if (strncmp (name, "mono.", 5))
577 pid = strtol (name + 5, &nend, 10);
578 if (pid <= 0 || nend == name + 5 || *nend)
582 array [i++] = GINT_TO_POINTER (pid);
586 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
588 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
597 mono_shared_area (void)
601 /* we should allow the user to configure the size */
602 int size = mono_pagesize ();
607 if (shared_area_disabled ()) {
608 if (!malloced_shared_area)
609 malloced_shared_area = malloc_shared_area (0);
610 /* get the pid here */
611 return malloced_shared_area;
614 /* perform cleanup of segments left over from dead processes */
615 mono_shared_area_instances_helper (NULL, 0, TRUE);
617 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
619 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
620 if (fd == -1 && errno == EEXIST) {
623 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
625 /* in case of failure we try to return a memory area anyway,
626 * even if it means the data can't be read by other processes
629 return malloc_shared_area (pid);
630 if (ftruncate (fd, size) != 0) {
634 res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
635 if (res == MAP_FAILED) {
638 return malloc_shared_area (pid);
640 /* we don't need the file descriptor anymore */
645 header->stats_start = sizeof (SAreaHeader);
646 header->stats_end = sizeof (SAreaHeader);
648 mono_atexit (mono_shared_area_remove);
653 mono_shared_area_remove (void)
657 if (shared_area_disabled ()) {
658 if (malloced_shared_area)
659 g_free (malloced_shared_area);
663 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
665 if (malloced_shared_area)
666 g_free (malloced_shared_area);
670 mono_shared_area_for_pid (void *pid)
673 /* we should allow the user to configure the size */
674 int size = mono_pagesize ();
678 if (shared_area_disabled ())
681 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
683 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
686 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
687 if (res == MAP_FAILED) {
691 /* FIXME: validate the area */
692 /* we don't need the file descriptor anymore */
698 mono_shared_area_unload (void *area)
700 /* FIXME: currently we load only a page */
701 munmap (area, mono_pagesize ());
705 mono_shared_area_instances (void **array, int count)
707 return mono_shared_area_instances_helper (array, count, FALSE);
711 mono_shared_area (void)
713 if (!malloced_shared_area)
714 malloced_shared_area = malloc_shared_area (getpid ());
715 /* get the pid here */
716 return malloced_shared_area;
720 mono_shared_area_remove (void)
722 if (malloced_shared_area)
723 g_free (malloced_shared_area);
724 malloced_shared_area = NULL;
728 mono_shared_area_for_pid (void *pid)
734 mono_shared_area_unload (void *area)
739 mono_shared_area_instances (void **array, int count)
744 #endif // HAVE_SHM_OPEN
748 #ifndef HAVE_VALLOC_ALIGNED
750 mono_valloc_aligned (size_t size, size_t alignment, int flags)
752 /* Allocate twice the memory to be able to put the block on an aligned address */
753 char *mem = mono_valloc (NULL, size + alignment, flags);
759 aligned = aligned_address (mem, size, alignment);
762 mono_vfree (mem, aligned - mem);
763 if (aligned + size < mem + size + alignment)
764 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size));