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>
22 #ifdef HAVE_SYS_SYSCTL_H
23 #include <sys/sysctl.h>
33 #endif /* !HOST_WIN32 */
35 #include "mono-mmap.h"
36 #include "mono-mmap-internals.h"
37 #include "mono-proclib.h"
38 #include <mono/utils/mono-threads.h>
39 #include <mono/utils/atomic.h>
40 #include <mono/utils/mono-counters.h>
42 #define BEGIN_CRITICAL_SECTION do { \
43 MonoThreadInfo *__info = mono_thread_info_current_unchecked (); \
44 if (__info) __info->inside_critical_region = TRUE; \
46 #define END_CRITICAL_SECTION \
47 if (__info) __info->inside_critical_region = FALSE; \
51 #define MAP_ANONYMOUS MAP_ANON
67 mono_malloc_shared_area (int pid
)
69 int size
= mono_pagesize ();
70 SAreaHeader
*sarea
= (SAreaHeader
*) g_malloc0 (size
);
73 sarea
->stats_start
= sizeof (SAreaHeader
);
74 sarea
->stats_end
= sizeof (SAreaHeader
);
80 mono_aligned_address (char *mem
, size_t size
, size_t alignment
)
82 char *aligned
= (char*)((size_t)(mem
+ (alignment
- 1)) & ~(alignment
- 1));
83 g_assert (aligned
>= mem
&& aligned
+ size
<= mem
+ size
+ alignment
&& !((size_t)aligned
& (alignment
- 1)));
87 static size_t allocation_count
[MONO_MEM_ACCOUNT_MAX
];
88 static size_t total_allocation_count
;
89 static size_t alloc_limit
;
92 mono_account_mem (MonoMemAccountType type
, ssize_t size
)
94 mono_atomic_fetch_add_word (&allocation_count
[type
], size
);
95 mono_atomic_fetch_add_word (&total_allocation_count
, size
);
99 mono_valloc_set_limit (size_t size
)
105 mono_valloc_can_alloc (size_t size
)
108 return (total_allocation_count
+ size
) < alloc_limit
;
113 mono_mem_account_type_name (MonoMemAccountType type
)
115 static const char *names
[] = {
124 "SGen shadow card table",
126 "SGen binary protocol",
136 mono_mem_account_register_counters (void)
138 for (int i
= 0; i
< MONO_MEM_ACCOUNT_MAX
; ++i
) {
139 const char *prefix
= "Valloc ";
140 const char *name
= mono_mem_account_type_name ((MonoMemAccountType
)i
);
142 g_assert (strlen (prefix
) + strlen (name
) < sizeof (descr
));
143 sprintf (descr
, "%s%s", prefix
, name
);
144 mono_counters_register (descr
, MONO_COUNTER_WORD
| MONO_COUNTER_RUNTIME
| MONO_COUNTER_BYTES
| MONO_COUNTER_VARIABLE
, (void*)&allocation_count
[i
]);
149 // Windows specific implementation in mono-mmap-windows.c
150 #define HAVE_VALLOC_ALIGNED
152 #elif defined(HOST_WASM)
153 // WebAssembly implementation in mono-mmap-wasm.c
154 #define HAVE_VALLOC_ALIGNED
158 static void* malloced_shared_area
= NULL
;
159 #if defined(HAVE_MMAP)
163 * Get the page size in use on the system. Addresses and sizes in the
164 * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
167 * Returns: the page size in bytes.
172 static int saved_pagesize
= 0;
175 return saved_pagesize
;
177 // Prefer sysconf () as it's signal safe.
178 #if defined (HAVE_SYSCONF) && defined (_SC_PAGESIZE)
179 saved_pagesize
= sysconf (_SC_PAGESIZE
);
181 saved_pagesize
= getpagesize ();
185 // While this could not happen in any of the Mono supported
186 // systems, this ensures this function never returns -1, and
187 // reduces the number of false positives
188 // that Coverity finds in consumer code.
190 if (saved_pagesize
== -1)
193 return saved_pagesize
;
197 mono_valloc_granule (void)
199 return mono_pagesize ();
203 prot_from_flags (int flags
)
205 int prot
= PROT_NONE
;
206 /* translate the protection bits */
207 if (flags
& MONO_MMAP_READ
)
209 if (flags
& MONO_MMAP_WRITE
)
211 if (flags
& MONO_MMAP_EXEC
)
216 #if defined(__APPLE__)
218 #define DARWIN_VERSION_MOJAVE 18
221 get_darwin_version (void)
223 static guint32 version
;
225 /* This doesn't need locking */
228 size_t size
= sizeof(str
);
229 int err
= sysctlbyname("kern.osrelease", str
, &size
, NULL
, 0);
231 err
= sscanf (str
, "%d", &version
);
233 g_assert (version
> 0);
239 static int use_mmap_jit
;
243 * \param flag indicating whether to enable or disable the use of MAP_JIT in mmap
245 * Call this method to enable or disable the use of MAP_JIT to create the pages
246 * for the JIT to use. This is only needed for scenarios where Mono is bundled
250 mono_setmmapjit (int flag
)
257 * \param addr memory address
258 * \param length memory area size
259 * \param flags protection flags
260 * Allocates \p length bytes of virtual memory with the \p flags
261 * protection. \p addr can be a preferred memory address or a
262 * mandatory one if MONO_MMAP_FIXED is set in \p flags.
263 * \p addr must be pagesize aligned and can be NULL.
264 * \p length must be a multiple of pagesize.
265 * \returns NULL on failure, the address of the memory area otherwise
268 mono_valloc (void *addr
, size_t length
, int flags
, MonoMemAccountType type
)
272 int prot
= prot_from_flags (flags
);
274 if (!mono_valloc_can_alloc (length
))
277 /* translate the flags */
278 if (flags
& MONO_MMAP_FIXED
)
280 if (flags
& MONO_MMAP_32BIT
)
285 /* emscripten throws an exception on 0 length */
289 #if defined(__APPLE__) && defined(MAP_JIT)
290 if (get_darwin_version () >= DARWIN_VERSION_MOJAVE
) {
291 /* Check for hardened runtime */
292 static int is_hardened_runtime
;
294 if (is_hardened_runtime
== 0 && !use_mmap_jit
) {
295 ptr
= mmap (NULL
, getpagesize (), PROT_WRITE
|PROT_EXEC
, MAP_PRIVATE
|MAP_ANONYMOUS
, -1, 0);
296 if (ptr
== MAP_FAILED
) {
297 is_hardened_runtime
= 1;
299 is_hardened_runtime
= 2;
300 munmap (ptr
, getpagesize ());
303 if ((flags
& MONO_MMAP_JIT
) && (use_mmap_jit
|| is_hardened_runtime
== 1))
308 mflags
|= MAP_ANONYMOUS
;
309 mflags
|= MAP_PRIVATE
;
311 BEGIN_CRITICAL_SECTION
;
312 ptr
= mmap (addr
, length
, prot
, mflags
, -1, 0);
313 if (ptr
== MAP_FAILED
) {
314 int fd
= open ("/dev/zero", O_RDONLY
);
316 ptr
= mmap (addr
, length
, prot
, mflags
, fd
, 0);
320 END_CRITICAL_SECTION
;
322 if (ptr
== MAP_FAILED
)
325 mono_account_mem (type
, (ssize_t
)length
);
332 * \param addr memory address returned by mono_valloc ()
333 * \param length size of memory area
334 * Remove the memory mapping at the address \p addr.
335 * \returns \c 0 on success.
338 mono_vfree (void *addr
, size_t length
, MonoMemAccountType type
)
341 BEGIN_CRITICAL_SECTION
;
342 res
= munmap (addr
, length
);
343 END_CRITICAL_SECTION
;
345 mono_account_mem (type
, -(ssize_t
)length
);
352 * \param length size of data to map
353 * \param flags protection flags
354 * \param fd file descriptor
355 * \param offset offset in the file
356 * \param ret_handle pointer to storage for returning a handle for the map
357 * Map the area of the file pointed to by the file descriptor \p fd, at offset
358 * \p offset and of size \p length in memory according to the protection flags
360 * \p offset and \p length must be multiples of the page size.
361 * \p ret_handle must point to a void*: this value must be used when unmapping
362 * the memory area using \c mono_file_unmap().
365 mono_file_map (size_t length
, int flags
, int fd
, guint64 offset
, void **ret_handle
)
367 return mono_file_map_error (length
, flags
, fd
, offset
, ret_handle
, NULL
, NULL
);
371 mono_file_map_error (size_t length
, int flags
, int fd
, guint64 offset
, void **ret_handle
,
372 const char *filepath
, char **error_message
)
376 int prot
= prot_from_flags (flags
);
377 /* translate the flags */
378 if (flags
& MONO_MMAP_PRIVATE
)
379 mflags
|= MAP_PRIVATE
;
380 if (flags
& MONO_MMAP_SHARED
)
381 mflags
|= MAP_SHARED
;
382 if (flags
& MONO_MMAP_FIXED
)
384 if (flags
& MONO_MMAP_32BIT
)
389 /* emscripten throws an exception on 0 length */
390 *error_message
= g_stdrup_printf ("%s failed file:%s length:0x%zx offset:0x%lluX error:%s\n",
391 __func__
, filepath
? filepath
: "", length
, offset
, "mmaps of zero length are not permitted with emscripten");
395 // No GC safe transition because this is called early in main.c
396 BEGIN_CRITICAL_SECTION
;
397 ptr
= mmap (0, length
, prot
, mflags
, fd
, offset
);
398 END_CRITICAL_SECTION
;
399 if (ptr
== MAP_FAILED
) {
401 *error_message
= g_strdup_printf ("%s failed file:%s length:0x%zX offset:0x%lluX error:%s(0x%X)\n",
402 __func__
, filepath
? filepath
: "", length
, offset
, g_strerror (errno
), errno
);
406 *ret_handle
= (void*)length
;
412 * \param addr memory address returned by mono_file_map ()
413 * \param handle handle of memory map
414 * Remove the memory mapping at the address \p addr.
415 * \p handle must be the value returned in ret_handle by \c mono_file_map().
416 * \returns \c 0 on success.
419 mono_file_unmap (void *addr
, void *handle
)
423 // No GC safe transition because this is called early in driver.c via mono_debug_init (with a few layers of indirection)
424 BEGIN_CRITICAL_SECTION
;
425 res
= munmap (addr
, (size_t)handle
);
426 END_CRITICAL_SECTION
;
433 * \param addr memory address
434 * \param length size of memory area
435 * \param flags new protection flags
436 * Change the protection for the memory area at \p addr for \p length bytes
437 * to matche the supplied \p flags.
438 * If \p flags includes MON_MMAP_DISCARD the pages are discarded from memory
439 * and the area is cleared to zero.
440 * \p addr must be aligned to the page size.
441 * \p length must be a multiple of the page size.
442 * \returns \c 0 on success.
445 mono_mprotect (void *addr
, size_t length
, int flags
)
447 int prot
= prot_from_flags (flags
);
449 if (flags
& MONO_MMAP_DISCARD
) {
450 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
452 if (madvise (addr
, length
, MADV_DONTNEED
))
453 memset (addr
, 0, length
);
455 memset (addr
, 0, length
);
456 /* some OSes (like AIX) have madvise but no MADV_FREE */
457 #if defined(HAVE_MADVISE) && defined(MADV_FREE)
458 madvise (addr
, length
, MADV_DONTNEED
);
459 madvise (addr
, length
, MADV_FREE
);
461 posix_madvise (addr
, length
, POSIX_MADV_DONTNEED
);
465 // No GC safe transition because this is called early in mini_init via mono_arch_init (with a few layers of indirection)
466 return mprotect (addr
, length
, prot
);
471 /* dummy malloc-based implementation */
479 mono_valloc_granule (void)
481 return mono_pagesize ();
485 mono_valloc (void *addr
, size_t length
, int flags
, MonoMemAccountType type
)
487 g_assert (addr
== NULL
);
488 return mono_valloc_aligned (length
, mono_pagesize (), flags
, type
);
492 mono_valloc_aligned (size_t size
, size_t alignment
, int flags
, MonoMemAccountType type
)
495 if (posix_memalign (&res
, alignment
, size
))
498 memset (res
, 0, size
);
502 #define HAVE_VALLOC_ALIGNED
505 mono_vfree (void *addr
, size_t length
, MonoMemAccountType type
)
512 mono_mprotect (void *addr
, size_t length
, int flags
)
514 if (flags
& MONO_MMAP_DISCARD
) {
515 memset (addr
, 0, length
);
522 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
524 static int use_shared_area
;
527 shared_area_disabled (void)
529 if (!use_shared_area
) {
530 if (g_hasenv ("MONO_DISABLE_SHARED_AREA"))
531 use_shared_area
= -1;
535 return use_shared_area
== -1;
539 mono_shared_area_instances_slow (void **array
, int count
, gboolean cleanup
)
544 gpointer
*processes
= mono_process_list (&num
);
545 for (i
= 0; i
< num
; ++i
) {
546 data
= mono_shared_area_for_pid (processes
[i
]);
549 mono_shared_area_unload (data
);
552 array
[j
++] = processes
[i
];
562 mono_shared_area_instances_helper (void **array
, int count
, gboolean cleanup
)
566 int curpid
= getpid ();
567 GDir
*dir
= g_dir_open ("/dev/shm/", 0, NULL
);
569 return mono_shared_area_instances_slow (array
, count
, cleanup
);
570 while ((name
= g_dir_read_name (dir
))) {
573 if (strncmp (name
, "mono.", 5))
575 pid
= strtol (name
+ 5, &nend
, 10);
576 if (pid
<= 0 || nend
== name
+ 5 || *nend
)
580 array
[i
++] = GINT_TO_POINTER (pid
);
584 if (curpid
!= pid
&& kill (pid
, 0) == -1 && (errno
== ESRCH
|| errno
== ENOMEM
)) {
586 g_snprintf (buf
, sizeof (buf
), "/mono.%d", pid
);
595 mono_shared_area (void)
599 /* we should allow the user to configure the size */
600 int size
= mono_pagesize ();
605 if (shared_area_disabled ()) {
606 if (!malloced_shared_area
)
607 malloced_shared_area
= mono_malloc_shared_area (0);
608 /* get the pid here */
609 return malloced_shared_area
;
612 /* perform cleanup of segments left over from dead processes */
613 mono_shared_area_instances_helper (NULL
, 0, TRUE
);
615 g_snprintf (buf
, sizeof (buf
), "/mono.%d", pid
);
617 fd
= shm_open (buf
, O_CREAT
|O_EXCL
|O_RDWR
, S_IRUSR
|S_IWUSR
|S_IRGRP
);
618 if (fd
== -1 && errno
== EEXIST
) {
621 fd
= shm_open (buf
, O_CREAT
|O_EXCL
|O_RDWR
, S_IRUSR
|S_IWUSR
|S_IRGRP
);
623 /* in case of failure we try to return a memory area anyway,
624 * even if it means the data can't be read by other processes
627 return mono_malloc_shared_area (pid
);
628 if (ftruncate (fd
, size
) != 0) {
632 BEGIN_CRITICAL_SECTION
;
633 res
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
634 END_CRITICAL_SECTION
;
636 if (res
== MAP_FAILED
) {
639 return mono_malloc_shared_area (pid
);
641 /* we don't need the file descriptor anymore */
643 header
= (SAreaHeader
*) res
;
646 header
->stats_start
= sizeof (SAreaHeader
);
647 header
->stats_end
= sizeof (SAreaHeader
);
649 mono_atexit (mono_shared_area_remove
);
654 mono_shared_area_remove (void)
658 if (shared_area_disabled ()) {
659 if (malloced_shared_area
)
660 g_free (malloced_shared_area
);
664 g_snprintf (buf
, sizeof (buf
), "/mono.%d", getpid ());
666 if (malloced_shared_area
)
667 g_free (malloced_shared_area
);
671 mono_shared_area_for_pid (void *pid
)
674 /* we should allow the user to configure the size */
675 int size
= mono_pagesize ();
679 if (shared_area_disabled ())
682 g_snprintf (buf
, sizeof (buf
), "/mono.%d", GPOINTER_TO_INT (pid
));
684 fd
= shm_open (buf
, O_RDONLY
, S_IRUSR
|S_IRGRP
);
687 BEGIN_CRITICAL_SECTION
;
688 res
= mmap (NULL
, size
, PROT_READ
, MAP_SHARED
, fd
, 0);
689 END_CRITICAL_SECTION
;
691 if (res
== MAP_FAILED
) {
695 /* FIXME: validate the area */
696 /* we don't need the file descriptor anymore */
702 mono_shared_area_unload (void *area
)
704 /* FIXME: currently we load only a page */
705 BEGIN_CRITICAL_SECTION
;
706 munmap (area
, mono_pagesize ());
707 END_CRITICAL_SECTION
;
711 mono_shared_area_instances (void **array
, int count
)
713 return mono_shared_area_instances_helper (array
, count
, FALSE
);
717 mono_shared_area (void)
719 if (!malloced_shared_area
)
720 malloced_shared_area
= mono_malloc_shared_area (getpid ());
721 /* get the pid here */
722 return malloced_shared_area
;
726 mono_shared_area_remove (void)
728 if (malloced_shared_area
)
729 g_free (malloced_shared_area
);
730 malloced_shared_area
= NULL
;
734 mono_shared_area_for_pid (void *pid
)
740 mono_shared_area_unload (void *area
)
745 mono_shared_area_instances (void **array
, int count
)
750 #endif // HAVE_SHM_OPEN
754 #ifndef HAVE_VALLOC_ALIGNED
756 mono_valloc_aligned (size_t size
, size_t alignment
, int flags
, MonoMemAccountType type
)
758 /* Allocate twice the memory to be able to put the block on an aligned address */
759 char *mem
= (char *) mono_valloc (NULL
, size
+ alignment
, flags
, type
);
765 aligned
= mono_aligned_address (mem
, size
, alignment
);
768 mono_vfree (mem
, aligned
- mem
, type
);
769 if (aligned
+ size
< mem
+ size
+ alignment
)
770 mono_vfree (aligned
+ size
, (mem
+ size
+ alignment
) - (aligned
+ size
), type
);