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 mono_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 mono_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 mono_account_mem (MonoMemAccountType type
, ssize_t size
)
91 mono_atomic_fetch_add_word (&allocation_count
[type
], size
);
92 mono_atomic_fetch_add_word (&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;
168 return saved_pagesize
;
170 // Prefer sysconf () as it's signal safe.
171 #if defined (HAVE_SYSCONF) && defined (_SC_PAGESIZE)
172 saved_pagesize
= sysconf (_SC_PAGESIZE
);
174 saved_pagesize
= getpagesize ();
177 return saved_pagesize
;
181 mono_valloc_granule (void)
183 return mono_pagesize ();
187 prot_from_flags (int flags
)
189 int prot
= PROT_NONE
;
190 /* translate the protection bits */
191 if (flags
& MONO_MMAP_READ
)
193 if (flags
& MONO_MMAP_WRITE
)
195 if (flags
& MONO_MMAP_EXEC
)
202 * \param addr memory address
203 * \param length memory area size
204 * \param flags protection flags
205 * Allocates \p length bytes of virtual memory with the \p flags
206 * protection. \p addr can be a preferred memory address or a
207 * mandatory one if MONO_MMAP_FIXED is set in \p flags.
208 * \p addr must be pagesize aligned and can be NULL.
209 * \p length must be a multiple of pagesize.
210 * \returns NULL on failure, the address of the memory area otherwise
213 mono_valloc (void *addr
, size_t length
, int flags
, MonoMemAccountType type
)
217 int prot
= prot_from_flags (flags
);
219 if (!mono_valloc_can_alloc (length
))
222 /* translate the flags */
223 if (flags
& MONO_MMAP_FIXED
)
225 if (flags
& MONO_MMAP_32BIT
)
228 mflags
|= MAP_ANONYMOUS
;
229 mflags
|= MAP_PRIVATE
;
231 BEGIN_CRITICAL_SECTION
;
232 ptr
= mmap (addr
, length
, prot
, mflags
, -1, 0);
233 if (ptr
== MAP_FAILED
) {
234 int fd
= open ("/dev/zero", O_RDONLY
);
236 ptr
= mmap (addr
, length
, prot
, mflags
, fd
, 0);
240 END_CRITICAL_SECTION
;
242 if (ptr
== MAP_FAILED
)
245 mono_account_mem (type
, (ssize_t
)length
);
252 * \param addr memory address returned by mono_valloc ()
253 * \param length size of memory area
254 * Remove the memory mapping at the address \p addr.
255 * \returns \c 0 on success.
258 mono_vfree (void *addr
, size_t length
, MonoMemAccountType type
)
261 BEGIN_CRITICAL_SECTION
;
262 res
= munmap (addr
, length
);
263 END_CRITICAL_SECTION
;
265 mono_account_mem (type
, -(ssize_t
)length
);
272 * \param length size of data to map
273 * \param flags protection flags
274 * \param fd file descriptor
275 * \param offset offset in the file
276 * \param ret_handle pointer to storage for returning a handle for the map
277 * Map the area of the file pointed to by the file descriptor \p fd, at offset
278 * \p offset and of size \p length in memory according to the protection flags
280 * \p offset and \p length must be multiples of the page size.
281 * \p ret_handle must point to a void*: this value must be used when unmapping
282 * the memory area using \c mono_file_unmap().
285 mono_file_map (size_t length
, int flags
, int fd
, guint64 offset
, void **ret_handle
)
289 int prot
= prot_from_flags (flags
);
290 /* translate the flags */
291 if (flags
& MONO_MMAP_PRIVATE
)
292 mflags
|= MAP_PRIVATE
;
293 if (flags
& MONO_MMAP_SHARED
)
294 mflags
|= MAP_SHARED
;
295 if (flags
& MONO_MMAP_FIXED
)
297 if (flags
& MONO_MMAP_32BIT
)
300 BEGIN_CRITICAL_SECTION
;
301 ptr
= mmap (0, length
, prot
, mflags
, fd
, offset
);
302 END_CRITICAL_SECTION
;
303 if (ptr
== MAP_FAILED
)
305 *ret_handle
= (void*)length
;
311 * \param addr memory address returned by mono_file_map ()
312 * \param handle handle of memory map
313 * Remove the memory mapping at the address \p addr.
314 * \p handle must be the value returned in ret_handle by \c mono_file_map().
315 * \returns \c 0 on success.
318 mono_file_unmap (void *addr
, void *handle
)
322 BEGIN_CRITICAL_SECTION
;
323 res
= munmap (addr
, (size_t)handle
);
324 END_CRITICAL_SECTION
;
331 * \param addr memory address
332 * \param length size of memory area
333 * \param flags new protection flags
334 * Change the protection for the memory area at \p addr for \p length bytes
335 * to matche the supplied \p flags.
336 * If \p flags includes MON_MMAP_DISCARD the pages are discarded from memory
337 * and the area is cleared to zero.
338 * \p addr must be aligned to the page size.
339 * \p length must be a multiple of the page size.
340 * \returns \c 0 on success.
343 mono_mprotect (void *addr
, size_t length
, int flags
)
345 int prot
= prot_from_flags (flags
);
347 if (flags
& MONO_MMAP_DISCARD
) {
348 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
350 if (madvise (addr
, length
, MADV_DONTNEED
))
351 memset (addr
, 0, length
);
353 memset (addr
, 0, length
);
354 /* some OSes (like AIX) have madvise but no MADV_FREE */
355 #if defined(HAVE_MADVISE) && defined(MADV_FREE)
356 madvise (addr
, length
, MADV_DONTNEED
);
357 madvise (addr
, length
, MADV_FREE
);
359 posix_madvise (addr
, length
, POSIX_MADV_DONTNEED
);
363 return mprotect (addr
, length
, prot
);
368 /* dummy malloc-based implementation */
376 mono_valloc_granule (void)
378 return mono_pagesize ();
382 mono_valloc (void *addr
, size_t length
, int flags
, MonoMemAccountType type
)
384 g_assert (addr
== NULL
);
385 return mono_valloc_aligned (length
, mono_pagesize (), flags
, type
);
389 mono_valloc_aligned (size_t size
, size_t alignment
, int flags
, MonoMemAccountType type
)
392 if (posix_memalign (&res
, alignment
, size
))
395 memset (res
, 0, size
);
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_hasenv ("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
= mono_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 mono_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 mono_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
= mono_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
= mono_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)