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