Avoid assert and crash when allocating smaller large objects in sgen.
[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         /* get the pid here */
216         return malloc_shared_area (0);
217 }
218
219 void
220 mono_shared_area_remove (void)
221 {
222         if (malloced_shared_area)
223                 g_free (malloced_shared_area);
224 }
225
226 void*
227 mono_shared_area_for_pid (void *pid)
228 {
229         return NULL;
230 }
231
232 void
233 mono_shared_area_unload (void *area)
234 {
235 }
236
237 int
238 mono_shared_area_instances (void **array, int count)
239 {
240         return 0;
241 }
242
243 #else
244 #if defined(HAVE_MMAP)
245
246 /**
247  * mono_pagesize:
248  * Get the page size in use on the system. Addresses and sizes in the
249  * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
250  * aligned.
251  *
252  * Returns: the page size in bytes.
253  */
254 int
255 mono_pagesize (void)
256 {
257         static int saved_pagesize = 0;
258         if (saved_pagesize)
259                 return saved_pagesize;
260         saved_pagesize = getpagesize ();
261         return saved_pagesize;
262 }
263
264 static int
265 prot_from_flags (int flags)
266 {
267         int prot = PROT_NONE;
268         /* translate the protection bits */
269         if (flags & MONO_MMAP_READ)
270                 prot |= PROT_READ;
271         if (flags & MONO_MMAP_WRITE)
272                 prot |= PROT_WRITE;
273         if (flags & MONO_MMAP_EXEC)
274                 prot |= PROT_EXEC;
275         return prot;
276 }
277
278 /**
279  * mono_valloc:
280  * @addr: memory address
281  * @length: memory area size
282  * @flags: protection flags
283  *
284  * Allocates @length bytes of virtual memory with the @flags
285  * protection. @addr can be a preferred memory address or a
286  * mandatory one if MONO_MMAP_FIXED is set in @flags.
287  * @addr must be pagesize aligned and can be NULL.
288  * @length must be a multiple of pagesize.
289  *
290  * Returns: NULL on failure, the address of the memory area otherwise
291  */
292 void*
293 mono_valloc (void *addr, size_t length, int flags)
294 {
295         void *ptr;
296         int mflags = 0;
297         int prot = prot_from_flags (flags);
298         /* translate the flags */
299         if (flags & MONO_MMAP_FIXED)
300                 mflags |= MAP_FIXED;
301         if (flags & MONO_MMAP_32BIT)
302                 mflags |= MAP_32BIT;
303
304         mflags |= MAP_ANONYMOUS;
305         mflags |= MAP_PRIVATE;
306
307         ptr = mmap (addr, length, prot, mflags, -1, 0);
308         if (ptr == (void*)-1) {
309                 int fd = open ("/dev/zero", O_RDONLY);
310                 if (fd != -1) {
311                         ptr = mmap (addr, length, prot, mflags, fd, 0);
312                         close (fd);
313                 }
314                 if (ptr == (void*)-1)
315                         return NULL;
316         }
317         return ptr;
318 }
319
320 /**
321  * mono_vfree:
322  * @addr: memory address returned by mono_valloc ()
323  * @length: size of memory area
324  *
325  * Remove the memory mapping at the address @addr.
326  *
327  * Returns: 0 on success.
328  */
329 int
330 mono_vfree (void *addr, size_t length)
331 {
332         return munmap (addr, length);
333 }
334
335 /**
336  * mono_file_map:
337  * @length: size of data to map
338  * @flags: protection flags
339  * @fd: file descriptor
340  * @offset: offset in the file
341  * @ret_handle: pointer to storage for returning a handle for the map
342  *
343  * Map the area of the file pointed to by the file descriptor @fd, at offset
344  * @offset and of size @length in memory according to the protection flags
345  * @flags.
346  * @offset and @length must be multiples of the page size.
347  * @ret_handle must point to a void*: this value must be used when unmapping
348  * the memory area using mono_file_unmap ().
349  *
350  */
351 void*
352 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
353 {
354         void *ptr;
355         int mflags = 0;
356         int prot = prot_from_flags (flags);
357         /* translate the flags */
358         if (flags & MONO_MMAP_PRIVATE)
359                 mflags |= MAP_PRIVATE;
360         if (flags & MONO_MMAP_SHARED)
361                 mflags |= MAP_SHARED;
362         if (flags & MONO_MMAP_FIXED)
363                 mflags |= MAP_FIXED;
364         if (flags & MONO_MMAP_32BIT)
365                 mflags |= MAP_32BIT;
366
367         ptr = mmap (0, length, prot, mflags, fd, offset);
368         if (ptr == (void*)-1)
369                 return NULL;
370         *ret_handle = (void*)length;
371         return ptr;
372 }
373
374 /**
375  * mono_file_unmap:
376  * @addr: memory address returned by mono_file_map ()
377  * @handle: handle of memory map
378  *
379  * Remove the memory mapping at the address @addr.
380  * @handle must be the value returned in ret_handle by mono_file_map ().
381  *
382  * Returns: 0 on success.
383  */
384 int
385 mono_file_unmap (void *addr, void *handle)
386 {
387         return munmap (addr, (size_t)handle);
388 }
389
390 /**
391  * mono_mprotect:
392  * @addr: memory address
393  * @length: size of memory area
394  * @flags: new protection flags
395  *
396  * Change the protection for the memory area at @addr for @length bytes
397  * to matche the supplied @flags.
398  * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
399  * and the area is cleared to zero.
400  * @addr must be aligned to the page size.
401  * @length must be a multiple of the page size.
402  *
403  * Returns: 0 on success.
404  */
405 int
406 mono_mprotect (void *addr, size_t length, int flags)
407 {
408         int prot = prot_from_flags (flags);
409
410         if (flags & MONO_MMAP_DISCARD) {
411                 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
412 #ifdef __linux__
413                 if (madvise (addr, length, MADV_DONTNEED))
414                         memset (addr, 0, length);
415 #else
416                 memset (addr, 0, length);
417 #ifdef HAVE_MADVISE
418                 madvise (addr, length, MADV_DONTNEED);
419                 madvise (addr, length, MADV_FREE);
420 #else
421                 posix_madvise (addr, length, POSIX_MADV_DONTNEED);
422 #endif
423 #endif
424         }
425         return mprotect (addr, length, prot);
426 }
427
428 #else
429
430 /* dummy malloc-based implementation */
431 int
432 mono_pagesize (void)
433 {
434         return 4096;
435 }
436
437 void*
438 mono_valloc (void *addr, size_t length, int flags)
439 {
440         return malloc (length);
441 }
442
443 void*
444 mono_valloc_aligned (size_t length, size_t alignment, int flags)
445 {
446         g_assert_not_reached ();
447 }
448
449 #define HAVE_VALLOC_ALIGNED
450
451 int
452 mono_vfree (void *addr, size_t length)
453 {
454         free (addr);
455         return 0;
456 }
457
458 int
459 mono_mprotect (void *addr, size_t length, int flags)
460 {
461         if (flags & MONO_MMAP_DISCARD) {
462                 memset (addr, 0, length);
463         }
464         return 0;
465 }
466 #endif // HAVE_MMAP
467
468 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
469
470 static int
471 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
472 {
473         int i, j = 0;
474         int num;
475         void *data;
476         gpointer *processes = mono_process_list (&num);
477         for (i = 0; i < num; ++i) {
478                 data = mono_shared_area_for_pid (processes [i]);
479                 if (!data)
480                         continue;
481                 mono_shared_area_unload (data);
482                 if (!cleanup) {
483                         if (j < count)
484                                 array [j++] = processes [i];
485                         else
486                                 break;
487                 }
488         }
489         g_free (processes);
490         return j;
491 }
492
493 static int
494 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
495 {
496         const char *name;
497         int i = 0;
498         int curpid = getpid ();
499         GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
500         if (!dir)
501                 return mono_shared_area_instances_slow (array, count, cleanup);
502         while ((name = g_dir_read_name (dir))) {
503                 int pid;
504                 char *nend;
505                 if (strncmp (name, "mono.", 5))
506                         continue;
507                 pid = strtol (name + 5, &nend, 10);
508                 if (pid <= 0 || nend == name + 5 || *nend)
509                         continue;
510                 if (!cleanup) {
511                         if (i < count)
512                                 array [i++] = GINT_TO_POINTER (pid);
513                         else
514                                 break;
515                 }
516                 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
517                         char buf [128];
518                         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
519                         shm_unlink (buf);
520                 }
521         }
522         g_dir_close (dir);
523         return i;
524 }
525
526 void*
527 mono_shared_area (void)
528 {
529         int fd;
530         int pid = getpid ();
531         /* we should allow the user to configure the size */
532         int size = mono_pagesize ();
533         char buf [128];
534         void *res;
535         SAreaHeader *header;
536
537         /* perform cleanup of segments left over from dead processes */
538         mono_shared_area_instances_helper (NULL, 0, TRUE);
539
540         g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
541
542         fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
543         if (fd == -1 && errno == EEXIST) {
544                 /* leftover */
545                 shm_unlink (buf);
546                 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
547         }
548         /* in case of failure we try to return a memory area anyway,
549          * even if it means the data can't be read by other processes
550          */
551         if (fd == -1)
552                 return malloc_shared_area (pid);
553         if (ftruncate (fd, size) != 0) {
554                 shm_unlink (buf);
555                 close (fd);
556         }
557         res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
558         if (res == MAP_FAILED) {
559                 shm_unlink (buf);
560                 close (fd);
561                 return malloc_shared_area (pid);
562         }
563         /* we don't need the file descriptor anymore */
564         close (fd);
565         header = res;
566         header->size = size;
567         header->pid = pid;
568         header->stats_start = sizeof (SAreaHeader);
569         header->stats_end = sizeof (SAreaHeader);
570
571         atexit (mono_shared_area_remove);
572         return res;
573 }
574
575 void
576 mono_shared_area_remove (void)
577 {
578         char buf [128];
579         g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
580         shm_unlink (buf);
581         if (malloced_shared_area)
582                 g_free (malloced_shared_area);
583 }
584
585 void*
586 mono_shared_area_for_pid (void *pid)
587 {
588         int fd;
589         /* we should allow the user to configure the size */
590         int size = mono_pagesize ();
591         char buf [128];
592         void *res;
593
594         g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
595
596         fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
597         if (fd == -1)
598                 return NULL;
599         res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
600         if (res == MAP_FAILED) {
601                 close (fd);
602                 return NULL;
603         }
604         /* FIXME: validate the area */
605         /* we don't need the file descriptor anymore */
606         close (fd);
607         return res;
608 }
609
610 void
611 mono_shared_area_unload  (void *area)
612 {
613         /* FIXME: currently we load only a page */
614         munmap (area, mono_pagesize ());
615 }
616
617 int
618 mono_shared_area_instances (void **array, int count)
619 {
620         return mono_shared_area_instances_helper (array, count, FALSE);
621 }
622 #else
623 void*
624 mono_shared_area (void)
625 {
626         return malloc_shared_area (getpid ());
627 }
628
629 void
630 mono_shared_area_remove (void)
631 {
632         if (malloced_shared_area)
633                 g_free (malloced_shared_area);
634         malloced_shared_area = NULL;
635 }
636
637 void*
638 mono_shared_area_for_pid (void *pid)
639 {
640         return NULL;
641 }
642
643 void
644 mono_shared_area_unload (void *area)
645 {
646 }
647
648 int
649 mono_shared_area_instances (void **array, int count)
650 {
651         return 0;
652 }
653
654 #endif // HAVE_SHM_OPEN
655
656 #endif // HOST_WIN32
657
658 #ifndef HAVE_VALLOC_ALIGNED
659 void*
660 mono_valloc_aligned (size_t size, size_t alignment, int flags)
661 {
662         /* Allocate twice the memory to be able to put the block on an aligned address */
663         char *mem = mono_valloc (NULL, size + alignment, flags);
664         char *aligned;
665
666         if (!mem)
667                 return NULL;
668
669         aligned = aligned_address (mem, size, alignment);
670
671         if (aligned > mem)
672                 mono_vfree (mem, aligned - mem);
673         if (aligned + size < mem + size + alignment)
674                 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size));
675
676         return aligned;
677 }
678 #endif