Fix windows build.
[mono-project/dkf.git] / mono / utils / mono-mmap.c
blob17af2ab59e8b853ad7cc712b6c029d125dc15567
1 /*
2 * mono-mmap.c: Support for mapping code into the process address space
4 * Author:
5 * Mono Team (mono-list@lists.ximian.com)
7 * Copyright 2001-2008 Novell, Inc.
8 */
10 #include "config.h"
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
31 #include "mono-mmap.h"
32 #include "mono-proclib.h"
34 #ifndef MAP_ANONYMOUS
35 #define MAP_ANONYMOUS MAP_ANON
36 #endif
38 #ifndef MAP_32BIT
39 #define MAP_32BIT 0
40 #endif
42 typedef struct {
43 int size;
44 int pid;
45 int reserved;
46 short stats_start;
47 short stats_end;
48 } SAreaHeader;
50 static void* malloced_shared_area = NULL;
52 static void*
53 malloc_shared_area (int pid)
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);
62 return sarea;
65 #ifdef HOST_WIN32
67 int
68 mono_pagesize (void)
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;
79 static int
80 prot_from_flags (int flags)
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 ();
95 return prot;
98 void*
99 mono_valloc (void *addr, size_t length, int flags)
101 void *ptr;
102 int mflags = MEM_COMMIT;
103 int prot = prot_from_flags (flags);
104 /* translate the flags */
106 ptr = VirtualAlloc (addr, length, mflags, prot);
107 return ptr;
111 mono_vfree (void *addr, size_t length)
113 int res = VirtualFree (addr, 0, MEM_RELEASE);
115 g_assert (res);
117 return 0;
120 void*
121 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
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;*/
139 mflags = FILE_MAP_READ;
140 if (flags & MONO_MMAP_WRITE)
141 mflags = FILE_MAP_COPY;
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;
152 *ret_handle = (void*)mapping;
153 return ptr;
157 mono_file_unmap (void *addr, void *handle)
159 UnmapViewOfFile (addr);
160 CloseHandle ((HANDLE)handle);
161 return 0;
165 mono_mprotect (void *addr, size_t length, int flags)
167 DWORD oldprot;
168 int prot = prot_from_flags (flags);
170 if (flags & MONO_MMAP_DISCARD) {
171 VirtualFree (addr, length, MEM_DECOMMIT);
172 VirtualAlloc (addr, length, MEM_COMMIT, prot);
173 return 0;
175 return VirtualProtect (addr, length, prot, &oldprot) == 0;
178 void*
179 mono_shared_area (void)
181 /* get the pid here */
182 return malloc_shared_area (0);
185 void
186 mono_shared_area_remove (void)
188 if (malloced_shared_area)
189 g_free (malloced_shared_area);
192 void*
193 mono_shared_area_for_pid (void *pid)
195 return NULL;
198 void
199 mono_shared_area_unload (void *area)
204 mono_shared_area_instances (void **array, int count)
206 return 0;
209 #else
210 #if defined(HAVE_MMAP)
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.
218 * Returns: the page size in bytes.
221 mono_pagesize (void)
223 static int saved_pagesize = 0;
224 if (saved_pagesize)
225 return saved_pagesize;
226 saved_pagesize = getpagesize ();
227 return saved_pagesize;
230 static int
231 prot_from_flags (int flags)
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;
245 * mono_valloc:
246 * @addr: memory address
247 * @length: memory area size
248 * @flags: protection flags
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.
256 * Returns: NULL on failure, the address of the memory area otherwise
258 void*
259 mono_valloc (void *addr, size_t length, int flags)
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;
270 mflags |= MAP_ANONYMOUS;
271 mflags |= MAP_PRIVATE;
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);
280 if (ptr == (void*)-1)
281 return NULL;
283 return ptr;
287 * mono_vfree:
288 * @addr: memory address returned by mono_valloc ()
289 * @length: size of memory area
291 * Remove the memory mapping at the address @addr.
293 * Returns: 0 on success.
296 mono_vfree (void *addr, size_t length)
298 return munmap (addr, length);
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
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 ().
317 void*
318 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
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;
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;
341 * mono_file_unmap:
342 * @addr: memory address returned by mono_file_map ()
343 * @handle: handle of memory map
345 * Remove the memory mapping at the address @addr.
346 * @handle must be the value returned in ret_handle by mono_file_map ().
348 * Returns: 0 on success.
351 mono_file_unmap (void *addr, void *handle)
353 return munmap (addr, (size_t)handle);
357 * mono_mprotect:
358 * @addr: memory address
359 * @length: size of memory area
360 * @flags: new protection flags
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.
369 * Returns: 0 on success.
372 mono_mprotect (void *addr, size_t length, int flags)
374 int prot = prot_from_flags (flags);
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
391 return mprotect (addr, length, prot);
394 #else
396 /* dummy malloc-based implementation */
398 mono_pagesize (void)
400 return 4096;
403 void*
404 mono_valloc (void *addr, size_t length, int flags)
406 return malloc (length);
409 void*
410 mono_valloc_aligned (size_t length, size_t alignment, int flags)
412 g_assert_not_reached ();
415 #define HAVE_VALLOC_ALIGNED
418 mono_vfree (void *addr, size_t length)
420 free (addr);
421 return 0;
425 mono_mprotect (void *addr, size_t length, int flags)
427 if (flags & MONO_MMAP_DISCARD) {
428 memset (addr, 0, length);
430 return 0;
432 #endif // HAVE_MMAP
434 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
436 static int
437 mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
439 int i, j = 0;
440 int num;
441 void *data;
442 gpointer *processes = mono_process_list (&num);
443 for (i = 0; i < num; ++i) {
444 data = mono_shared_area_for_pid (processes [i]);
445 if (!data)
446 continue;
447 mono_shared_area_unload (data);
448 if (!cleanup) {
449 if (j < count)
450 array [j++] = processes [i];
451 else
452 break;
455 g_free (processes);
456 return j;
459 static int
460 mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
462 const char *name;
463 int i = 0;
464 int curpid = getpid ();
465 GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
466 if (!dir)
467 return mono_shared_area_instances_slow (array, count, cleanup);
468 while ((name = g_dir_read_name (dir))) {
469 int pid;
470 char *nend;
471 if (strncmp (name, "mono.", 5))
472 continue;
473 pid = strtol (name + 5, &nend, 10);
474 if (pid <= 0 || nend == name + 5 || *nend)
475 continue;
476 if (!cleanup) {
477 if (i < count)
478 array [i++] = GINT_TO_POINTER (pid);
479 else
480 break;
482 if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
483 char buf [128];
484 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
485 shm_unlink (buf);
488 g_dir_close (dir);
489 return i;
492 void*
493 mono_shared_area (void)
495 int fd;
496 int pid = getpid ();
497 /* we should allow the user to configure the size */
498 int size = mono_pagesize ();
499 char buf [128];
500 void *res;
501 SAreaHeader *header;
503 /* perform cleanup of segments left over from dead processes */
504 mono_shared_area_instances_helper (NULL, 0, TRUE);
506 g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
508 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
509 if (fd == -1 && errno == EEXIST) {
510 /* leftover */
511 shm_unlink (buf);
512 fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
514 /* in case of failure we try to return a memory area anyway,
515 * even if it means the data can't be read by other processes
517 if (fd == -1)
518 return malloc_shared_area (pid);
519 if (ftruncate (fd, size) != 0) {
520 shm_unlink (buf);
521 close (fd);
523 res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
524 if (res == MAP_FAILED) {
525 shm_unlink (buf);
526 close (fd);
527 return malloc_shared_area (pid);
529 /* we don't need the file descriptor anymore */
530 close (fd);
531 header = res;
532 header->size = size;
533 header->pid = pid;
534 header->stats_start = sizeof (SAreaHeader);
535 header->stats_end = sizeof (SAreaHeader);
537 atexit (mono_shared_area_remove);
538 return res;
541 void
542 mono_shared_area_remove (void)
544 char buf [128];
545 g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
546 shm_unlink (buf);
547 if (malloced_shared_area)
548 g_free (malloced_shared_area);
551 void*
552 mono_shared_area_for_pid (void *pid)
554 int fd;
555 /* we should allow the user to configure the size */
556 int size = mono_pagesize ();
557 char buf [128];
558 void *res;
560 g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
562 fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
563 if (fd == -1)
564 return NULL;
565 res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
566 if (res == MAP_FAILED) {
567 close (fd);
568 return NULL;
570 /* FIXME: validate the area */
571 /* we don't need the file descriptor anymore */
572 close (fd);
573 return res;
576 void
577 mono_shared_area_unload (void *area)
579 /* FIXME: currently we load only a page */
580 munmap (area, mono_pagesize ());
584 mono_shared_area_instances (void **array, int count)
586 return mono_shared_area_instances_helper (array, count, FALSE);
588 #else
589 void*
590 mono_shared_area (void)
592 return malloc_shared_area (getpid ());
595 void
596 mono_shared_area_remove (void)
598 if (malloced_shared_area)
599 g_free (malloced_shared_area);
600 malloced_shared_area = NULL;
603 void*
604 mono_shared_area_for_pid (void *pid)
606 return NULL;
609 void
610 mono_shared_area_unload (void *area)
615 mono_shared_area_instances (void **array, int count)
617 return 0;
620 #endif // HAVE_SHM_OPEN
622 #endif // HOST_WIN32
624 #ifndef HAVE_VALLOC_ALIGNED
625 void*
626 mono_valloc_aligned (size_t size, size_t alignment, int flags)
628 /* Allocate twice the memory to be able to put the block on an aligned address */
629 char *mem = mono_valloc (NULL, size + alignment, flags);
630 char *aligned;
632 g_assert (mem);
634 aligned = (char*)((gulong)(mem + (alignment - 1)) & ~(alignment - 1));
635 g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((gulong)aligned & (alignment - 1)));
637 if (aligned > mem)
638 mono_vfree (mem, aligned - mem);
639 if (aligned + size < mem + size + alignment)
640 mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size));
642 return aligned;
644 #endif