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