2 * mono-mmap.c: Support for mapping code into the process address space
5 * Mono Team (mono-list@lists.ximian.com)
7 * Copyright 2001-2008 Novell, Inc.
16 #include <sys/types.h>
31 #include "mono-mmap.h"
32 #include "mono-proclib.h"
35 #define MAP_ANONYMOUS MAP_ANON
50 static void* malloced_shared_area
= NULL
;
53 malloc_shared_area (int pid
)
55 int size
= mono_pagesize ();
56 SAreaHeader
*sarea
= g_malloc0 (size
);
59 sarea
->stats_start
= sizeof (SAreaHeader
);
60 sarea
->stats_end
= sizeof (SAreaHeader
);
71 static int saved_pagesize
= 0;
73 return saved_pagesize
;
74 GetSystemInfo (&info
);
75 saved_pagesize
= info
.dwAllocationGranularity
;
76 return saved_pagesize
;
80 prot_from_flags (int flags
)
82 int prot
= flags
& (MONO_MMAP_READ
|MONO_MMAP_WRITE
|MONO_MMAP_EXEC
);
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;
93 g_assert_not_reached ();
99 mono_valloc (void *addr
, size_t length
, int flags
)
102 int mflags
= MEM_COMMIT
;
103 int prot
= prot_from_flags (flags
);
104 /* translate the flags */
106 ptr
= VirtualAlloc (addr
, length
, mflags
, prot
);
111 mono_vfree (void *addr
, size_t length
)
113 int res
= VirtualFree (addr
, 0, MEM_RELEASE
);
121 mono_file_map (size_t length
, int flags
, int fd
, guint64 offset
, void **ret_handle
)
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)
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
);
147 ptr
= MapViewOfFile (mapping
, mflags
, 0, offset
, length
);
149 CloseHandle (mapping
);
152 *ret_handle
= (void*)mapping
;
157 mono_file_unmap (void *addr
, void *handle
)
159 UnmapViewOfFile (addr
);
160 CloseHandle ((HANDLE
)handle
);
165 mono_mprotect (void *addr
, size_t length
, int flags
)
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
);
175 return VirtualProtect (addr
, length
, prot
, &oldprot
) == 0;
179 mono_shared_area (void)
181 /* get the pid here */
182 return malloc_shared_area (0);
186 mono_shared_area_remove (void)
188 if (malloced_shared_area
)
189 g_free (malloced_shared_area
);
193 mono_shared_area_for_pid (void *pid
)
199 mono_shared_area_unload (void *area
)
204 mono_shared_area_instances (void **array
, int count
)
210 #if defined(HAVE_MMAP)
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
218 * Returns: the page size in bytes.
223 static int saved_pagesize
= 0;
225 return saved_pagesize
;
226 saved_pagesize
= getpagesize ();
227 return saved_pagesize
;
231 prot_from_flags (int flags
)
233 int prot
= PROT_NONE
;
234 /* translate the protection bits */
235 if (flags
& MONO_MMAP_READ
)
237 if (flags
& MONO_MMAP_WRITE
)
239 if (flags
& MONO_MMAP_EXEC
)
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
259 mono_valloc (void *addr
, size_t length
, int flags
)
263 int prot
= prot_from_flags (flags
);
264 /* translate the flags */
265 if (flags
& MONO_MMAP_FIXED
)
267 if (flags
& MONO_MMAP_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
);
277 ptr
= mmap (addr
, length
, prot
, mflags
, fd
, 0);
280 if (ptr
== (void*)-1)
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
);
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
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 ().
318 mono_file_map (size_t length
, int flags
, int fd
, guint64 offset
, void **ret_handle
)
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
)
330 if (flags
& MONO_MMAP_32BIT
)
333 ptr
= mmap (0, length
, prot
, mflags
, fd
, offset
);
334 if (ptr
== (void*)-1)
336 *ret_handle
= (void*)length
;
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
);
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) */
379 if (madvise (addr
, length
, MADV_DONTNEED
))
380 memset (addr
, 0, length
);
382 memset (addr
, 0, length
);
384 madvise (addr
, length
, MADV_DONTNEED
);
385 madvise (addr
, length
, MADV_FREE
);
387 posix_madvise (addr
, length
, POSIX_MADV_DONTNEED
);
391 return mprotect (addr
, length
, prot
);
396 /* dummy malloc-based implementation */
404 mono_valloc (void *addr
, size_t length
, int flags
)
406 return malloc (length
);
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
)
425 mono_mprotect (void *addr
, size_t length
, int flags
)
427 if (flags
& MONO_MMAP_DISCARD
) {
428 memset (addr
, 0, length
);
434 #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
437 mono_shared_area_instances_slow (void **array
, int count
, gboolean cleanup
)
442 gpointer
*processes
= mono_process_list (&num
);
443 for (i
= 0; i
< num
; ++i
) {
444 data
= mono_shared_area_for_pid (processes
[i
]);
447 mono_shared_area_unload (data
);
450 array
[j
++] = processes
[i
];
460 mono_shared_area_instances_helper (void **array
, int count
, gboolean cleanup
)
464 int curpid
= getpid ();
465 GDir
*dir
= g_dir_open ("/dev/shm/", 0, NULL
);
467 return mono_shared_area_instances_slow (array
, count
, cleanup
);
468 while ((name
= g_dir_read_name (dir
))) {
471 if (strncmp (name
, "mono.", 5))
473 pid
= strtol (name
+ 5, &nend
, 10);
474 if (pid
<= 0 || nend
== name
+ 5 || *nend
)
478 array
[i
++] = GINT_TO_POINTER (pid
);
482 if (curpid
!= pid
&& kill (pid
, 0) == -1 && (errno
== ESRCH
|| errno
== ENOMEM
)) {
484 g_snprintf (buf
, sizeof (buf
), "/mono.%d", pid
);
493 mono_shared_area (void)
497 /* we should allow the user to configure the size */
498 int size
= mono_pagesize ();
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
) {
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
518 return malloc_shared_area (pid
);
519 if (ftruncate (fd
, size
) != 0) {
523 res
= mmap (NULL
, size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
524 if (res
== MAP_FAILED
) {
527 return malloc_shared_area (pid
);
529 /* we don't need the file descriptor anymore */
534 header
->stats_start
= sizeof (SAreaHeader
);
535 header
->stats_end
= sizeof (SAreaHeader
);
537 atexit (mono_shared_area_remove
);
542 mono_shared_area_remove (void)
545 g_snprintf (buf
, sizeof (buf
), "/mono.%d", getpid ());
547 if (malloced_shared_area
)
548 g_free (malloced_shared_area
);
552 mono_shared_area_for_pid (void *pid
)
555 /* we should allow the user to configure the size */
556 int size
= mono_pagesize ();
560 g_snprintf (buf
, sizeof (buf
), "/mono.%d", GPOINTER_TO_INT (pid
));
562 fd
= shm_open (buf
, O_RDONLY
, S_IRUSR
|S_IRGRP
);
565 res
= mmap (NULL
, size
, PROT_READ
, MAP_SHARED
, fd
, 0);
566 if (res
== MAP_FAILED
) {
570 /* FIXME: validate the area */
571 /* we don't need the file descriptor anymore */
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
);
590 mono_shared_area (void)
592 return malloc_shared_area (getpid ());
596 mono_shared_area_remove (void)
598 if (malloced_shared_area
)
599 g_free (malloced_shared_area
);
600 malloced_shared_area
= NULL
;
604 mono_shared_area_for_pid (void *pid
)
610 mono_shared_area_unload (void *area
)
615 mono_shared_area_instances (void **array
, int count
)
620 #endif // HAVE_SHM_OPEN
624 #ifndef HAVE_VALLOC_ALIGNED
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
);
634 aligned
= (char*)((gulong
)(mem
+ (alignment
- 1)) & ~(alignment
- 1));
635 g_assert (aligned
>= mem
&& aligned
+ size
<= mem
+ size
+ alignment
&& !((gulong
)aligned
& (alignment
- 1)));
638 mono_vfree (mem
, aligned
- mem
);
639 if (aligned
+ size
< mem
+ size
+ alignment
)
640 mono_vfree (aligned
+ size
, (mem
+ size
+ alignment
) - (aligned
+ size
));