[tools] Add nuget-hash-extractor tool to help produce the runtime ignored assemblies...
[mono-project.git] / mono / utils / mono-mmap.c
blob10a190ff9b059383a5705e6e0e5d765a79a7122d
1 /*
2 * mono-mmap.c: Support for mapping code into the process address space
4 * Author:
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.
9 */
11 #include <config.h>
13 #ifndef HOST_WIN32
14 #include <sys/types.h>
15 #if HAVE_SYS_STAT_H
16 #include <sys/stat.h>
17 #endif
18 #if HAVE_SYS_MMAN_H
19 #include <sys/mman.h>
20 #endif
21 #ifdef HAVE_SIGNAL_H
22 #include <signal.h>
23 #endif
24 #include <fcntl.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <errno.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; \
44 } while (0) \
46 #ifndef MAP_ANONYMOUS
47 #define MAP_ANONYMOUS MAP_ANON
48 #endif
50 #ifndef MAP_32BIT
51 #define MAP_32BIT 0
52 #endif
54 typedef struct {
55 int size;
56 int pid;
57 int reserved;
58 short stats_start;
59 short stats_end;
60 } SAreaHeader;
62 void*
63 malloc_shared_area (int pid)
65 int size = mono_pagesize ();
66 SAreaHeader *sarea = (SAreaHeader *) g_malloc0 (size);
67 sarea->size = size;
68 sarea->pid = pid;
69 sarea->stats_start = sizeof (SAreaHeader);
70 sarea->stats_end = sizeof (SAreaHeader);
72 return sarea;
75 char*
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)));
80 return aligned;
83 static volatile size_t allocation_count [MONO_MEM_ACCOUNT_MAX];
85 void
86 account_mem (MonoMemAccountType type, ssize_t size)
88 #if SIZEOF_VOID_P == 4
89 InterlockedAdd ((volatile gint32*)&allocation_count [type], (gint32)size);
90 #else
91 InterlockedAdd64 ((volatile gint64*)&allocation_count [type], (gint64)size);
92 #endif
95 const char*
96 mono_mem_account_type_name (MonoMemAccountType type)
98 static const char *names[] = {
99 "code",
100 "hazard pointers",
101 "domain",
102 "SGen internal",
103 "SGen nursery",
104 "SGen LOS",
105 "SGen mark&sweep",
106 "SGen card table",
107 "SGen shadow card table",
108 "SGen debugging",
109 "SGen binary protocol",
110 "exceptions",
111 "profiler",
112 "other"
115 return names [type];
118 void
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);
124 char descr [128];
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]);
131 #ifdef HOST_WIN32
132 // Windows specific implementation in mono-mmap-windows.c
133 #define HAVE_VALLOC_ALIGNED
135 #else
137 static void* malloced_shared_area = NULL;
138 #if defined(HAVE_MMAP)
141 * mono_pagesize:
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
144 * aligned.
146 * Returns: the page size in bytes.
149 mono_pagesize (void)
151 static int saved_pagesize = 0;
152 if (saved_pagesize)
153 return saved_pagesize;
154 saved_pagesize = getpagesize ();
155 return saved_pagesize;
159 mono_valloc_granule (void)
161 return mono_pagesize ();
164 static int
165 prot_from_flags (int flags)
167 int prot = PROT_NONE;
168 /* translate the protection bits */
169 if (flags & MONO_MMAP_READ)
170 prot |= PROT_READ;
171 if (flags & MONO_MMAP_WRITE)
172 prot |= PROT_WRITE;
173 if (flags & MONO_MMAP_EXEC)
174 prot |= PROT_EXEC;
175 return prot;
179 * mono_valloc:
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
192 void*
193 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
195 void *ptr;
196 int mflags = 0;
197 int prot = prot_from_flags (flags);
198 /* translate the flags */
199 if (flags & MONO_MMAP_FIXED)
200 mflags |= MAP_FIXED;
201 if (flags & MONO_MMAP_32BIT)
202 mflags |= MAP_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);
211 if (fd != -1) {
212 ptr = mmap (addr, length, prot, mflags, fd, 0);
213 close (fd);
216 END_CRITICAL_SECTION;
218 if (ptr == MAP_FAILED)
219 return NULL;
221 account_mem (type, (ssize_t)length);
223 return ptr;
227 * mono_vfree:
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)
238 int res;
239 BEGIN_CRITICAL_SECTION;
240 res = munmap (addr, length);
241 END_CRITICAL_SECTION;
243 account_mem (type, -(ssize_t)length);
245 return res;
249 * mono_file_map:
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
258 * @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 ().
264 void*
265 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
267 void *ptr;
268 int mflags = 0;
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)
276 mflags |= MAP_FIXED;
277 if (flags & MONO_MMAP_32BIT)
278 mflags |= MAP_32BIT;
280 BEGIN_CRITICAL_SECTION;
281 ptr = mmap (0, length, prot, mflags, fd, offset);
282 END_CRITICAL_SECTION;
283 if (ptr == MAP_FAILED)
284 return NULL;
285 *ret_handle = (void*)length;
286 return ptr;
290 * mono_file_unmap:
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)
302 int res;
304 BEGIN_CRITICAL_SECTION;
305 res = munmap (addr, (size_t)handle);
306 END_CRITICAL_SECTION;
308 return res;
312 * mono_mprotect:
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);
331 void *new_addr;
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;
337 return -1;
339 #else
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) */
347 #ifdef __linux__
348 if (madvise (addr, length, MADV_DONTNEED))
349 memset (addr, 0, length);
350 #else
351 memset (addr, 0, length);
352 #ifdef HAVE_MADVISE
353 madvise (addr, length, MADV_DONTNEED);
354 madvise (addr, length, MADV_FREE);
355 #else
356 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
357 #endif
358 #endif
360 return mprotect (addr, length, prot);
362 #endif // __native_client__
364 #else
366 /* dummy malloc-based implementation */
368 mono_pagesize (void)
370 return 4096;
374 mono_valloc_granule (void)
376 return mono_pagesize ();
379 void*
380 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
382 return g_malloc (length);
385 void*
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)
396 g_free (addr);
397 return 0;
401 mono_mprotect (void *addr, size_t length, int flags)
403 if (flags & MONO_MMAP_DISCARD) {
404 memset (addr, 0, length);
406 return 0;
409 #endif // HAVE_MMAP
411 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
413 static int use_shared_area;
415 static gboolean
416 shared_area_disabled (void)
418 if (!use_shared_area) {
419 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
420 use_shared_area = -1;
421 else
422 use_shared_area = 1;
424 return use_shared_area == -1;
427 static int
428 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
430 int i, j = 0;
431 int num;
432 void *data;
433 gpointer *processes = mono_process_list (&num);
434 for (i = 0; i < num; ++i) {
435 data = mono_shared_area_for_pid (processes [i]);
436 if (!data)
437 continue;
438 mono_shared_area_unload (data);
439 if (!cleanup) {
440 if (j < count)
441 array [j++] = processes [i];
442 else
443 break;
446 g_free (processes);
447 return j;
450 static int
451 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
453 const char *name;
454 int i = 0;
455 int curpid = getpid ();
456 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
457 if (!dir)
458 return mono_shared_area_instances_slow (array, count, cleanup);
459 while ((name = g_dir_read_name (dir))) {
460 int pid;
461 char *nend;
462 if (strncmp (name, "mono.", 5))
463 continue;
464 pid = strtol (name + 5, &nend, 10);
465 if (pid <= 0 || nend == name + 5 || *nend)
466 continue;
467 if (!cleanup) {
468 if (i < count)
469 array [i++] = GINT_TO_POINTER (pid);
470 else
471 break;
473 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
474 char buf [128];
475 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
476 shm_unlink (buf);
479 g_dir_close (dir);
480 return i;
483 void*
484 mono_shared_area (void)
486 int fd;
487 int pid = getpid ();
488 /* we should allow the user to configure the size */
489 int size = mono_pagesize ();
490 char buf [128];
491 void *res;
492 SAreaHeader *header;
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) {
508 /* leftover */
509 shm_unlink (buf);
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
515 if (fd == -1)
516 return malloc_shared_area (pid);
517 if (ftruncate (fd, size) != 0) {
518 shm_unlink (buf);
519 close (fd);
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) {
526 shm_unlink (buf);
527 close (fd);
528 return malloc_shared_area (pid);
530 /* we don't need the file descriptor anymore */
531 close (fd);
532 header = (SAreaHeader *) res;
533 header->size = size;
534 header->pid = pid;
535 header->stats_start = sizeof (SAreaHeader);
536 header->stats_end = sizeof (SAreaHeader);
538 mono_atexit (mono_shared_area_remove);
539 return res;
542 void
543 mono_shared_area_remove (void)
545 char buf [128];
547 if (shared_area_disabled ()) {
548 if (malloced_shared_area)
549 g_free (malloced_shared_area);
550 return;
553 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
554 shm_unlink (buf);
555 if (malloced_shared_area)
556 g_free (malloced_shared_area);
559 void*
560 mono_shared_area_for_pid (void *pid)
562 int fd;
563 /* we should allow the user to configure the size */
564 int size = mono_pagesize ();
565 char buf [128];
566 void *res;
568 if (shared_area_disabled ())
569 return NULL;
571 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
573 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
574 if (fd == -1)
575 return NULL;
576 BEGIN_CRITICAL_SECTION;
577 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
578 END_CRITICAL_SECTION;
580 if (res == MAP_FAILED) {
581 close (fd);
582 return NULL;
584 /* FIXME: validate the area */
585 /* we don't need the file descriptor anymore */
586 close (fd);
587 return res;
590 void
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);
604 #else
605 void*
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;
614 void
615 mono_shared_area_remove (void)
617 if (malloced_shared_area)
618 g_free (malloced_shared_area);
619 malloced_shared_area = NULL;
622 void*
623 mono_shared_area_for_pid (void *pid)
625 return NULL;
628 void
629 mono_shared_area_unload (void *area)
634 mono_shared_area_instances (void **array, int count)
636 return 0;
639 #endif // HAVE_SHM_OPEN
641 #endif // HOST_WIN32
643 #ifndef HAVE_VALLOC_ALIGNED
644 void*
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);
649 char *aligned;
651 if (!mem)
652 return NULL;
654 aligned = aligned_address (mem, size, alignment);
656 if (aligned > mem)
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);
661 return aligned;
663 #endif
666 mono_pages_not_faulted (void *addr, size_t size)
668 #ifdef HAVE_MINCORE
669 int i;
670 gint64 count;
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*.
679 #ifdef __linux__
680 if (mincore (addr, size, (unsigned char *)faulted) != 0) {
681 #else
682 if (mincore (addr, size, (char *)faulted) != 0) {
683 #endif
684 count = -1;
685 } else {
686 count = 0;
687 for (i = 0; i < npages; ++i) {
688 if (faulted [i] != 0)
689 ++count;
693 g_free (faulted);
695 return count;
696 #else
697 return -1;
698 #endif