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-proclib.h"
35 #define MAP_ANONYMOUS MAP_ANON
50 static void* malloced_shared_area = NULL;
53 malloc_shared_area (int pid)
55 int size = mono_pagesize ();
56 SAreaHeader *sarea = g_malloc0 (size);
59 sarea->stats_start = sizeof (SAreaHeader);
60 sarea->stats_end = sizeof (SAreaHeader);
66 aligned_address (char *mem, size_t size, size_t alignment)
68 char *aligned = (char*)((gulong)(mem + (alignment - 1)) & ~(alignment - 1));
69 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((gulong)aligned & (alignment - 1)));
79 static int saved_pagesize = 0;
81 return saved_pagesize;
82 GetSystemInfo (&info);
83 saved_pagesize = info.dwAllocationGranularity;
84 return saved_pagesize;
88 prot_from_flags (int flags)
90 int prot = flags & (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC);
92 case 0: prot = PAGE_NOACCESS; break;
93 case MONO_MMAP_READ: prot = PAGE_READONLY; break;
94 case MONO_MMAP_READ|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READ; break;
95 case MONO_MMAP_READ|MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
96 case MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
97 case MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
98 case MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
99 case MONO_MMAP_EXEC: prot = PAGE_EXECUTE; break;
101 g_assert_not_reached ();
107 mono_valloc (void *addr, size_t length, int flags)
110 int mflags = MEM_COMMIT;
111 int prot = prot_from_flags (flags);
112 /* translate the flags */
114 ptr = VirtualAlloc (addr, length, mflags, prot);
119 mono_valloc_aligned (size_t length, size_t alignment, int flags)
121 int prot = prot_from_flags (flags);
122 char *mem = VirtualAlloc (NULL, length + alignment, MEM_RESERVE, prot);
128 aligned = aligned_address (mem, length, alignment);
130 aligned = VirtualAlloc (aligned, length, MEM_COMMIT, prot);
136 #define HAVE_VALLOC_ALIGNED
139 mono_vfree (void *addr, size_t length)
141 MEMORY_BASIC_INFORMATION mbi;
142 SIZE_T query_result = VirtualQuery (addr, &mbi, sizeof (mbi));
145 g_assert (query_result);
147 res = VirtualFree (mbi.AllocationBase, 0, MEM_RELEASE);
155 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
159 HANDLE file, mapping;
160 int prot = prot_from_flags (flags);
161 /* translate the flags */
162 /*if (flags & MONO_MMAP_PRIVATE)
163 mflags |= MAP_PRIVATE;
164 if (flags & MONO_MMAP_SHARED)
165 mflags |= MAP_SHARED;
166 if (flags & MONO_MMAP_ANON)
167 mflags |= MAP_ANONYMOUS;
168 if (flags & MONO_MMAP_FIXED)
170 if (flags & MONO_MMAP_32BIT)
171 mflags |= MAP_32BIT;*/
173 mflags = FILE_MAP_READ;
174 if (flags & MONO_MMAP_WRITE)
175 mflags = FILE_MAP_COPY;
177 file = (HANDLE) _get_osfhandle (fd);
178 mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
181 ptr = MapViewOfFile (mapping, mflags, 0, offset, length);
183 CloseHandle (mapping);
186 *ret_handle = (void*)mapping;
191 mono_file_unmap (void *addr, void *handle)
193 UnmapViewOfFile (addr);
194 CloseHandle ((HANDLE)handle);
199 mono_mprotect (void *addr, size_t length, int flags)
202 int prot = prot_from_flags (flags);
204 if (flags & MONO_MMAP_DISCARD) {
205 VirtualFree (addr, length, MEM_DECOMMIT);
206 VirtualAlloc (addr, length, MEM_COMMIT, prot);
209 return VirtualProtect (addr, length, prot, &oldprot) == 0;
213 mono_shared_area (void)
215 if (!malloced_shared_area)
216 malloced_shared_area = malloc_shared_area (0);
217 /* get the pid here */
218 return malloced_shared_area;
222 mono_shared_area_remove (void)
224 if (malloced_shared_area)
225 g_free (malloced_shared_area);
226 malloced_shared_area = NULL;
230 mono_shared_area_for_pid (void *pid)
236 mono_shared_area_unload (void *area)
241 mono_shared_area_instances (void **array, int count)
247 #if defined(HAVE_MMAP)
251 * Get the page size in use on the system. Addresses and sizes in the
252 * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
255 * Returns: the page size in bytes.
260 static int saved_pagesize = 0;
262 return saved_pagesize;
263 saved_pagesize = getpagesize ();
264 return saved_pagesize;
268 prot_from_flags (int flags)
270 int prot = PROT_NONE;
271 /* translate the protection bits */
272 if (flags & MONO_MMAP_READ)
274 if (flags & MONO_MMAP_WRITE)
276 if (flags & MONO_MMAP_EXEC)
283 * @addr: memory address
284 * @length: memory area size
285 * @flags: protection flags
287 * Allocates @length bytes of virtual memory with the @flags
288 * protection. @addr can be a preferred memory address or a
289 * mandatory one if MONO_MMAP_FIXED is set in @flags.
290 * @addr must be pagesize aligned and can be NULL.
291 * @length must be a multiple of pagesize.
293 * Returns: NULL on failure, the address of the memory area otherwise
296 mono_valloc (void *addr, size_t length, int flags)
300 int prot = prot_from_flags (flags);
301 /* translate the flags */
302 if (flags & MONO_MMAP_FIXED)
304 if (flags & MONO_MMAP_32BIT)
307 mflags |= MAP_ANONYMOUS;
308 mflags |= MAP_PRIVATE;
310 ptr = mmap (addr, length, prot, mflags, -1, 0);
311 if (ptr == (void*)-1) {
312 int fd = open ("/dev/zero", O_RDONLY);
314 ptr = mmap (addr, length, prot, mflags, fd, 0);
317 if (ptr == (void*)-1)
325 * @addr: memory address returned by mono_valloc ()
326 * @length: size of memory area
328 * Remove the memory mapping at the address @addr.
330 * Returns: 0 on success.
333 mono_vfree (void *addr, size_t length)
335 return munmap (addr, length);
340 * @length: size of data to map
341 * @flags: protection flags
342 * @fd: file descriptor
343 * @offset: offset in the file
344 * @ret_handle: pointer to storage for returning a handle for the map
346 * Map the area of the file pointed to by the file descriptor @fd, at offset
347 * @offset and of size @length in memory according to the protection flags
349 * @offset and @length must be multiples of the page size.
350 * @ret_handle must point to a void*: this value must be used when unmapping
351 * the memory area using mono_file_unmap ().
355 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
359 int prot = prot_from_flags (flags);
360 /* translate the flags */
361 if (flags & MONO_MMAP_PRIVATE)
362 mflags |= MAP_PRIVATE;
363 if (flags & MONO_MMAP_SHARED)
364 mflags |= MAP_SHARED;
365 if (flags & MONO_MMAP_FIXED)
367 if (flags & MONO_MMAP_32BIT)
370 ptr = mmap (0, length, prot, mflags, fd, offset);
371 if (ptr == (void*)-1)
373 *ret_handle = (void*)length;
379 * @addr: memory address returned by mono_file_map ()
380 * @handle: handle of memory map
382 * Remove the memory mapping at the address @addr.
383 * @handle must be the value returned in ret_handle by mono_file_map ().
385 * Returns: 0 on success.
388 mono_file_unmap (void *addr, void *handle)
390 return munmap (addr, (size_t)handle);
395 * @addr: memory address
396 * @length: size of memory area
397 * @flags: new protection flags
399 * Change the protection for the memory area at @addr for @length bytes
400 * to matche the supplied @flags.
401 * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
402 * and the area is cleared to zero.
403 * @addr must be aligned to the page size.
404 * @length must be a multiple of the page size.
406 * Returns: 0 on success.
408 #if defined(__native_client__)
410 mono_mprotect (void *addr, size_t length, int flags)
412 int prot = prot_from_flags (flags);
415 if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
417 new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
418 if (new_addr == addr) return 0;
423 mono_mprotect (void *addr, size_t length, int flags)
425 int prot = prot_from_flags (flags);
427 if (flags & MONO_MMAP_DISCARD) {
428 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
430 if (madvise (addr, length, MADV_DONTNEED))
431 memset (addr, 0, length);
433 memset (addr, 0, length);
435 madvise (addr, length, MADV_DONTNEED);
436 madvise (addr, length, MADV_FREE);
438 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
442 return mprotect (addr, length, prot);
444 #endif // __native_client__
448 /* dummy malloc-based implementation */
456 mono_valloc (void *addr, size_t length, int flags)
458 return malloc (length);
462 mono_valloc_aligned (size_t length, size_t alignment, int flags)
464 g_assert_not_reached ();
467 #define HAVE_VALLOC_ALIGNED
470 mono_vfree (void *addr, size_t length)
477 mono_mprotect (void *addr, size_t length, int flags)
479 if (flags & MONO_MMAP_DISCARD) {
480 memset (addr, 0, length);
486 static int use_shared_area;
489 shared_area_disabled (void)
491 if (!use_shared_area) {
492 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
493 use_shared_area = -1;
496 return use_shared_area == -1;
499 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
502 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
507 gpointer *processes = mono_process_list (&num);
508 for (i = 0; i < num; ++i) {
509 data = mono_shared_area_for_pid (processes [i]);
512 mono_shared_area_unload (data);
515 array [j++] = processes [i];
525 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
529 int curpid = getpid ();
530 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
532 return mono_shared_area_instances_slow (array, count, cleanup);
533 while ((name = g_dir_read_name (dir))) {
536 if (strncmp (name, "mono.", 5))
538 pid = strtol (name + 5, &nend, 10);
539 if (pid <= 0 || nend == name + 5 || *nend)
543 array [i++] = GINT_TO_POINTER (pid);
547 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
549 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
558 mono_shared_area (void)
562 /* we should allow the user to configure the size */
563 int size = mono_pagesize ();
568 if (shared_area_disabled ()) {
569 if (!malloced_shared_area)
570 malloced_shared_area = malloc_shared_area (0);
571 /* get the pid here */
572 return malloced_shared_area;
575 /* perform cleanup of segments left over from dead processes */
576 mono_shared_area_instances_helper (NULL, 0, TRUE);
578 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
580 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
581 if (fd == -1 && errno == EEXIST) {
584 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
586 /* in case of failure we try to return a memory area anyway,
587 * even if it means the data can't be read by other processes
590 return malloc_shared_area (pid);
591 if (ftruncate (fd, size) != 0) {
595 res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
596 if (res == MAP_FAILED) {
599 return malloc_shared_area (pid);
601 /* we don't need the file descriptor anymore */
606 header->stats_start = sizeof (SAreaHeader);
607 header->stats_end = sizeof (SAreaHeader);
609 atexit (mono_shared_area_remove);
614 mono_shared_area_remove (void)
618 if (shared_area_disabled ()) {
619 if (malloced_shared_area)
620 g_free (malloced_shared_area);
624 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
626 if (malloced_shared_area)
627 g_free (malloced_shared_area);
631 mono_shared_area_for_pid (void *pid)
634 /* we should allow the user to configure the size */
635 int size = mono_pagesize ();
639 if (shared_area_disabled ())
642 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
644 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
647 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
648 if (res == MAP_FAILED) {
652 /* FIXME: validate the area */
653 /* we don't need the file descriptor anymore */
659 mono_shared_area_unload (void *area)
661 /* FIXME: currently we load only a page */
662 munmap (area, mono_pagesize ());
666 mono_shared_area_instances (void **array, int count)
668 return mono_shared_area_instances_helper (array, count, FALSE);
672 mono_shared_area (void)
674 if (!malloced_shared_area)
675 malloced_shared_area = malloc_shared_area (getpid ());
676 /* get the pid here */
677 return malloced_shared_area;
681 mono_shared_area_remove (void)
683 if (malloced_shared_area)
684 g_free (malloced_shared_area);
685 malloced_shared_area = NULL;
689 mono_shared_area_for_pid (void *pid)
695 mono_shared_area_unload (void *area)
700 mono_shared_area_instances (void **array, int count)
705 #endif // HAVE_SHM_OPEN
709 #ifndef HAVE_VALLOC_ALIGNED
711 mono_valloc_aligned (size_t size, size_t alignment, int flags)
713 /* Allocate twice the memory to be able to put the block on an aligned address */
714 char *mem = mono_valloc (NULL, size + alignment, flags);
720 aligned = aligned_address (mem, size, alignment);
723 mono_vfree (mem, aligned - mem);
724 if (aligned + size < mem + size + alignment)
725 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size));