2007-10-11 Zoltan Varga <vargaz@freemail.hu>
[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 #endif
15
16 #include "mono-mmap.h"
17
18 #ifndef MAP_ANONYMOUS
19 #define MAP_ANONYMOUS MAP_ANON
20 #endif
21
22 #ifndef MAP_32BIT
23 #define MAP_32BIT 0
24 #endif
25
26 #ifdef PLATFORM_WIN32
27
28 int
29 mono_pagesize (void)
30 {
31         SYSTEM_INFO info;
32         static int saved_pagesize = 0;
33         if (saved_pagesize)
34                 return saved_pagesize;
35         GetSystemInfo (&info);
36         saved_pagesize = info.dwAllocationGranularity;
37         return saved_pagesize;
38 }
39
40 static int
41 prot_from_flags (int flags)
42 {
43         int prot = flags & (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC);
44         switch (prot) {
45         case 0: prot = PAGE_NOACCESS; break;
46         case MONO_MMAP_READ: prot = PAGE_READONLY; break;
47         case MONO_MMAP_READ|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READ; break;
48         case MONO_MMAP_READ|MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
49         case MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
50         case MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
51         case MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
52         case MONO_MMAP_EXEC: prot = PAGE_EXECUTE; break;
53         default:
54                 g_assert_not_reached ();
55         }
56         return prot;
57 }
58
59 void*
60 mono_valloc (void *addr, size_t length, int flags)
61 {
62         void *ptr;
63         int mflags = MEM_COMMIT;
64         int prot = prot_from_flags (flags);
65         /* translate the flags */
66
67         ptr = VirtualAlloc (addr, length, mflags, prot);
68         return ptr;
69 }
70
71 int
72 mono_vfree (void *addr, size_t length)
73 {
74         int res = VirtualFree (addr, 0, MEM_RELEASE);
75
76         g_assert (res);
77
78         return 0;
79 }
80
81 void*
82 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
83 {
84         void *ptr;
85         int mflags = 0;
86         HANDLE file, mapping;
87         int prot = prot_from_flags (flags);
88         /* translate the flags */
89         /*if (flags & MONO_MMAP_PRIVATE)
90                 mflags |= MAP_PRIVATE;
91         if (flags & MONO_MMAP_SHARED)
92                 mflags |= MAP_SHARED;
93         if (flags & MONO_MMAP_ANON)
94                 mflags |= MAP_ANONYMOUS;
95         if (flags & MONO_MMAP_FIXED)
96                 mflags |= MAP_FIXED;
97         if (flags & MONO_MMAP_32BIT)
98                 mflags |= MAP_32BIT;*/
99
100         mflags = FILE_MAP_READ;
101         if (flags & MONO_MMAP_WRITE)
102                 mflags = FILE_MAP_COPY;
103
104         file = (HANDLE) _get_osfhandle (fd);
105         mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
106         if (mapping == NULL)
107                 return NULL;
108         ptr = MapViewOfFile (mapping, mflags, 0, offset, length);
109         if (ptr == NULL) {
110                 CloseHandle (mapping);
111                 return NULL;
112         }
113         *ret_handle = (void*)mapping;
114         return ptr;
115 }
116
117 int
118 mono_file_unmap (void *addr, void *handle)
119 {
120         UnmapViewOfFile (addr);
121         CloseHandle ((HANDLE)handle);
122         return 0;
123 }
124
125 int
126 mono_mprotect (void *addr, size_t length, int flags)
127 {
128         DWORD oldprot;
129         int prot = prot_from_flags (flags);
130
131         if (flags & MONO_MMAP_DISCARD) {
132                 VirtualFree (addr, length, MEM_DECOMMIT);
133                 VirtualAlloc (addr, length, MEM_COMMIT, prot);
134                 return 0;
135         }
136         return VirtualProtect (addr, length, prot, &oldprot) == 0;
137 }
138
139 #elif defined(HAVE_MMAP)
140
141 /**
142  * mono_pagesize:
143  * Get the page size in use on the system. Addresses and sizes in the
144  * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
145  * aligned.
146  *
147  * Returns: the page size in bytes.
148  */
149 int
150 mono_pagesize (void)
151 {
152         static int saved_pagesize = 0;
153         if (saved_pagesize)
154                 return saved_pagesize;
155         saved_pagesize = getpagesize ();
156         return saved_pagesize;
157 }
158
159 static int
160 prot_from_flags (int flags)
161 {
162         int prot = PROT_NONE;
163         /* translate the protection bits */
164         if (flags & MONO_MMAP_READ)
165                 prot |= PROT_READ;
166         if (flags & MONO_MMAP_WRITE)
167                 prot |= PROT_WRITE;
168         if (flags & MONO_MMAP_EXEC)
169                 prot |= PROT_EXEC;
170         return prot;
171 }
172
173 /**
174  * mono_valloc:
175  * @addr: memory address
176  * @length: memory area size
177  * @flags: protection flags
178  *
179  * Allocates @length bytes of virtual memory with the @flags
180  * protection. @addr can be a preferred memory address or a
181  * mandatory one if MONO_MMAP_FIXED is set in @flags.
182  * @addr must be pagesize aligned and can be NULL.
183  * @length must be a multiple of pagesize.
184  *
185  * Returns: NULL on failure, the address of the memory area otherwise
186  */
187 void*
188 mono_valloc (void *addr, size_t length, int flags)
189 {
190         void *ptr;
191         int mflags = 0;
192         int prot = prot_from_flags (flags);
193         /* translate the flags */
194         if (flags & MONO_MMAP_FIXED)
195                 mflags |= MAP_FIXED;
196         if (flags & MONO_MMAP_32BIT)
197                 mflags |= MAP_32BIT;
198
199         mflags |= MAP_ANONYMOUS;
200         mflags |= MAP_PRIVATE;
201
202         ptr = mmap (addr, length, prot, mflags, -1, 0);
203         if (ptr == (void*)-1) {
204                 int fd = open ("/dev/zero", O_RDONLY);
205                 if (fd != -1) {
206                         ptr = mmap (addr, length, prot, mflags, fd, 0);
207                         close (fd);
208                 }
209                 if (ptr == (void*)-1)
210                         return NULL;
211         }
212         return ptr;
213 }
214
215 /**
216  * mono_vfree:
217  * @addr: memory address returned by mono_valloc ()
218  * @length: size of memory area
219  *
220  * Remove the memory mapping at the address @addr.
221  *
222  * Returns: 0 on success.
223  */
224 int
225 mono_vfree (void *addr, size_t length)
226 {
227         return munmap (addr, length);
228 }
229
230 /**
231  * mono_file_map:
232  * @length: size of data to map
233  * @flags: protection flags
234  * @fd: file descriptor
235  * @offset: offset in the file
236  * @ret_handle: pointer to storage for returning a handle for the map
237  *
238  * Map the area of the file pointed to by the file descriptor @fd, at offset
239  * @offset and of size @length in memory according to the protection flags
240  * @flags.
241  * @offset and @length must be multiples of the page size.
242  * @ret_handle must point to a void*: this value must be used when unmapping
243  * the memory area using mono_file_unmap ().
244  *
245  */
246 void*
247 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
248 {
249         void *ptr;
250         int mflags = 0;
251         int prot = prot_from_flags (flags);
252         /* translate the flags */
253         if (flags & MONO_MMAP_PRIVATE)
254                 mflags |= MAP_PRIVATE;
255         if (flags & MONO_MMAP_SHARED)
256                 mflags |= MAP_SHARED;
257         if (flags & MONO_MMAP_FIXED)
258                 mflags |= MAP_FIXED;
259         if (flags & MONO_MMAP_32BIT)
260                 mflags |= MAP_32BIT;
261
262         ptr = mmap (0, length, prot, mflags, fd, offset);
263         if (ptr == (void*)-1)
264                 return NULL;
265         *ret_handle = (void*)length;
266         return ptr;
267 }
268
269 /**
270  * mono_file_unmap:
271  * @addr: memory address returned by mono_file_map ()
272  * @handle: handle of memory map
273  *
274  * Remove the memory mapping at the address @addr.
275  * @handle must be the value returned in ret_handle by mono_file_map ().
276  *
277  * Returns: 0 on success.
278  */
279 int
280 mono_file_unmap (void *addr, void *handle)
281 {
282         return munmap (addr, (size_t)handle);
283 }
284
285 /**
286  * mono_mprotect:
287  * @addr: memory address
288  * @length: size of memory area
289  * @flags: new protection flags
290  *
291  * Change the protection for the memory area at @addr for @length bytes
292  * to matche the supplied @flags.
293  * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
294  * and the area is cleared to zero.
295  * @addr must be aligned to the page size.
296  * @length must be a multiple of the page size.
297  *
298  * Returns: 0 on success.
299  */
300 int
301 mono_mprotect (void *addr, size_t length, int flags)
302 {
303         int prot = prot_from_flags (flags);
304
305         if (flags & MONO_MMAP_DISCARD) {
306                 /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
307 #ifdef __linux__
308                 if (madvise (addr, length, MADV_DONTNEED))
309                         memset (addr, 0, length);
310 #else
311                 memset (addr, 0, length);
312                 madvise (addr, length, MADV_DONTNEED);
313                 madvise (addr, length, MADV_FREE);
314 #endif
315         }
316         return mprotect (addr, length, prot);
317 }
318
319 #else
320
321 /* dummy malloc-based implementation */
322 int
323 mono_pagesize (void)
324 {
325         return 4096;
326 }
327
328 void*
329 mono_valloc (void *addr, size_t length, int flags)
330 {
331         return malloc (length);
332 }
333
334 int
335 mono_vfree (void *addr, size_t length)
336 {
337         free (addr);
338         return 0;
339 }
340
341 void*
342 mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
343 {
344         guint64 cur_offset;
345         size_t bytes_read;
346         void *ptr = malloc (length);
347         if (!ptr)
348                 return NULL;
349         cur_offset = lseek (fd, 0, SEEK_CUR);
350         if (lseek (fd, offset, SEEK_SET) != offset) {
351                 free (ptr);
352                 return NULL;
353         }
354         bytes_read = read (fd, ptr, length);
355         lseek (fd, cur_offset, SEEK_SET);
356         *ret_handle = NULL;
357         return ptr;
358 }
359
360 int
361 mono_file_unmap (void *addr, void *handle)
362 {
363         free (addr);
364         return 0;
365 }
366
367 int
368 mono_mprotect (void *addr, size_t length, int flags)
369 {
370         if (flags & MONO_MMAP_DISCARD) {
371                 memset (addr, 0, length);
372         }
373         return 0;
374 }
375
376 #endif
377