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