Revert some changes which don't have proper dependencies.
[mono-project.git] / mono / utils / mono-mmap.c
blobeadfb0f761812855c3c337b836dff806255ad4f5
1 /**
2 * \file
3 * Support for mapping code into the process address space
5 * Author:
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.
12 #include <config.h>
14 #ifndef HOST_WIN32
15 #include <sys/types.h>
16 #if HAVE_SYS_STAT_H
17 #include <sys/stat.h>
18 #endif
19 #if HAVE_SYS_MMAN_H
20 #include <sys/mman.h>
21 #endif
22 #ifdef HAVE_SYS_SYSCTL_H
23 #include <sys/sysctl.h>
24 #endif
25 #ifdef HAVE_SIGNAL_H
26 #include <signal.h>
27 #endif
28 #include <fcntl.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <errno.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; \
48 } while (0) \
50 #ifndef MAP_ANONYMOUS
51 #define MAP_ANONYMOUS MAP_ANON
52 #endif
54 #ifndef MAP_32BIT
55 #define MAP_32BIT 0
56 #endif
58 typedef struct {
59 int size;
60 int pid;
61 int reserved;
62 short stats_start;
63 short stats_end;
64 } SAreaHeader;
66 void*
67 mono_malloc_shared_area (int pid)
69 int size = mono_pagesize ();
70 SAreaHeader *sarea = (SAreaHeader *) g_malloc0 (size);
71 sarea->size = size;
72 sarea->pid = pid;
73 sarea->stats_start = sizeof (SAreaHeader);
74 sarea->stats_end = sizeof (SAreaHeader);
76 return sarea;
79 char*
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)));
84 return aligned;
87 static size_t allocation_count [MONO_MEM_ACCOUNT_MAX];
88 static size_t total_allocation_count;
89 static size_t alloc_limit;
91 void
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);
98 void
99 mono_valloc_set_limit (size_t size)
101 alloc_limit = size;
104 gboolean
105 mono_valloc_can_alloc (size_t size)
107 if (alloc_limit)
108 return (total_allocation_count + size) < alloc_limit;
109 return TRUE;
112 const char*
113 mono_mem_account_type_name (MonoMemAccountType type)
115 static const char *names[] = {
116 "code",
117 "hazard pointers",
118 "domain",
119 "SGen internal",
120 "SGen nursery",
121 "SGen LOS",
122 "SGen mark&sweep",
123 "SGen card table",
124 "SGen shadow card table",
125 "SGen debugging",
126 "SGen binary protocol",
127 "exceptions",
128 "profiler",
129 "other"
132 return names [type];
135 void
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);
141 char descr [128];
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]);
148 #ifdef HOST_WIN32
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
156 #else
158 static void* malloced_shared_area = NULL;
159 #if defined(HAVE_MMAP)
162 * mono_pagesize:
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
165 * aligned.
167 * Returns: the page size in bytes.
170 mono_pagesize (void)
172 static int saved_pagesize = 0;
174 if (saved_pagesize)
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);
180 #else
181 saved_pagesize = getpagesize ();
182 #endif
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)
191 return 64*1024;
193 return saved_pagesize;
197 mono_valloc_granule (void)
199 return mono_pagesize ();
202 static int
203 prot_from_flags (int flags)
205 int prot = PROT_NONE;
206 /* translate the protection bits */
207 if (flags & MONO_MMAP_READ)
208 prot |= PROT_READ;
209 if (flags & MONO_MMAP_WRITE)
210 prot |= PROT_WRITE;
211 if (flags & MONO_MMAP_EXEC)
212 prot |= PROT_EXEC;
213 return prot;
216 #if defined(__APPLE__)
218 #define DARWIN_VERSION_MOJAVE 18
220 static guint32
221 get_darwin_version (void)
223 static guint32 version;
225 /* This doesn't need locking */
226 if (!version) {
227 char str[256] = {0};
228 size_t size = sizeof(str);
229 int err = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
230 g_assert (err == 0);
231 err = sscanf (str, "%d", &version);
232 g_assert (err == 1);
233 g_assert (version > 0);
235 return version;
237 #endif
239 static int use_mmap_jit;
242 * mono_setmmapjit:
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
247 * as an App in MacOS
249 void
250 mono_setmmapjit (int flag)
252 use_mmap_jit = flag;
256 * mono_valloc:
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
267 void*
268 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
270 void *ptr;
271 int mflags = 0;
272 int prot = prot_from_flags (flags);
274 if (!mono_valloc_can_alloc (length))
275 return NULL;
277 /* translate the flags */
278 if (flags & MONO_MMAP_FIXED)
279 mflags |= MAP_FIXED;
280 if (flags & MONO_MMAP_32BIT)
281 mflags |= MAP_32BIT;
283 #ifdef HOST_WASM
284 if (length == 0)
285 /* emscripten throws an exception on 0 length */
286 return NULL;
287 #endif
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;
298 } else {
299 is_hardened_runtime = 2;
300 munmap (ptr, getpagesize ());
303 if ((flags & MONO_MMAP_JIT) && (use_mmap_jit || is_hardened_runtime == 1))
304 mflags |= MAP_JIT;
306 #endif
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);
315 if (fd != -1) {
316 ptr = mmap (addr, length, prot, mflags, fd, 0);
317 close (fd);
320 END_CRITICAL_SECTION;
322 if (ptr == MAP_FAILED)
323 return NULL;
325 mono_account_mem (type, (ssize_t)length);
327 return ptr;
331 * mono_vfree:
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)
340 int res;
341 BEGIN_CRITICAL_SECTION;
342 res = munmap (addr, length);
343 END_CRITICAL_SECTION;
345 mono_account_mem (type, -(ssize_t)length);
347 return res;
351 * mono_file_map:
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
359 * \p 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().
364 void*
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);
370 void*
371 mono_file_map_error (size_t length, int flags, int fd, guint64 offset, void **ret_handle,
372 const char *filepath, char **error_message)
374 void *ptr;
375 int mflags = 0;
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)
383 mflags |= MAP_FIXED;
384 if (flags & MONO_MMAP_32BIT)
385 mflags |= MAP_32BIT;
387 #ifdef HOST_WASM
388 if (length == 0)
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");
392 return NULL;
393 #endif
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) {
400 if (error_message) {
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);
404 return NULL;
406 *ret_handle = (void*)length;
407 return ptr;
411 * mono_file_unmap:
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)
421 int res;
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;
428 return res;
432 * mono_mprotect:
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) */
451 #ifdef __linux__
452 if (madvise (addr, length, MADV_DONTNEED))
453 memset (addr, 0, length);
454 #else
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);
460 #else
461 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
462 #endif
463 #endif
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);
469 #else
471 /* dummy malloc-based implementation */
473 mono_pagesize (void)
475 return 4096;
479 mono_valloc_granule (void)
481 return mono_pagesize ();
484 void*
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);
491 void*
492 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
494 void *res = NULL;
495 if (posix_memalign (&res, alignment, size))
496 return NULL;
498 memset (res, 0, size);
499 return res;
502 #define HAVE_VALLOC_ALIGNED
505 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
507 g_free (addr);
508 return 0;
512 mono_mprotect (void *addr, size_t length, int flags)
514 if (flags & MONO_MMAP_DISCARD) {
515 memset (addr, 0, length);
517 return 0;
520 #endif // HAVE_MMAP
522 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
524 static int use_shared_area;
526 static gboolean
527 shared_area_disabled (void)
529 if (!use_shared_area) {
530 if (g_hasenv ("MONO_DISABLE_SHARED_AREA"))
531 use_shared_area = -1;
532 else
533 use_shared_area = 1;
535 return use_shared_area == -1;
538 static int
539 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
541 int i, j = 0;
542 int num;
543 void *data;
544 gpointer *processes = mono_process_list (&num);
545 for (i = 0; i < num; ++i) {
546 data = mono_shared_area_for_pid (processes [i]);
547 if (!data)
548 continue;
549 mono_shared_area_unload (data);
550 if (!cleanup) {
551 if (j < count)
552 array [j++] = processes [i];
553 else
554 break;
557 g_free (processes);
558 return j;
561 static int
562 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
564 const char *name;
565 int i = 0;
566 int curpid = getpid ();
567 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
568 if (!dir)
569 return mono_shared_area_instances_slow (array, count, cleanup);
570 while ((name = g_dir_read_name (dir))) {
571 int pid;
572 char *nend;
573 if (strncmp (name, "mono.", 5))
574 continue;
575 pid = strtol (name + 5, &nend, 10);
576 if (pid <= 0 || nend == name + 5 || *nend)
577 continue;
578 if (!cleanup) {
579 if (i < count)
580 array [i++] = GINT_TO_POINTER (pid);
581 else
582 break;
584 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
585 char buf [128];
586 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
587 shm_unlink (buf);
590 g_dir_close (dir);
591 return i;
594 void*
595 mono_shared_area (void)
597 int fd;
598 int pid = getpid ();
599 /* we should allow the user to configure the size */
600 int size = mono_pagesize ();
601 char buf [128];
602 void *res;
603 SAreaHeader *header;
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) {
619 /* leftover */
620 shm_unlink (buf);
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
626 if (fd == -1)
627 return mono_malloc_shared_area (pid);
628 if (ftruncate (fd, size) != 0) {
629 shm_unlink (buf);
630 close (fd);
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) {
637 shm_unlink (buf);
638 close (fd);
639 return mono_malloc_shared_area (pid);
641 /* we don't need the file descriptor anymore */
642 close (fd);
643 header = (SAreaHeader *) res;
644 header->size = size;
645 header->pid = pid;
646 header->stats_start = sizeof (SAreaHeader);
647 header->stats_end = sizeof (SAreaHeader);
649 mono_atexit (mono_shared_area_remove);
650 return res;
653 void
654 mono_shared_area_remove (void)
656 char buf [128];
658 if (shared_area_disabled ()) {
659 if (malloced_shared_area)
660 g_free (malloced_shared_area);
661 return;
664 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
665 shm_unlink (buf);
666 if (malloced_shared_area)
667 g_free (malloced_shared_area);
670 void*
671 mono_shared_area_for_pid (void *pid)
673 int fd;
674 /* we should allow the user to configure the size */
675 int size = mono_pagesize ();
676 char buf [128];
677 void *res;
679 if (shared_area_disabled ())
680 return NULL;
682 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
684 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
685 if (fd == -1)
686 return NULL;
687 BEGIN_CRITICAL_SECTION;
688 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
689 END_CRITICAL_SECTION;
691 if (res == MAP_FAILED) {
692 close (fd);
693 return NULL;
695 /* FIXME: validate the area */
696 /* we don't need the file descriptor anymore */
697 close (fd);
698 return res;
701 void
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);
715 #else
716 void*
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;
725 void
726 mono_shared_area_remove (void)
728 if (malloced_shared_area)
729 g_free (malloced_shared_area);
730 malloced_shared_area = NULL;
733 void*
734 mono_shared_area_for_pid (void *pid)
736 return NULL;
739 void
740 mono_shared_area_unload (void *area)
745 mono_shared_area_instances (void **array, int count)
747 return 0;
750 #endif // HAVE_SHM_OPEN
752 #endif // HOST_WIN32
754 #ifndef HAVE_VALLOC_ALIGNED
755 void*
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);
760 char *aligned;
762 if (!mem)
763 return NULL;
765 aligned = mono_aligned_address (mem, size, alignment);
767 if (aligned > mem)
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);
772 return aligned;
774 #endif