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