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.
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include <sys/types.h>
29 #endif /* !HOST_WIN32 */
31 #include "mono-mmap.h"
32 #include "mono-mmap-internals.h"
33 #include "mono-proclib.h"
34 #include <mono/utils/mono-threads.h>
35 #include <mono/utils/atomic.h>
36 #include <mono/utils/mono-counters.h>
38 #define BEGIN_CRITICAL_SECTION do { \
39 MonoThreadInfo *__info = mono_thread_info_current_unchecked (); \
40 if (__info) __info->inside_critical_region = TRUE; \
42 #define END_CRITICAL_SECTION \
43 if (__info) __info->inside_critical_region = FALSE; \
47 #define MAP_ANONYMOUS MAP_ANON
63 malloc_shared_area (int pid)
65 int size = mono_pagesize ();
66 SAreaHeader *sarea = (SAreaHeader *) g_malloc0 (size);
69 sarea->stats_start = sizeof (SAreaHeader);
70 sarea->stats_end = sizeof (SAreaHeader);
76 aligned_address (char *mem, size_t size, size_t alignment)
78 char *aligned = (char*)((size_t)(mem + (alignment - 1)) & ~(alignment - 1));
79 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((size_t)aligned & (alignment - 1)));
83 static volatile size_t allocation_count [MONO_MEM_ACCOUNT_MAX];
86 account_mem (MonoMemAccountType type, ssize_t size)
88 #if SIZEOF_VOID_P == 4
89 InterlockedAdd ((volatile gint32*)&allocation_count [type], (gint32)size);
91 InterlockedAdd64 ((volatile gint64*)&allocation_count [type], (gint64)size);
96 mono_mem_account_type_name (MonoMemAccountType type)
98 static const char *names[] = {
107 "SGen shadow card table",
109 "SGen binary protocol",
119 mono_mem_account_register_counters (void)
121 for (int i = 0; i < MONO_MEM_ACCOUNT_MAX; ++i) {
122 const char *prefix = "Valloc ";
123 const char *name = mono_mem_account_type_name (i);
125 g_assert (strlen (prefix) + strlen (name) < sizeof (descr));
126 sprintf (descr, "%s%s", prefix, name);
127 mono_counters_register (descr, MONO_COUNTER_WORD | MONO_COUNTER_RUNTIME | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, (void*)&allocation_count [i]);
132 // Windows specific implementation in mono-mmap-windows.c
133 #define HAVE_VALLOC_ALIGNED
137 static void* malloced_shared_area = NULL;
138 #if defined(HAVE_MMAP)
142 * Get the page size in use on the system. Addresses and sizes in the
143 * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
146 * Returns: the page size in bytes.
151 static int saved_pagesize = 0;
153 return saved_pagesize;
154 saved_pagesize = getpagesize ();
155 return saved_pagesize;
159 prot_from_flags (int flags)
161 int prot = PROT_NONE;
162 /* translate the protection bits */
163 if (flags & MONO_MMAP_READ)
165 if (flags & MONO_MMAP_WRITE)
167 if (flags & MONO_MMAP_EXEC)
174 * @addr: memory address
175 * @length: memory area size
176 * @flags: protection flags
178 * Allocates @length bytes of virtual memory with the @flags
179 * protection. @addr can be a preferred memory address or a
180 * mandatory one if MONO_MMAP_FIXED is set in @flags.
181 * @addr must be pagesize aligned and can be NULL.
182 * @length must be a multiple of pagesize.
184 * Returns: NULL on failure, the address of the memory area otherwise
187 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
191 int prot = prot_from_flags (flags);
192 /* translate the flags */
193 if (flags & MONO_MMAP_FIXED)
195 if (flags & MONO_MMAP_32BIT)
198 mflags |= MAP_ANONYMOUS;
199 mflags |= MAP_PRIVATE;
201 BEGIN_CRITICAL_SECTION;
202 ptr = mmap (addr, length, prot, mflags, -1, 0);
203 if (ptr == MAP_FAILED) {
204 int fd = open ("/dev/zero", O_RDONLY);
206 ptr = mmap (addr, length, prot, mflags, fd, 0);
210 END_CRITICAL_SECTION;
212 if (ptr == MAP_FAILED)
215 account_mem (type, (ssize_t)length);
222 * @addr: memory address returned by mono_valloc ()
223 * @length: size of memory area
225 * Remove the memory mapping at the address @addr.
227 * Returns: 0 on success.
230 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
233 BEGIN_CRITICAL_SECTION;
234 res = munmap (addr, length);
235 END_CRITICAL_SECTION;
237 account_mem (type, -(ssize_t)length);
244 * @length: size of data to map
245 * @flags: protection flags
246 * @fd: file descriptor
247 * @offset: offset in the file
248 * @ret_handle: pointer to storage for returning a handle for the map
250 * Map the area of the file pointed to by the file descriptor @fd, at offset
251 * @offset and of size @length in memory according to the protection flags
253 * @offset and @length must be multiples of the page size.
254 * @ret_handle must point to a void*: this value must be used when unmapping
255 * the memory area using mono_file_unmap ().
259 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
263 int prot = prot_from_flags (flags);
264 /* translate the flags */
265 if (flags & MONO_MMAP_PRIVATE)
266 mflags |= MAP_PRIVATE;
267 if (flags & MONO_MMAP_SHARED)
268 mflags |= MAP_SHARED;
269 if (flags & MONO_MMAP_FIXED)
271 if (flags & MONO_MMAP_32BIT)
274 BEGIN_CRITICAL_SECTION;
275 ptr = mmap (0, length, prot, mflags, fd, offset);
276 END_CRITICAL_SECTION;
277 if (ptr == MAP_FAILED)
279 *ret_handle = (void*)length;
285 * @addr: memory address returned by mono_file_map ()
286 * @handle: handle of memory map
288 * Remove the memory mapping at the address @addr.
289 * @handle must be the value returned in ret_handle by mono_file_map ().
291 * Returns: 0 on success.
294 mono_file_unmap (void *addr, void *handle)
298 BEGIN_CRITICAL_SECTION;
299 res = munmap (addr, (size_t)handle);
300 END_CRITICAL_SECTION;
307 * @addr: memory address
308 * @length: size of memory area
309 * @flags: new protection flags
311 * Change the protection for the memory area at @addr for @length bytes
312 * to matche the supplied @flags.
313 * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
314 * and the area is cleared to zero.
315 * @addr must be aligned to the page size.
316 * @length must be a multiple of the page size.
318 * Returns: 0 on success.
320 #if defined(__native_client__)
322 mono_mprotect (void *addr, size_t length, int flags)
324 int prot = prot_from_flags (flags);
327 if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
329 new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
330 if (new_addr == addr) return 0;
335 mono_mprotect (void *addr, size_t length, int flags)
337 int prot = prot_from_flags (flags);
339 if (flags & MONO_MMAP_DISCARD) {
340 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
342 if (madvise (addr, length, MADV_DONTNEED))
343 memset (addr, 0, length);
345 memset (addr, 0, length);
347 madvise (addr, length, MADV_DONTNEED);
348 madvise (addr, length, MADV_FREE);
350 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
354 return mprotect (addr, length, prot);
356 #endif // __native_client__
360 /* dummy malloc-based implementation */
368 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
370 return g_malloc (length);
374 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
376 g_assert_not_reached ();
379 #define HAVE_VALLOC_ALIGNED
382 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
389 mono_mprotect (void *addr, size_t length, int flags)
391 if (flags & MONO_MMAP_DISCARD) {
392 memset (addr, 0, length);
399 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
401 static int use_shared_area;
404 shared_area_disabled (void)
406 if (!use_shared_area) {
407 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
408 use_shared_area = -1;
412 return use_shared_area == -1;
416 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
421 gpointer *processes = mono_process_list (&num);
422 for (i = 0; i < num; ++i) {
423 data = mono_shared_area_for_pid (processes [i]);
426 mono_shared_area_unload (data);
429 array [j++] = processes [i];
439 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
443 int curpid = getpid ();
444 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
446 return mono_shared_area_instances_slow (array, count, cleanup);
447 while ((name = g_dir_read_name (dir))) {
450 if (strncmp (name, "mono.", 5))
452 pid = strtol (name + 5, &nend, 10);
453 if (pid <= 0 || nend == name + 5 || *nend)
457 array [i++] = GINT_TO_POINTER (pid);
461 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
463 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
472 mono_shared_area (void)
476 /* we should allow the user to configure the size */
477 int size = mono_pagesize ();
482 if (shared_area_disabled ()) {
483 if (!malloced_shared_area)
484 malloced_shared_area = malloc_shared_area (0);
485 /* get the pid here */
486 return malloced_shared_area;
489 /* perform cleanup of segments left over from dead processes */
490 mono_shared_area_instances_helper (NULL, 0, TRUE);
492 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
494 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
495 if (fd == -1 && errno == EEXIST) {
498 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
500 /* in case of failure we try to return a memory area anyway,
501 * even if it means the data can't be read by other processes
504 return malloc_shared_area (pid);
505 if (ftruncate (fd, size) != 0) {
509 BEGIN_CRITICAL_SECTION;
510 res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
511 END_CRITICAL_SECTION;
513 if (res == MAP_FAILED) {
516 return malloc_shared_area (pid);
518 /* we don't need the file descriptor anymore */
520 header = (SAreaHeader *) res;
523 header->stats_start = sizeof (SAreaHeader);
524 header->stats_end = sizeof (SAreaHeader);
526 mono_atexit (mono_shared_area_remove);
531 mono_shared_area_remove (void)
535 if (shared_area_disabled ()) {
536 if (malloced_shared_area)
537 g_free (malloced_shared_area);
541 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
543 if (malloced_shared_area)
544 g_free (malloced_shared_area);
548 mono_shared_area_for_pid (void *pid)
551 /* we should allow the user to configure the size */
552 int size = mono_pagesize ();
556 if (shared_area_disabled ())
559 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
561 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
564 BEGIN_CRITICAL_SECTION;
565 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
566 END_CRITICAL_SECTION;
568 if (res == MAP_FAILED) {
572 /* FIXME: validate the area */
573 /* we don't need the file descriptor anymore */
579 mono_shared_area_unload (void *area)
581 /* FIXME: currently we load only a page */
582 BEGIN_CRITICAL_SECTION;
583 munmap (area, mono_pagesize ());
584 END_CRITICAL_SECTION;
588 mono_shared_area_instances (void **array, int count)
590 return mono_shared_area_instances_helper (array, count, FALSE);
594 mono_shared_area (void)
596 if (!malloced_shared_area)
597 malloced_shared_area = malloc_shared_area (getpid ());
598 /* get the pid here */
599 return malloced_shared_area;
603 mono_shared_area_remove (void)
605 if (malloced_shared_area)
606 g_free (malloced_shared_area);
607 malloced_shared_area = NULL;
611 mono_shared_area_for_pid (void *pid)
617 mono_shared_area_unload (void *area)
622 mono_shared_area_instances (void **array, int count)
627 #endif // HAVE_SHM_OPEN
631 #ifndef HAVE_VALLOC_ALIGNED
633 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
635 /* Allocate twice the memory to be able to put the block on an aligned address */
636 char *mem = (char *) mono_valloc (NULL, size + alignment, flags, type);
642 aligned = aligned_address (mem, size, alignment);
645 mono_vfree (mem, aligned - mem, type);
646 if (aligned + size < mem + size + alignment)
647 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size), type);
654 mono_pages_not_faulted (void *addr, size_t size)
659 int pagesize = mono_pagesize ();
660 int npages = (size + pagesize - 1) / pagesize;
661 char *faulted = (char *) g_malloc0 (sizeof (char*) * npages);
664 * We cast `faulted` to void* because Linux wants an unsigned
665 * char* while BSD wants a char*.
668 if (mincore (addr, size, (unsigned char *)faulted) != 0) {
670 if (mincore (addr, size, (char *)faulted) != 0) {
675 for (i = 0; i < npages; ++i) {
676 if (faulted [i] != 0)