3 * Support for mapping code into the process address space
6 * Mono Team (mono-list@lists.ximian.com)
8 * Copyright 2001-2008 Novell, Inc.
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
15 #include <sys/types.h>
30 #endif /* !HOST_WIN32 */
32 #include "mono-mmap.h"
33 #include "mono-mmap-internals.h"
34 #include "mono-proclib.h"
35 #include <mono/utils/mono-threads.h>
36 #include <mono/utils/atomic.h>
37 #include <mono/utils/mono-counters.h>
39 #define BEGIN_CRITICAL_SECTION do { \
40 MonoThreadInfo *__info = mono_thread_info_current_unchecked (); \
41 if (__info) __info->inside_critical_region = TRUE; \
43 #define END_CRITICAL_SECTION \
44 if (__info) __info->inside_critical_region = FALSE; \
48 #define MAP_ANONYMOUS MAP_ANON
64 malloc_shared_area (int pid)
66 int size = mono_pagesize ();
67 SAreaHeader *sarea = (SAreaHeader *) g_malloc0 (size);
70 sarea->stats_start = sizeof (SAreaHeader);
71 sarea->stats_end = sizeof (SAreaHeader);
77 aligned_address (char *mem, size_t size, size_t alignment)
79 char *aligned = (char*)((size_t)(mem + (alignment - 1)) & ~(alignment - 1));
80 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((size_t)aligned & (alignment - 1)));
84 static size_t allocation_count [MONO_MEM_ACCOUNT_MAX];
85 static size_t total_allocation_count;
86 static size_t alloc_limit;
89 account_mem (MonoMemAccountType type, ssize_t size)
91 InterlockedAddP (&allocation_count [type], size);
92 InterlockedAddP (&total_allocation_count, size);
96 mono_valloc_set_limit (size_t size)
102 mono_valloc_can_alloc (size_t size)
105 return (total_allocation_count + size) < alloc_limit;
110 mono_mem_account_type_name (MonoMemAccountType type)
112 static const char *names[] = {
121 "SGen shadow card table",
123 "SGen binary protocol",
133 mono_mem_account_register_counters (void)
135 for (int i = 0; i < MONO_MEM_ACCOUNT_MAX; ++i) {
136 const char *prefix = "Valloc ";
137 const char *name = mono_mem_account_type_name (i);
139 g_assert (strlen (prefix) + strlen (name) < sizeof (descr));
140 sprintf (descr, "%s%s", prefix, name);
141 mono_counters_register (descr, MONO_COUNTER_WORD | MONO_COUNTER_RUNTIME | MONO_COUNTER_BYTES | MONO_COUNTER_VARIABLE, (void*)&allocation_count [i]);
146 // Windows specific implementation in mono-mmap-windows.c
147 #define HAVE_VALLOC_ALIGNED
151 static void* malloced_shared_area = NULL;
152 #if defined(HAVE_MMAP)
156 * Get the page size in use on the system. Addresses and sizes in the
157 * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
160 * Returns: the page size in bytes.
165 static int saved_pagesize = 0;
167 return saved_pagesize;
168 saved_pagesize = getpagesize ();
169 return saved_pagesize;
173 mono_valloc_granule (void)
175 return mono_pagesize ();
179 prot_from_flags (int flags)
181 int prot = PROT_NONE;
182 /* translate the protection bits */
183 if (flags & MONO_MMAP_READ)
185 if (flags & MONO_MMAP_WRITE)
187 if (flags & MONO_MMAP_EXEC)
194 * \param addr memory address
195 * \param length memory area size
196 * \param flags protection flags
197 * Allocates \p length bytes of virtual memory with the \p flags
198 * protection. \p addr can be a preferred memory address or a
199 * mandatory one if MONO_MMAP_FIXED is set in \p flags.
200 * \p addr must be pagesize aligned and can be NULL.
201 * \p length must be a multiple of pagesize.
202 * \returns NULL on failure, the address of the memory area otherwise
205 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
209 int prot = prot_from_flags (flags);
211 if (!mono_valloc_can_alloc (length))
214 /* translate the flags */
215 if (flags & MONO_MMAP_FIXED)
217 if (flags & MONO_MMAP_32BIT)
220 mflags |= MAP_ANONYMOUS;
221 mflags |= MAP_PRIVATE;
223 BEGIN_CRITICAL_SECTION;
224 ptr = mmap (addr, length, prot, mflags, -1, 0);
225 if (ptr == MAP_FAILED) {
226 int fd = open ("/dev/zero", O_RDONLY);
228 ptr = mmap (addr, length, prot, mflags, fd, 0);
232 END_CRITICAL_SECTION;
234 if (ptr == MAP_FAILED)
237 account_mem (type, (ssize_t)length);
244 * \param addr memory address returned by mono_valloc ()
245 * \param length size of memory area
246 * Remove the memory mapping at the address \p addr.
247 * \returns \c 0 on success.
250 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
253 BEGIN_CRITICAL_SECTION;
254 res = munmap (addr, length);
255 END_CRITICAL_SECTION;
257 account_mem (type, -(ssize_t)length);
264 * \param length size of data to map
265 * \param flags protection flags
266 * \param fd file descriptor
267 * \param offset offset in the file
268 * \param ret_handle pointer to storage for returning a handle for the map
269 * Map the area of the file pointed to by the file descriptor \p fd, at offset
270 * \p offset and of size \p length in memory according to the protection flags
272 * \p offset and \p length must be multiples of the page size.
273 * \p ret_handle must point to a void*: this value must be used when unmapping
274 * the memory area using \c mono_file_unmap().
277 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
281 int prot = prot_from_flags (flags);
282 /* translate the flags */
283 if (flags & MONO_MMAP_PRIVATE)
284 mflags |= MAP_PRIVATE;
285 if (flags & MONO_MMAP_SHARED)
286 mflags |= MAP_SHARED;
287 if (flags & MONO_MMAP_FIXED)
289 if (flags & MONO_MMAP_32BIT)
292 BEGIN_CRITICAL_SECTION;
293 ptr = mmap (0, length, prot, mflags, fd, offset);
294 END_CRITICAL_SECTION;
295 if (ptr == MAP_FAILED)
297 *ret_handle = (void*)length;
303 * \param addr memory address returned by mono_file_map ()
304 * \param handle handle of memory map
305 * Remove the memory mapping at the address \p addr.
306 * \p handle must be the value returned in ret_handle by \c mono_file_map().
307 * \returns \c 0 on success.
310 mono_file_unmap (void *addr, void *handle)
314 BEGIN_CRITICAL_SECTION;
315 res = munmap (addr, (size_t)handle);
316 END_CRITICAL_SECTION;
323 * \param addr memory address
324 * \param length size of memory area
325 * \param flags new protection flags
326 * Change the protection for the memory area at \p addr for \p length bytes
327 * to matche the supplied \p flags.
328 * If \p flags includes MON_MMAP_DISCARD the pages are discarded from memory
329 * and the area is cleared to zero.
330 * \p addr must be aligned to the page size.
331 * \p length must be a multiple of the page size.
332 * \returns \c 0 on success.
334 #if defined(__native_client__)
336 mono_mprotect (void *addr, size_t length, int flags)
338 int prot = prot_from_flags (flags);
341 if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
343 new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
344 if (new_addr == addr) return 0;
349 mono_mprotect (void *addr, size_t length, int flags)
351 int prot = prot_from_flags (flags);
353 if (flags & MONO_MMAP_DISCARD) {
354 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
356 if (madvise (addr, length, MADV_DONTNEED))
357 memset (addr, 0, length);
359 memset (addr, 0, length);
361 madvise (addr, length, MADV_DONTNEED);
362 madvise (addr, length, MADV_FREE);
364 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
368 return mprotect (addr, length, prot);
370 #endif // __native_client__
374 /* dummy malloc-based implementation */
382 mono_valloc_granule (void)
384 return mono_pagesize ();
388 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
390 return g_malloc (length);
394 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
396 g_assert_not_reached ();
399 #define HAVE_VALLOC_ALIGNED
402 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
409 mono_mprotect (void *addr, size_t length, int flags)
411 if (flags & MONO_MMAP_DISCARD) {
412 memset (addr, 0, length);
419 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
421 static int use_shared_area;
424 shared_area_disabled (void)
426 if (!use_shared_area) {
427 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
428 use_shared_area = -1;
432 return use_shared_area == -1;
436 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
441 gpointer *processes = mono_process_list (&num);
442 for (i = 0; i < num; ++i) {
443 data = mono_shared_area_for_pid (processes [i]);
446 mono_shared_area_unload (data);
449 array [j++] = processes [i];
459 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
463 int curpid = getpid ();
464 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
466 return mono_shared_area_instances_slow (array, count, cleanup);
467 while ((name = g_dir_read_name (dir))) {
470 if (strncmp (name, "mono.", 5))
472 pid = strtol (name + 5, &nend, 10);
473 if (pid <= 0 || nend == name + 5 || *nend)
477 array [i++] = GINT_TO_POINTER (pid);
481 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
483 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
492 mono_shared_area (void)
496 /* we should allow the user to configure the size */
497 int size = mono_pagesize ();
502 if (shared_area_disabled ()) {
503 if (!malloced_shared_area)
504 malloced_shared_area = malloc_shared_area (0);
505 /* get the pid here */
506 return malloced_shared_area;
509 /* perform cleanup of segments left over from dead processes */
510 mono_shared_area_instances_helper (NULL, 0, TRUE);
512 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
514 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
515 if (fd == -1 && errno == EEXIST) {
518 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
520 /* in case of failure we try to return a memory area anyway,
521 * even if it means the data can't be read by other processes
524 return malloc_shared_area (pid);
525 if (ftruncate (fd, size) != 0) {
529 BEGIN_CRITICAL_SECTION;
530 res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
531 END_CRITICAL_SECTION;
533 if (res == MAP_FAILED) {
536 return malloc_shared_area (pid);
538 /* we don't need the file descriptor anymore */
540 header = (SAreaHeader *) res;
543 header->stats_start = sizeof (SAreaHeader);
544 header->stats_end = sizeof (SAreaHeader);
546 mono_atexit (mono_shared_area_remove);
551 mono_shared_area_remove (void)
555 if (shared_area_disabled ()) {
556 if (malloced_shared_area)
557 g_free (malloced_shared_area);
561 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
563 if (malloced_shared_area)
564 g_free (malloced_shared_area);
568 mono_shared_area_for_pid (void *pid)
571 /* we should allow the user to configure the size */
572 int size = mono_pagesize ();
576 if (shared_area_disabled ())
579 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
581 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
584 BEGIN_CRITICAL_SECTION;
585 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
586 END_CRITICAL_SECTION;
588 if (res == MAP_FAILED) {
592 /* FIXME: validate the area */
593 /* we don't need the file descriptor anymore */
599 mono_shared_area_unload (void *area)
601 /* FIXME: currently we load only a page */
602 BEGIN_CRITICAL_SECTION;
603 munmap (area, mono_pagesize ());
604 END_CRITICAL_SECTION;
608 mono_shared_area_instances (void **array, int count)
610 return mono_shared_area_instances_helper (array, count, FALSE);
614 mono_shared_area (void)
616 if (!malloced_shared_area)
617 malloced_shared_area = malloc_shared_area (getpid ());
618 /* get the pid here */
619 return malloced_shared_area;
623 mono_shared_area_remove (void)
625 if (malloced_shared_area)
626 g_free (malloced_shared_area);
627 malloced_shared_area = NULL;
631 mono_shared_area_for_pid (void *pid)
637 mono_shared_area_unload (void *area)
642 mono_shared_area_instances (void **array, int count)
647 #endif // HAVE_SHM_OPEN
651 #ifndef HAVE_VALLOC_ALIGNED
653 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
655 /* Allocate twice the memory to be able to put the block on an aligned address */
656 char *mem = (char *) mono_valloc (NULL, size + alignment, flags, type);
662 aligned = aligned_address (mem, size, alignment);
665 mono_vfree (mem, aligned - mem, type);
666 if (aligned + size < mem + size + alignment)
667 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size), type);
674 mono_pages_not_faulted (void *addr, size_t size)
679 int pagesize = mono_pagesize ();
680 int npages = (size + pagesize - 1) / pagesize;
681 char *faulted = (char *) g_malloc0 (sizeof (char*) * npages);
684 * We cast `faulted` to void* because Linux wants an unsigned
685 * char* while BSD wants a char*.
688 if (mincore (addr, size, (unsigned char *)faulted) != 0) {
690 if (mincore (addr, size, (char *)faulted) != 0) {
695 for (i = 0; i < npages; ++i) {
696 if (faulted [i] != 0)