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 mono_valloc_granule (void)
161 return mono_pagesize ();
165 prot_from_flags (int flags
)
167 int prot
= PROT_NONE
;
168 /* translate the protection bits */
169 if (flags
& MONO_MMAP_READ
)
171 if (flags
& MONO_MMAP_WRITE
)
173 if (flags
& MONO_MMAP_EXEC
)
180 * @addr: memory address
181 * @length: memory area size
182 * @flags: protection flags
184 * Allocates @length bytes of virtual memory with the @flags
185 * protection. @addr can be a preferred memory address or a
186 * mandatory one if MONO_MMAP_FIXED is set in @flags.
187 * @addr must be pagesize aligned and can be NULL.
188 * @length must be a multiple of pagesize.
190 * Returns: NULL on failure, the address of the memory area otherwise
193 mono_valloc (void *addr
, size_t length
, int flags
, MonoMemAccountType type
)
197 int prot
= prot_from_flags (flags
);
198 /* translate the flags */
199 if (flags
& MONO_MMAP_FIXED
)
201 if (flags
& MONO_MMAP_32BIT
)
204 mflags
|= MAP_ANONYMOUS
;
205 mflags
|= MAP_PRIVATE
;
207 BEGIN_CRITICAL_SECTION
;
208 ptr
= mmap (addr
, length
, prot
, mflags
, -1, 0);
209 if (ptr
== MAP_FAILED
) {
210 int fd
= open ("/dev/zero", O_RDONLY
);
212 ptr
= mmap (addr
, length
, prot
, mflags
, fd
, 0);
216 END_CRITICAL_SECTION
;
218 if (ptr
== MAP_FAILED
)
221 account_mem (type
, (ssize_t
)length
);
228 * @addr: memory address returned by mono_valloc ()
229 * @length: size of memory area
231 * Remove the memory mapping at the address @addr.
233 * Returns: 0 on success.
236 mono_vfree (void *addr
, size_t length
, MonoMemAccountType type
)
239 BEGIN_CRITICAL_SECTION
;
240 res
= munmap (addr
, length
);
241 END_CRITICAL_SECTION
;
243 account_mem (type
, -(ssize_t
)length
);
250 * @length: size of data to map
251 * @flags: protection flags
252 * @fd: file descriptor
253 * @offset: offset in the file
254 * @ret_handle: pointer to storage for returning a handle for the map
256 * Map the area of the file pointed to by the file descriptor @fd, at offset
257 * @offset and of size @length in memory according to the protection flags
259 * @offset and @length must be multiples of the page size.
260 * @ret_handle must point to a void*: this value must be used when unmapping
261 * the memory area using mono_file_unmap ().
265 mono_file_map (size_t length
, int flags
, int fd
, guint64 offset
, void **ret_handle
)
269 int prot
= prot_from_flags (flags
);
270 /* translate the flags */
271 if (flags
& MONO_MMAP_PRIVATE
)
272 mflags
|= MAP_PRIVATE
;
273 if (flags
& MONO_MMAP_SHARED
)
274 mflags
|= MAP_SHARED
;
275 if (flags
& MONO_MMAP_FIXED
)
277 if (flags
& MONO_MMAP_32BIT
)
280 BEGIN_CRITICAL_SECTION
;
281 ptr
= mmap (0, length
, prot
, mflags
, fd
, offset
);
282 END_CRITICAL_SECTION
;
283 if (ptr
== MAP_FAILED
)
285 *ret_handle
= (void*)length
;
291 * @addr: memory address returned by mono_file_map ()
292 * @handle: handle of memory map
294 * Remove the memory mapping at the address @addr.
295 * @handle must be the value returned in ret_handle by mono_file_map ().
297 * Returns: 0 on success.
300 mono_file_unmap (void *addr
, void *handle
)
304 BEGIN_CRITICAL_SECTION
;
305 res
= munmap (addr
, (size_t)handle
);
306 END_CRITICAL_SECTION
;
313 * @addr: memory address
314 * @length: size of memory area
315 * @flags: new protection flags
317 * Change the protection for the memory area at @addr for @length bytes
318 * to matche the supplied @flags.
319 * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
320 * and the area is cleared to zero.
321 * @addr must be aligned to the page size.
322 * @length must be a multiple of the page size.
324 * Returns: 0 on success.
326 #if defined(__native_client__)
328 mono_mprotect (void *addr
, size_t length
, int flags
)
330 int prot
= prot_from_flags (flags
);
333 if (flags
& MONO_MMAP_DISCARD
) memset (addr
, 0, length
);
335 new_addr
= mmap(addr
, length
, prot
, MAP_PRIVATE
| MAP_FIXED
| MAP_ANONYMOUS
, -1, 0);
336 if (new_addr
== addr
) return 0;
341 mono_mprotect (void *addr
, size_t length
, int flags
)
343 int prot
= prot_from_flags (flags
);
345 if (flags
& MONO_MMAP_DISCARD
) {
346 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
348 if (madvise (addr
, length
, MADV_DONTNEED
))
349 memset (addr
, 0, length
);
351 memset (addr
, 0, length
);
353 madvise (addr
, length
, MADV_DONTNEED
);
354 madvise (addr
, length
, MADV_FREE
);
356 posix_madvise (addr
, length
, POSIX_MADV_DONTNEED
);
360 return mprotect (addr
, length
, prot
);
362 #endif // __native_client__
366 /* dummy malloc-based implementation */
374 mono_valloc_granule (void)
376 return mono_pagesize ();
380 mono_valloc (void *addr
, size_t length
, int flags
, MonoMemAccountType type
)
382 return g_malloc (length
);
386 mono_valloc_aligned (size_t size
, size_t alignment
, int flags
, MonoMemAccountType type
)
388 g_assert_not_reached ();
391 #define HAVE_VALLOC_ALIGNED
394 mono_vfree (void *addr
, size_t length
, MonoMemAccountType type
)
401 mono_mprotect (void *addr
, size_t length
, int flags
)
403 if (flags
& MONO_MMAP_DISCARD
) {
404 memset (addr
, 0, length
);
411 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
413 static int use_shared_area
;
416 shared_area_disabled (void)
418 if (!use_shared_area
) {
419 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
420 use_shared_area
= -1;
424 return use_shared_area
== -1;
428 mono_shared_area_instances_slow (void **array
, int count
, gboolean cleanup
)
433 gpointer
*processes
= mono_process_list (&num
);
434 for (i
= 0; i
< num
; ++i
) {
435 data
= mono_shared_area_for_pid (processes
[i
]);
438 mono_shared_area_unload (data
);
441 array
[j
++] = processes
[i
];
451 mono_shared_area_instances_helper (void **array
, int count
, gboolean cleanup
)
455 int curpid
= getpid ();
456 GDir
*dir
= g_dir_open ("/dev/shm/", 0, NULL
);
458 return mono_shared_area_instances_slow (array
, count
, cleanup
);
459 while ((name
= g_dir_read_name (dir
))) {
462 if (strncmp (name
, "mono.", 5))
464 pid
= strtol (name
+ 5, &nend
, 10);
465 if (pid
<= 0 || nend
== name
+ 5 || *nend
)
469 array
[i
++] = GINT_TO_POINTER (pid
);
473 if (curpid
!= pid
&& kill (pid
, 0) == -1 && (errno
== ESRCH
|| errno
== ENOMEM
)) {
475 g_snprintf (buf
, sizeof (buf
), "/mono.%d", pid
);
484 mono_shared_area (void)
488 /* we should allow the user to configure the size */
489 int size
= mono_pagesize ();
494 if (shared_area_disabled ()) {
495 if (!malloced_shared_area
)
496 malloced_shared_area
= malloc_shared_area (0);
497 /* get the pid here */
498 return malloced_shared_area
;
501 /* perform cleanup of segments left over from dead processes */
502 mono_shared_area_instances_helper (NULL
, 0, TRUE
);
504 g_snprintf (buf
, sizeof (buf
), "/mono.%d", pid
);
506 fd
= shm_open (buf
, O_CREAT
|O_EXCL
|O_RDWR
, S_IRUSR
|S_IWUSR
|S_IRGRP
);
507 if (fd
== -1 && errno
== EEXIST
) {
510 fd
= shm_open (buf
, O_CREAT
|O_EXCL
|O_RDWR
, S_IRUSR
|S_IWUSR
|S_IRGRP
);
512 /* in case of failure we try to return a memory area anyway,
513 * even if it means the data can't be read by other processes
516 return malloc_shared_area (pid
);
517 if (ftruncate (fd
, size
) != 0) {
521 BEGIN_CRITICAL_SECTION
;
522 res
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
523 END_CRITICAL_SECTION
;
525 if (res
== MAP_FAILED
) {
528 return malloc_shared_area (pid
);
530 /* we don't need the file descriptor anymore */
532 header
= (SAreaHeader
*) res
;
535 header
->stats_start
= sizeof (SAreaHeader
);
536 header
->stats_end
= sizeof (SAreaHeader
);
538 mono_atexit (mono_shared_area_remove
);
543 mono_shared_area_remove (void)
547 if (shared_area_disabled ()) {
548 if (malloced_shared_area
)
549 g_free (malloced_shared_area
);
553 g_snprintf (buf
, sizeof (buf
), "/mono.%d", getpid ());
555 if (malloced_shared_area
)
556 g_free (malloced_shared_area
);
560 mono_shared_area_for_pid (void *pid
)
563 /* we should allow the user to configure the size */
564 int size
= mono_pagesize ();
568 if (shared_area_disabled ())
571 g_snprintf (buf
, sizeof (buf
), "/mono.%d", GPOINTER_TO_INT (pid
));
573 fd
= shm_open (buf
, O_RDONLY
, S_IRUSR
|S_IRGRP
);
576 BEGIN_CRITICAL_SECTION
;
577 res
= mmap (NULL
, size
, PROT_READ
, MAP_SHARED
, fd
, 0);
578 END_CRITICAL_SECTION
;
580 if (res
== MAP_FAILED
) {
584 /* FIXME: validate the area */
585 /* we don't need the file descriptor anymore */
591 mono_shared_area_unload (void *area
)
593 /* FIXME: currently we load only a page */
594 BEGIN_CRITICAL_SECTION
;
595 munmap (area
, mono_pagesize ());
596 END_CRITICAL_SECTION
;
600 mono_shared_area_instances (void **array
, int count
)
602 return mono_shared_area_instances_helper (array
, count
, FALSE
);
606 mono_shared_area (void)
608 if (!malloced_shared_area
)
609 malloced_shared_area
= malloc_shared_area (getpid ());
610 /* get the pid here */
611 return malloced_shared_area
;
615 mono_shared_area_remove (void)
617 if (malloced_shared_area
)
618 g_free (malloced_shared_area
);
619 malloced_shared_area
= NULL
;
623 mono_shared_area_for_pid (void *pid
)
629 mono_shared_area_unload (void *area
)
634 mono_shared_area_instances (void **array
, int count
)
639 #endif // HAVE_SHM_OPEN
643 #ifndef HAVE_VALLOC_ALIGNED
645 mono_valloc_aligned (size_t size
, size_t alignment
, int flags
, MonoMemAccountType type
)
647 /* Allocate twice the memory to be able to put the block on an aligned address */
648 char *mem
= (char *) mono_valloc (NULL
, size
+ alignment
, flags
, type
);
654 aligned
= aligned_address (mem
, size
, alignment
);
657 mono_vfree (mem
, aligned
- mem
, type
);
658 if (aligned
+ size
< mem
+ size
+ alignment
)
659 mono_vfree (aligned
+ size
, (mem
+ size
+ alignment
) - (aligned
+ size
), type
);
666 mono_pages_not_faulted (void *addr
, size_t size
)
671 int pagesize
= mono_pagesize ();
672 int npages
= (size
+ pagesize
- 1) / pagesize
;
673 char *faulted
= (char *) g_malloc0 (sizeof (char*) * npages
);
676 * We cast `faulted` to void* because Linux wants an unsigned
677 * char* while BSD wants a char*.
680 if (mincore (addr
, size
, (unsigned char *)faulted
) != 0) {
682 if (mincore (addr
, size
, (char *)faulted
) != 0) {
687 for (i
= 0; i
< npages
; ++i
) {
688 if (faulted
[i
] != 0)