Merge pull request #3749 from BrzVlad/fix-mips-fix
[mono.git] / mono / utils / mono-mmap.c
1 /*
2  * mono-mmap.c: Support for mapping code into the process address space
3  *
4  * Author:
5  *   Mono Team (mono-list@lists.ximian.com)
6  *
7  * Copyright 2001-2008 Novell, Inc.
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10
11 #include <config.h>
12
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 */
30
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>
37
38 #define BEGIN_CRITICAL_SECTION do { \
39         MonoThreadInfo *__info = mono_thread_info_current_unchecked (); \
40         if (__info) __info->inside_critical_region = TRUE;      \
41
42 #define END_CRITICAL_SECTION \
43         if (__info) __info->inside_critical_region = FALSE;     \
44 } while (0)     \
45
46 #ifndef MAP_ANONYMOUS
47 #define MAP_ANONYMOUS MAP_ANON
48 #endif
49
50 #ifndef MAP_32BIT
51 #define MAP_32BIT 0
52 #endif
53
54 typedef struct {
55         int size;
56         int pid;
57         int reserved;
58         short stats_start;
59         short stats_end;
60 } SAreaHeader;
61
62 void*
63 malloc_shared_area (int pid)
64 {
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);
71
72         return sarea;
73 }
74
75 char*
76 aligned_address (char *mem, size_t size, size_t alignment)
77 {
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;
81 }
82
83 static volatile size_t allocation_count [MONO_MEM_ACCOUNT_MAX];
84
85 void
86 account_mem (MonoMemAccountType type, ssize_t size)
87 {
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
93 }
94
95 const char*
96 mono_mem_account_type_name (MonoMemAccountType type)
97 {
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"
113         };
114
115         return names [type];
116 }
117
118 void
119 mono_mem_account_register_counters (void)
120 {
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]);
128         }
129 }
130
131 #ifdef HOST_WIN32
132 // Windows specific implementation in mono-mmap-windows.c
133 #define HAVE_VALLOC_ALIGNED
134
135 #else
136
137 static void* malloced_shared_area = NULL;
138 #if defined(HAVE_MMAP)
139
140 /**
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.
145  *
146  * Returns: the page size in bytes.
147  */
148 int
149 mono_pagesize (void)
150 {
151         static int saved_pagesize = 0;
152         if (saved_pagesize)
153                 return saved_pagesize;
154         saved_pagesize = getpagesize ();
155         return saved_pagesize;
156 }
157
158 static int
159 prot_from_flags (int flags)
160 {
161         int prot = PROT_NONE;
162         /* translate the protection bits */
163         if (flags & MONO_MMAP_READ)
164                 prot |= PROT_READ;
165         if (flags & MONO_MMAP_WRITE)
166                 prot |= PROT_WRITE;
167         if (flags & MONO_MMAP_EXEC)
168                 prot |= PROT_EXEC;
169         return prot;
170 }
171
172 /**
173  * mono_valloc:
174  * @addr: memory address
175  * @length: memory area size
176  * @flags: protection flags
177  *
178  * Allocates @length bytes of virtual memory with the @flags
179  * protection. @addr can be a preferred memory address or a
180  * mandatory one if MONO_MMAP_FIXED is set in @flags.
181  * @addr must be pagesize aligned and can be NULL.
182  * @length must be a multiple of pagesize.
183  *
184  * Returns: NULL on failure, the address of the memory area otherwise
185  */
186 void*
187 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
188 {
189         void *ptr;
190         int mflags = 0;
191         int prot = prot_from_flags (flags);
192         /* translate the flags */
193         if (flags & MONO_MMAP_FIXED)
194                 mflags |= MAP_FIXED;
195         if (flags & MONO_MMAP_32BIT)
196                 mflags |= MAP_32BIT;
197
198         mflags |= MAP_ANONYMOUS;
199         mflags |= MAP_PRIVATE;
200
201         BEGIN_CRITICAL_SECTION;
202         ptr = mmap (addr, length, prot, mflags, -1, 0);
203         if (ptr == MAP_FAILED) {
204                 int fd = open ("/dev/zero", O_RDONLY);
205                 if (fd != -1) {
206                         ptr = mmap (addr, length, prot, mflags, fd, 0);
207                         close (fd);
208                 }
209         }
210         END_CRITICAL_SECTION;
211
212         if (ptr == MAP_FAILED)
213                 return NULL;
214
215         account_mem (type, (ssize_t)length);
216
217         return ptr;
218 }
219
220 /**
221  * mono_vfree:
222  * @addr: memory address returned by mono_valloc ()
223  * @length: size of memory area
224  *
225  * Remove the memory mapping at the address @addr.
226  *
227  * Returns: 0 on success.
228  */
229 int
230 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
231 {
232         int res;
233         BEGIN_CRITICAL_SECTION;
234         res = munmap (addr, length);
235         END_CRITICAL_SECTION;
236
237         account_mem (type, -(ssize_t)length);
238
239         return res;
240 }
241
242 /**
243  * mono_file_map:
244  * @length: size of data to map
245  * @flags: protection flags
246  * @fd: file descriptor
247  * @offset: offset in the file
248  * @ret_handle: pointer to storage for returning a handle for the map
249  *
250  * Map the area of the file pointed to by the file descriptor @fd, at offset
251  * @offset and of size @length in memory according to the protection flags
252  * @flags.
253  * @offset and @length must be multiples of the page size.
254  * @ret_handle must point to a void*: this value must be used when unmapping
255  * the memory area using mono_file_unmap ().
256  *
257  */
258 void*
259 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
260 {
261         void *ptr;
262         int mflags = 0;
263         int prot = prot_from_flags (flags);
264         /* translate the flags */
265         if (flags & MONO_MMAP_PRIVATE)
266                 mflags |= MAP_PRIVATE;
267         if (flags & MONO_MMAP_SHARED)
268                 mflags |= MAP_SHARED;
269         if (flags & MONO_MMAP_FIXED)
270                 mflags |= MAP_FIXED;
271         if (flags & MONO_MMAP_32BIT)
272                 mflags |= MAP_32BIT;
273
274         BEGIN_CRITICAL_SECTION;
275         ptr = mmap (0, length, prot, mflags, fd, offset);
276         END_CRITICAL_SECTION;
277         if (ptr == MAP_FAILED)
278                 return NULL;
279         *ret_handle = (void*)length;
280         return ptr;
281 }
282
283 /**
284  * mono_file_unmap:
285  * @addr: memory address returned by mono_file_map ()
286  * @handle: handle of memory map
287  *
288  * Remove the memory mapping at the address @addr.
289  * @handle must be the value returned in ret_handle by mono_file_map ().
290  *
291  * Returns: 0 on success.
292  */
293 int
294 mono_file_unmap (void *addr, void *handle)
295 {
296         int res;
297
298         BEGIN_CRITICAL_SECTION;
299         res = munmap (addr, (size_t)handle);
300         END_CRITICAL_SECTION;
301
302         return res;
303 }
304
305 /**
306  * mono_mprotect:
307  * @addr: memory address
308  * @length: size of memory area
309  * @flags: new protection flags
310  *
311  * Change the protection for the memory area at @addr for @length bytes
312  * to matche the supplied @flags.
313  * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
314  * and the area is cleared to zero.
315  * @addr must be aligned to the page size.
316  * @length must be a multiple of the page size.
317  *
318  * Returns: 0 on success.
319  */
320 #if defined(__native_client__)
321 int
322 mono_mprotect (void *addr, size_t length, int flags)
323 {
324         int prot = prot_from_flags (flags);
325         void *new_addr;
326
327         if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
328
329         new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
330         if (new_addr == addr) return 0;
331         return -1;
332 }
333 #else
334 int
335 mono_mprotect (void *addr, size_t length, int flags)
336 {
337         int prot = prot_from_flags (flags);
338
339         if (flags & MONO_MMAP_DISCARD) {
340                 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
341 #ifdef __linux__
342                 if (madvise (addr, length, MADV_DONTNEED))
343                         memset (addr, 0, length);
344 #else
345                 memset (addr, 0, length);
346 #ifdef HAVE_MADVISE
347                 madvise (addr, length, MADV_DONTNEED);
348                 madvise (addr, length, MADV_FREE);
349 #else
350                 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
351 #endif
352 #endif
353         }
354         return mprotect (addr, length, prot);
355 }
356 #endif // __native_client__
357
358 #else
359
360 /* dummy malloc-based implementation */
361 int
362 mono_pagesize (void)
363 {
364         return 4096;
365 }
366
367 void*
368 mono_valloc (void *addr, size_t length, int flags, MonoMemAccountType type)
369 {
370         return g_malloc (length);
371 }
372
373 void*
374 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
375 {
376         g_assert_not_reached ();
377 }
378
379 #define HAVE_VALLOC_ALIGNED
380
381 int
382 mono_vfree (void *addr, size_t length, MonoMemAccountType type)
383 {
384         g_free (addr);
385         return 0;
386 }
387
388 int
389 mono_mprotect (void *addr, size_t length, int flags)
390 {
391         if (flags & MONO_MMAP_DISCARD) {
392                 memset (addr, 0, length);
393         }
394         return 0;
395 }
396
397 #endif // HAVE_MMAP
398
399 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
400
401 static int use_shared_area;
402
403 static gboolean
404 shared_area_disabled (void)
405 {
406         if (!use_shared_area) {
407                 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
408                         use_shared_area = -1;
409                 else
410                         use_shared_area = 1;
411         }
412         return use_shared_area == -1;
413 }
414
415 static int
416 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
417 {
418         int i, j = 0;
419         int num;
420         void *data;
421         gpointer *processes = mono_process_list (&num);
422         for (i = 0; i < num; ++i) {
423                 data = mono_shared_area_for_pid (processes [i]);
424                 if (!data)
425                         continue;
426                 mono_shared_area_unload (data);
427                 if (!cleanup) {
428                         if (j < count)
429                                 array [j++] = processes [i];
430                         else
431                                 break;
432                 }
433         }
434         g_free (processes);
435         return j;
436 }
437
438 static int
439 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
440 {
441         const char *name;
442         int i = 0;
443         int curpid = getpid ();
444         GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
445         if (!dir)
446                 return mono_shared_area_instances_slow (array, count, cleanup);
447         while ((name = g_dir_read_name (dir))) {
448                 int pid;
449                 char *nend;
450                 if (strncmp (name, "mono.", 5))
451                         continue;
452                 pid = strtol (name + 5, &nend, 10);
453                 if (pid <= 0 || nend == name + 5 || *nend)
454                         continue;
455                 if (!cleanup) {
456                         if (i < count)
457                                 array [i++] = GINT_TO_POINTER (pid);
458                         else
459                                 break;
460                 }
461                 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
462                         char buf [128];
463                         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
464                         shm_unlink (buf);
465                 }
466         }
467         g_dir_close (dir);
468         return i;
469 }
470
471 void*
472 mono_shared_area (void)
473 {
474         int fd;
475         int pid = getpid ();
476         /* we should allow the user to configure the size */
477         int size = mono_pagesize ();
478         char buf [128];
479         void *res;
480         SAreaHeader *header;
481
482         if (shared_area_disabled ()) {
483                 if (!malloced_shared_area)
484                         malloced_shared_area = malloc_shared_area (0);
485                 /* get the pid here */
486                 return malloced_shared_area;
487         }
488
489         /* perform cleanup of segments left over from dead processes */
490         mono_shared_area_instances_helper (NULL, 0, TRUE);
491
492         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
493
494         fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
495         if (fd == -1 && errno == EEXIST) {
496                 /* leftover */
497                 shm_unlink (buf);
498                 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
499         }
500         /* in case of failure we try to return a memory area anyway,
501          * even if it means the data can't be read by other processes
502          */
503         if (fd == -1)
504                 return malloc_shared_area (pid);
505         if (ftruncate (fd, size) != 0) {
506                 shm_unlink (buf);
507                 close (fd);
508         }
509         BEGIN_CRITICAL_SECTION;
510         res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
511         END_CRITICAL_SECTION;
512
513         if (res == MAP_FAILED) {
514                 shm_unlink (buf);
515                 close (fd);
516                 return malloc_shared_area (pid);
517         }
518         /* we don't need the file descriptor anymore */
519         close (fd);
520         header = (SAreaHeader *) res;
521         header->size = size;
522         header->pid = pid;
523         header->stats_start = sizeof (SAreaHeader);
524         header->stats_end = sizeof (SAreaHeader);
525
526         mono_atexit (mono_shared_area_remove);
527         return res;
528 }
529
530 void
531 mono_shared_area_remove (void)
532 {
533         char buf [128];
534
535         if (shared_area_disabled ()) {
536                 if (malloced_shared_area)
537                         g_free (malloced_shared_area);
538                 return;
539         }
540
541         g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
542         shm_unlink (buf);
543         if (malloced_shared_area)
544                 g_free (malloced_shared_area);
545 }
546
547 void*
548 mono_shared_area_for_pid (void *pid)
549 {
550         int fd;
551         /* we should allow the user to configure the size */
552         int size = mono_pagesize ();
553         char buf [128];
554         void *res;
555
556         if (shared_area_disabled ())
557                 return NULL;
558
559         g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
560
561         fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
562         if (fd == -1)
563                 return NULL;
564         BEGIN_CRITICAL_SECTION;
565         res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
566         END_CRITICAL_SECTION;
567
568         if (res == MAP_FAILED) {
569                 close (fd);
570                 return NULL;
571         }
572         /* FIXME: validate the area */
573         /* we don't need the file descriptor anymore */
574         close (fd);
575         return res;
576 }
577
578 void
579 mono_shared_area_unload  (void *area)
580 {
581         /* FIXME: currently we load only a page */
582         BEGIN_CRITICAL_SECTION;
583         munmap (area, mono_pagesize ());
584         END_CRITICAL_SECTION;
585 }
586
587 int
588 mono_shared_area_instances (void **array, int count)
589 {
590         return mono_shared_area_instances_helper (array, count, FALSE);
591 }
592 #else
593 void*
594 mono_shared_area (void)
595 {
596         if (!malloced_shared_area)
597                 malloced_shared_area = malloc_shared_area (getpid ());
598         /* get the pid here */
599         return malloced_shared_area;
600 }
601
602 void
603 mono_shared_area_remove (void)
604 {
605         if (malloced_shared_area)
606                 g_free (malloced_shared_area);
607         malloced_shared_area = NULL;
608 }
609
610 void*
611 mono_shared_area_for_pid (void *pid)
612 {
613         return NULL;
614 }
615
616 void
617 mono_shared_area_unload (void *area)
618 {
619 }
620
621 int
622 mono_shared_area_instances (void **array, int count)
623 {
624         return 0;
625 }
626
627 #endif // HAVE_SHM_OPEN
628
629 #endif // HOST_WIN32
630
631 #ifndef HAVE_VALLOC_ALIGNED
632 void*
633 mono_valloc_aligned (size_t size, size_t alignment, int flags, MonoMemAccountType type)
634 {
635         /* Allocate twice the memory to be able to put the block on an aligned address */
636         char *mem = (char *) mono_valloc (NULL, size + alignment, flags, type);
637         char *aligned;
638
639         if (!mem)
640                 return NULL;
641
642         aligned = aligned_address (mem, size, alignment);
643
644         if (aligned > mem)
645                 mono_vfree (mem, aligned - mem, type);
646         if (aligned + size < mem + size + alignment)
647                 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size), type);
648
649         return aligned;
650 }
651 #endif
652
653 int
654 mono_pages_not_faulted (void *addr, size_t size)
655 {
656 #ifdef HAVE_MINCORE
657         int i;
658         gint64 count;
659         int pagesize = mono_pagesize ();
660         int npages = (size + pagesize - 1) / pagesize;
661         char *faulted = (char *) g_malloc0 (sizeof (char*) * npages);
662
663         /*
664          * We cast `faulted` to void* because Linux wants an unsigned
665          * char* while BSD wants a char*.
666          */
667 #ifdef __linux__
668         if (mincore (addr, size, (unsigned char *)faulted) != 0) {
669 #else
670         if (mincore (addr, size, (char *)faulted) != 0) {
671 #endif
672                 count = -1;
673         } else {
674                 count = 0;
675                 for (i = 0; i < npages; ++i) {
676                         if (faulted [i] != 0)
677                                 ++count;
678                 }
679         }
680
681         g_free (faulted);
682
683         return count;
684 #else
685         return -1;
686 #endif
687 }