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