f3b4d17c77bd9104d49c742c56b84d70a1d63eb7
[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  */
9
10 #include "config.h"
11
12 #ifdef HOST_WIN32
13 #include <windows.h>
14 #include <io.h>
15 #else
16 #include <sys/types.h>
17 #if HAVE_SYS_STAT_H
18 #include <sys/stat.h>
19 #endif
20 #if HAVE_SYS_MMAN_H
21 #include <sys/mman.h>
22 #endif
23 #ifdef HAVE_SIGNAL_H
24 #include <signal.h>
25 #endif
26 #include <fcntl.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #endif
32
33 #include "mono-mmap.h"
34 #include "mono-mmap-internals.h"
35 #include "mono-proclib.h"
36 #include <mono/utils/mono-threads.h>
37
38
39 #define BEGIN_CRITICAL_SECTION do { \
40         MonoThreadInfo *__info = mono_thread_info_current_unchecked (); \
41         if (__info) __info->inside_critical_region = TRUE;      \
42
43 #define END_CRITICAL_SECTION \
44         if (__info) __info->inside_critical_region = FALSE;     \
45 } while (0)     \
46
47 #ifndef MAP_ANONYMOUS
48 #define MAP_ANONYMOUS MAP_ANON
49 #endif
50
51 #ifndef MAP_32BIT
52 #define MAP_32BIT 0
53 #endif
54
55 typedef struct {
56         int size;
57         int pid;
58         int reserved;
59         short stats_start;
60         short stats_end;
61 } SAreaHeader;
62
63 static void* malloced_shared_area = NULL;
64
65 static void*
66 malloc_shared_area (int pid)
67 {
68         int size = mono_pagesize ();
69         SAreaHeader *sarea = (SAreaHeader *) g_malloc0 (size);
70         sarea->size = size;
71         sarea->pid = pid;
72         sarea->stats_start = sizeof (SAreaHeader);
73         sarea->stats_end = sizeof (SAreaHeader);
74
75         return sarea;
76 }
77
78 static char*
79 aligned_address (char *mem, size_t size, size_t alignment)
80 {
81         char *aligned = (char*)((size_t)(mem + (alignment - 1)) & ~(alignment - 1));
82         g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((size_t)aligned & (alignment - 1)));
83         return aligned;
84 }
85
86 #ifdef HOST_WIN32
87
88 int
89 mono_pagesize (void)
90 {
91         SYSTEM_INFO info;
92         static int saved_pagesize = 0;
93         if (saved_pagesize)
94                 return saved_pagesize;
95         GetSystemInfo (&info);
96         saved_pagesize = info.dwAllocationGranularity;
97         return saved_pagesize;
98 }
99
100 static int
101 prot_from_flags (int flags)
102 {
103         int prot = flags & (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC);
104         switch (prot) {
105         case 0: prot = PAGE_NOACCESS; break;
106         case MONO_MMAP_READ: prot = PAGE_READONLY; break;
107         case MONO_MMAP_READ|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READ; break;
108         case MONO_MMAP_READ|MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
109         case MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
110         case MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
111         case MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
112         case MONO_MMAP_EXEC: prot = PAGE_EXECUTE; break;
113         default:
114                 g_assert_not_reached ();
115         }
116         return prot;
117 }
118
119 void*
120 mono_valloc (void *addr, size_t length, int flags)
121 {
122         void *ptr;
123         int mflags = MEM_RESERVE|MEM_COMMIT;
124         int prot = prot_from_flags (flags);
125         /* translate the flags */
126
127         ptr = VirtualAlloc (addr, length, mflags, prot);
128         return ptr;
129 }
130
131 void*
132 mono_valloc_aligned (size_t length, size_t alignment, int flags)
133 {
134         int prot = prot_from_flags (flags);
135         char *mem = VirtualAlloc (NULL, length + alignment, MEM_RESERVE, prot);
136         char *aligned;
137
138         if (!mem)
139                 return NULL;
140
141         aligned = aligned_address (mem, length, alignment);
142
143         aligned = VirtualAlloc (aligned, length, MEM_COMMIT, prot);
144         g_assert (aligned);
145
146         return aligned;
147 }
148
149 #define HAVE_VALLOC_ALIGNED
150
151 int
152 mono_vfree (void *addr, size_t length)
153 {
154         MEMORY_BASIC_INFORMATION mbi;
155         SIZE_T query_result = VirtualQuery (addr, &mbi, sizeof (mbi));
156         BOOL res;
157
158         g_assert (query_result);
159
160         res = VirtualFree (mbi.AllocationBase, 0, MEM_RELEASE);
161
162         g_assert (res);
163
164         return 0;
165 }
166
167 void*
168 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
169 {
170         void *ptr;
171         int mflags = 0;
172         HANDLE file, mapping;
173         int prot = prot_from_flags (flags);
174         /* translate the flags */
175         /*if (flags & MONO_MMAP_PRIVATE)
176                 mflags |= MAP_PRIVATE;
177         if (flags & MONO_MMAP_SHARED)
178                 mflags |= MAP_SHARED;
179         if (flags & MONO_MMAP_ANON)
180                 mflags |= MAP_ANONYMOUS;
181         if (flags & MONO_MMAP_FIXED)
182                 mflags |= MAP_FIXED;
183         if (flags & MONO_MMAP_32BIT)
184                 mflags |= MAP_32BIT;*/
185
186         mflags = FILE_MAP_READ;
187         if (flags & MONO_MMAP_WRITE)
188                 mflags = FILE_MAP_COPY;
189
190         file = (HANDLE) _get_osfhandle (fd);
191         mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
192         if (mapping == NULL)
193                 return NULL;
194         ptr = MapViewOfFile (mapping, mflags, 0, offset, length);
195         if (ptr == NULL) {
196                 CloseHandle (mapping);
197                 return NULL;
198         }
199         *ret_handle = (void*)mapping;
200         return ptr;
201 }
202
203 int
204 mono_file_unmap (void *addr, void *handle)
205 {
206         UnmapViewOfFile (addr);
207         CloseHandle ((HANDLE)handle);
208         return 0;
209 }
210
211 int
212 mono_mprotect (void *addr, size_t length, int flags)
213 {
214         DWORD oldprot;
215         int prot = prot_from_flags (flags);
216
217         if (flags & MONO_MMAP_DISCARD) {
218                 VirtualFree (addr, length, MEM_DECOMMIT);
219                 VirtualAlloc (addr, length, MEM_COMMIT, prot);
220                 return 0;
221         }
222         return VirtualProtect (addr, length, prot, &oldprot) == 0;
223 }
224
225 void*
226 mono_shared_area (void)
227 {
228         if (!malloced_shared_area)
229                 malloced_shared_area = malloc_shared_area (0);
230         /* get the pid here */
231         return malloced_shared_area;
232 }
233
234 void
235 mono_shared_area_remove (void)
236 {
237         if (malloced_shared_area)
238                 g_free (malloced_shared_area);
239         malloced_shared_area = NULL;
240 }
241
242 void*
243 mono_shared_area_for_pid (void *pid)
244 {
245         return NULL;
246 }
247
248 void
249 mono_shared_area_unload (void *area)
250 {
251 }
252
253 int
254 mono_shared_area_instances (void **array, int count)
255 {
256         return 0;
257 }
258
259 #else
260 #if defined(HAVE_MMAP)
261
262 /**
263  * mono_pagesize:
264  * Get the page size in use on the system. Addresses and sizes in the
265  * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
266  * aligned.
267  *
268  * Returns: the page size in bytes.
269  */
270 int
271 mono_pagesize (void)
272 {
273         static int saved_pagesize = 0;
274         if (saved_pagesize)
275                 return saved_pagesize;
276         saved_pagesize = getpagesize ();
277         return saved_pagesize;
278 }
279
280 static int
281 prot_from_flags (int flags)
282 {
283         int prot = PROT_NONE;
284         /* translate the protection bits */
285         if (flags & MONO_MMAP_READ)
286                 prot |= PROT_READ;
287         if (flags & MONO_MMAP_WRITE)
288                 prot |= PROT_WRITE;
289         if (flags & MONO_MMAP_EXEC)
290                 prot |= PROT_EXEC;
291         return prot;
292 }
293
294 /**
295  * mono_valloc:
296  * @addr: memory address
297  * @length: memory area size
298  * @flags: protection flags
299  *
300  * Allocates @length bytes of virtual memory with the @flags
301  * protection. @addr can be a preferred memory address or a
302  * mandatory one if MONO_MMAP_FIXED is set in @flags.
303  * @addr must be pagesize aligned and can be NULL.
304  * @length must be a multiple of pagesize.
305  *
306  * Returns: NULL on failure, the address of the memory area otherwise
307  */
308 void*
309 mono_valloc (void *addr, size_t length, int flags)
310 {
311         void *ptr;
312         int mflags = 0;
313         int prot = prot_from_flags (flags);
314         /* translate the flags */
315         if (flags & MONO_MMAP_FIXED)
316                 mflags |= MAP_FIXED;
317         if (flags & MONO_MMAP_32BIT)
318                 mflags |= MAP_32BIT;
319
320         mflags |= MAP_ANONYMOUS;
321         mflags |= MAP_PRIVATE;
322
323         BEGIN_CRITICAL_SECTION;
324         ptr = mmap (addr, length, prot, mflags, -1, 0);
325         if (ptr == MAP_FAILED) {
326                 int fd = open ("/dev/zero", O_RDONLY);
327                 if (fd != -1) {
328                         ptr = mmap (addr, length, prot, mflags, fd, 0);
329                         close (fd);
330                 }
331         }
332         END_CRITICAL_SECTION;
333
334         if (ptr == MAP_FAILED)
335                 return NULL;
336         return ptr;
337 }
338
339 /**
340  * mono_vfree:
341  * @addr: memory address returned by mono_valloc ()
342  * @length: size of memory area
343  *
344  * Remove the memory mapping at the address @addr.
345  *
346  * Returns: 0 on success.
347  */
348 int
349 mono_vfree (void *addr, size_t length)
350 {
351         int res;
352         BEGIN_CRITICAL_SECTION;
353         res = munmap (addr, length);
354         END_CRITICAL_SECTION;
355         return res;
356 }
357
358 /**
359  * mono_file_map:
360  * @length: size of data to map
361  * @flags: protection flags
362  * @fd: file descriptor
363  * @offset: offset in the file
364  * @ret_handle: pointer to storage for returning a handle for the map
365  *
366  * Map the area of the file pointed to by the file descriptor @fd, at offset
367  * @offset and of size @length in memory according to the protection flags
368  * @flags.
369  * @offset and @length must be multiples of the page size.
370  * @ret_handle must point to a void*: this value must be used when unmapping
371  * the memory area using mono_file_unmap ().
372  *
373  */
374 void*
375 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
376 {
377         void *ptr;
378         int mflags = 0;
379         int prot = prot_from_flags (flags);
380         /* translate the flags */
381         if (flags & MONO_MMAP_PRIVATE)
382                 mflags |= MAP_PRIVATE;
383         if (flags & MONO_MMAP_SHARED)
384                 mflags |= MAP_SHARED;
385         if (flags & MONO_MMAP_FIXED)
386                 mflags |= MAP_FIXED;
387         if (flags & MONO_MMAP_32BIT)
388                 mflags |= MAP_32BIT;
389
390         BEGIN_CRITICAL_SECTION;
391         ptr = mmap (0, length, prot, mflags, fd, offset);
392         END_CRITICAL_SECTION;
393         if (ptr == MAP_FAILED)
394                 return NULL;
395         *ret_handle = (void*)length;
396         return ptr;
397 }
398
399 /**
400  * mono_file_unmap:
401  * @addr: memory address returned by mono_file_map ()
402  * @handle: handle of memory map
403  *
404  * Remove the memory mapping at the address @addr.
405  * @handle must be the value returned in ret_handle by mono_file_map ().
406  *
407  * Returns: 0 on success.
408  */
409 int
410 mono_file_unmap (void *addr, void *handle)
411 {
412         int res;
413
414         BEGIN_CRITICAL_SECTION;
415         res = munmap (addr, (size_t)handle);
416         END_CRITICAL_SECTION;
417
418         return res;
419 }
420
421 /**
422  * mono_mprotect:
423  * @addr: memory address
424  * @length: size of memory area
425  * @flags: new protection flags
426  *
427  * Change the protection for the memory area at @addr for @length bytes
428  * to matche the supplied @flags.
429  * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
430  * and the area is cleared to zero.
431  * @addr must be aligned to the page size.
432  * @length must be a multiple of the page size.
433  *
434  * Returns: 0 on success.
435  */
436 #if defined(__native_client__)
437 int
438 mono_mprotect (void *addr, size_t length, int flags)
439 {
440         int prot = prot_from_flags (flags);
441         void *new_addr;
442
443         if (flags & MONO_MMAP_DISCARD) memset (addr, 0, length);
444
445         new_addr = mmap(addr, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
446         if (new_addr == addr) return 0;
447         return -1;
448 }
449 #else
450 int
451 mono_mprotect (void *addr, size_t length, int flags)
452 {
453         int prot = prot_from_flags (flags);
454
455         if (flags & MONO_MMAP_DISCARD) {
456                 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
457 #ifdef __linux__
458                 if (madvise (addr, length, MADV_DONTNEED))
459                         memset (addr, 0, length);
460 #else
461                 memset (addr, 0, length);
462 #ifdef HAVE_MADVISE
463                 madvise (addr, length, MADV_DONTNEED);
464                 madvise (addr, length, MADV_FREE);
465 #else
466                 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
467 #endif
468 #endif
469         }
470         return mprotect (addr, length, prot);
471 }
472 #endif // __native_client__
473
474 #else
475
476 /* dummy malloc-based implementation */
477 int
478 mono_pagesize (void)
479 {
480         return 4096;
481 }
482
483 void*
484 mono_valloc (void *addr, size_t length, int flags)
485 {
486         return malloc (length);
487 }
488
489 void*
490 mono_valloc_aligned (size_t length, size_t alignment, int flags)
491 {
492         g_assert_not_reached ();
493 }
494
495 #define HAVE_VALLOC_ALIGNED
496
497 int
498 mono_vfree (void *addr, size_t length)
499 {
500         free (addr);
501         return 0;
502 }
503
504 int
505 mono_mprotect (void *addr, size_t length, int flags)
506 {
507         if (flags & MONO_MMAP_DISCARD) {
508                 memset (addr, 0, length);
509         }
510         return 0;
511 }
512
513 #endif // HAVE_MMAP
514
515 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
516
517 static int use_shared_area;
518
519 static gboolean
520 shared_area_disabled (void)
521 {
522         if (!use_shared_area) {
523                 if (g_getenv ("MONO_DISABLE_SHARED_AREA"))
524                         use_shared_area = -1;
525                 else
526                         use_shared_area = 1;
527         }
528         return use_shared_area == -1;
529 }
530
531 static int
532 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
533 {
534         int i, j = 0;
535         int num;
536         void *data;
537         gpointer *processes = mono_process_list (&num);
538         for (i = 0; i < num; ++i) {
539                 data = mono_shared_area_for_pid (processes [i]);
540                 if (!data)
541                         continue;
542                 mono_shared_area_unload (data);
543                 if (!cleanup) {
544                         if (j < count)
545                                 array [j++] = processes [i];
546                         else
547                                 break;
548                 }
549         }
550         g_free (processes);
551         return j;
552 }
553
554 static int
555 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
556 {
557         const char *name;
558         int i = 0;
559         int curpid = getpid ();
560         GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
561         if (!dir)
562                 return mono_shared_area_instances_slow (array, count, cleanup);
563         while ((name = g_dir_read_name (dir))) {
564                 int pid;
565                 char *nend;
566                 if (strncmp (name, "mono.", 5))
567                         continue;
568                 pid = strtol (name + 5, &nend, 10);
569                 if (pid <= 0 || nend == name + 5 || *nend)
570                         continue;
571                 if (!cleanup) {
572                         if (i < count)
573                                 array [i++] = GINT_TO_POINTER (pid);
574                         else
575                                 break;
576                 }
577                 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
578                         char buf [128];
579                         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
580                         shm_unlink (buf);
581                 }
582         }
583         g_dir_close (dir);
584         return i;
585 }
586
587 void*
588 mono_shared_area (void)
589 {
590         int fd;
591         int pid = getpid ();
592         /* we should allow the user to configure the size */
593         int size = mono_pagesize ();
594         char buf [128];
595         void *res;
596         SAreaHeader *header;
597
598         if (shared_area_disabled ()) {
599                 if (!malloced_shared_area)
600                         malloced_shared_area = malloc_shared_area (0);
601                 /* get the pid here */
602                 return malloced_shared_area;
603         }
604
605         /* perform cleanup of segments left over from dead processes */
606         mono_shared_area_instances_helper (NULL, 0, TRUE);
607
608         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
609
610         fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
611         if (fd == -1 && errno == EEXIST) {
612                 /* leftover */
613                 shm_unlink (buf);
614                 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
615         }
616         /* in case of failure we try to return a memory area anyway,
617          * even if it means the data can't be read by other processes
618          */
619         if (fd == -1)
620                 return malloc_shared_area (pid);
621         if (ftruncate (fd, size) != 0) {
622                 shm_unlink (buf);
623                 close (fd);
624         }
625         BEGIN_CRITICAL_SECTION;
626         res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
627         END_CRITICAL_SECTION;
628
629         if (res == MAP_FAILED) {
630                 shm_unlink (buf);
631                 close (fd);
632                 return malloc_shared_area (pid);
633         }
634         /* we don't need the file descriptor anymore */
635         close (fd);
636         header = (SAreaHeader *) res;
637         header->size = size;
638         header->pid = pid;
639         header->stats_start = sizeof (SAreaHeader);
640         header->stats_end = sizeof (SAreaHeader);
641
642         mono_atexit (mono_shared_area_remove);
643         return res;
644 }
645
646 void
647 mono_shared_area_remove (void)
648 {
649         char buf [128];
650
651         if (shared_area_disabled ()) {
652                 if (malloced_shared_area)
653                         g_free (malloced_shared_area);
654                 return;
655         }
656
657         g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
658         shm_unlink (buf);
659         if (malloced_shared_area)
660                 g_free (malloced_shared_area);
661 }
662
663 void*
664 mono_shared_area_for_pid (void *pid)
665 {
666         int fd;
667         /* we should allow the user to configure the size */
668         int size = mono_pagesize ();
669         char buf [128];
670         void *res;
671
672         if (shared_area_disabled ())
673                 return NULL;
674
675         g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
676
677         fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
678         if (fd == -1)
679                 return NULL;
680         BEGIN_CRITICAL_SECTION;
681         res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
682         END_CRITICAL_SECTION;
683
684         if (res == MAP_FAILED) {
685                 close (fd);
686                 return NULL;
687         }
688         /* FIXME: validate the area */
689         /* we don't need the file descriptor anymore */
690         close (fd);
691         return res;
692 }
693
694 void
695 mono_shared_area_unload  (void *area)
696 {
697         /* FIXME: currently we load only a page */
698         BEGIN_CRITICAL_SECTION;
699         munmap (area, mono_pagesize ());
700         END_CRITICAL_SECTION;
701 }
702
703 int
704 mono_shared_area_instances (void **array, int count)
705 {
706         return mono_shared_area_instances_helper (array, count, FALSE);
707 }
708 #else
709 void*
710 mono_shared_area (void)
711 {
712         if (!malloced_shared_area)
713                 malloced_shared_area = malloc_shared_area (getpid ());
714         /* get the pid here */
715         return malloced_shared_area;
716 }
717
718 void
719 mono_shared_area_remove (void)
720 {
721         if (malloced_shared_area)
722                 g_free (malloced_shared_area);
723         malloced_shared_area = NULL;
724 }
725
726 void*
727 mono_shared_area_for_pid (void *pid)
728 {
729         return NULL;
730 }
731
732 void
733 mono_shared_area_unload (void *area)
734 {
735 }
736
737 int
738 mono_shared_area_instances (void **array, int count)
739 {
740         return 0;
741 }
742
743 #endif // HAVE_SHM_OPEN
744
745 #endif // HOST_WIN32
746
747 #ifndef HAVE_VALLOC_ALIGNED
748 void*
749 mono_valloc_aligned (size_t size, size_t alignment, int flags)
750 {
751         /* Allocate twice the memory to be able to put the block on an aligned address */
752         char *mem = (char *) mono_valloc (NULL, size + alignment, flags);
753         char *aligned;
754
755         if (!mem)
756                 return NULL;
757
758         aligned = aligned_address (mem, size, alignment);
759
760         if (aligned > mem)
761                 mono_vfree (mem, aligned - mem);
762         if (aligned + size < mem + size + alignment)
763                 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size));
764
765         return aligned;
766 }
767 #endif
768
769 int
770 mono_pages_not_faulted (void *addr, size_t size)
771 {
772 #ifdef HAVE_MINCORE
773         int i;
774         gint64 count;
775         int pagesize = mono_pagesize ();
776         int npages = (size + pagesize - 1) / pagesize;
777         char *faulted = (char *) g_malloc0 (sizeof (char*) * npages);
778
779         /*
780          * We cast `faulted` to void* because Linux wants an unsigned
781          * char* while BSD wants a char*.
782          */
783 #ifdef __linux__
784         if (mincore (addr, size, (unsigned char *)faulted) != 0) {
785 #else
786         if (mincore (addr, size, (char *)faulted) != 0) {
787 #endif
788                 count = -1;
789         } else {
790                 count = 0;
791                 for (i = 0; i < npages; ++i) {
792                         if (faulted [i] != 0)
793                                 ++count;
794                 }
795         }
796
797         g_free (faulted);
798
799         return count;
800 #else
801         return -1;
802 #endif
803 }