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