* assembly.c (build_assembly_name): add arg for passing the assembly
[mono.git] / mono / metadata / rawbuffer.c
1 /*
2  * rawbuffer.c: Manages buffers that might have been mmapped or malloced
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9 #include <config.h>
10 #if defined(PLATFORM_WIN32)
11 #define USE_WIN32_API           1
12 #endif
13
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <errno.h>
18 #ifdef USE_WIN32_API
19 #include <winsock2.h>
20 #include <windows.h>
21 #include <io.h>
22 #else
23 #include <sys/mman.h>
24 #endif
25 #include <sys/types.h>
26 #include <glib.h>
27 #include "rawbuffer.h"
28
29 #include <mono/io-layer/io-layer.h>
30
31 #define ROUND_DOWN(VALUE,SIZE)  ((VALUE) & ~((SIZE) - 1))
32 #define ROUND_UP(VALUE,SIZE)    (ROUND_DOWN((VALUE) + (SIZE) - 1, (SIZE)))
33 #if SIZEOF_VOID_P == 8
34 #define UINTPTR_TYPE guint64
35 #else
36 #define UINTPTR_TYPE guint32
37 #endif
38
39 static GHashTable *mmap_map = NULL;
40 static size_t alignment = 0;
41 #define mono_mmap_lock() EnterCriticalSection (&mmap_mutex)
42 #define mono_mmap_unlock() LeaveCriticalSection (&mmap_mutex)
43 static CRITICAL_SECTION mmap_mutex;
44 static gboolean make_unreadable = FALSE;
45 static guint32 n_pagefaults = 0;
46
47 static void
48 get_alignment (void)
49 {
50 #ifdef USE_WIN32_API
51         SYSTEM_INFO info;
52
53         GetSystemInfo (&info);
54         alignment = info.dwAllocationGranularity;
55 #else
56         alignment = getpagesize ();
57 #endif
58 }
59
60 static void *
61 mono_raw_buffer_load_malloc (int fd, int is_writable, guint32 base, size_t size)
62 {
63         void *ptr;
64
65         ptr = g_malloc (size);
66         if (ptr == NULL)
67                 return NULL;
68
69         if (lseek (fd, base, 0) == (off_t) -1) {
70                 g_free (ptr);
71                 return NULL;
72         }
73
74         read (fd, ptr, size);
75         return ptr;
76 }
77
78 static void
79 mono_raw_buffer_free_malloc (void *base)
80 {
81         g_free (base);
82 }
83
84 void
85 mono_raw_buffer_init (void)
86 {
87         InitializeCriticalSection (&mmap_mutex);
88
89         get_alignment ();
90
91         mmap_map = g_hash_table_new (NULL, NULL);
92 }
93
94 void
95 mono_raw_buffer_cleanup (void)
96 {
97         g_hash_table_destroy (mmap_map);
98
99         DeleteCriticalSection (&mmap_mutex);
100 }
101
102 static void *
103 mono_raw_buffer_load_mmap (int fd, int is_writable, guint32 base, size_t size)
104 {
105 #ifdef USE_WIN32_API
106         /* FileMapping implementation */
107
108         DWORD start, end;
109         int prot, access;
110         void *ptr;
111         HANDLE file, mapping;
112
113         start = ROUND_DOWN (base, alignment);
114         end = base + size;
115         
116         if (is_writable) {
117                 prot = PAGE_WRITECOPY;
118                 access = FILE_MAP_COPY;
119         }
120         else {
121                 prot = PAGE_READONLY;
122                 access = FILE_MAP_READ;
123         }
124
125         file = (HANDLE) _get_osfhandle (fd);
126         mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
127         if (mapping == NULL)
128                 return 0;
129
130         ptr = MapViewOfFile (mapping, access, 0, start, end - start);
131         if (ptr == NULL) {
132                 CloseHandle (mapping);
133                 return 0;
134         }
135
136         mono_mmap_lock ();
137         g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (mapping));
138         mono_mmap_unlock ();
139         
140         return ((char *)ptr) + (base - start);
141
142 #else
143         /* mmap implementation */
144
145
146         size_t start, end;
147         int prot = PROT_READ;
148         int flags = 0;
149         void *ptr;
150
151         start = ROUND_DOWN (base, alignment);
152         end = ROUND_UP (base + size, alignment);
153
154         if (is_writable){
155                 prot |= PROT_WRITE;
156                 flags = MAP_SHARED;
157         } else {
158                 flags = MAP_PRIVATE;
159         }
160
161         ptr = mmap (0, end - start, prot, flags, fd, start);
162
163         if (ptr == (void *) -1)
164                 return 0;
165
166         /* 
167          * This seems to prevent segmentation faults on Fedora Linux, no
168          * idea why :). See
169          * http://bugzilla.ximian.com/show_bug.cgi?id=49499
170          * for more info.
171          */
172         mprotect (ptr, end - start, prot | PROT_EXEC);
173
174         if (make_unreadable) {
175                 int res = mprotect (ptr, end - start, 0);
176                 g_assert (res == 0);
177         }
178
179         mono_mmap_lock ();
180         g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (size));
181         mono_mmap_unlock ();
182
183         return ((char *)ptr) + (base - start);
184 #endif
185 }
186
187 static void
188 mono_raw_buffer_free_mmap (void *base)
189 {
190         int value;
191
192         mono_mmap_lock ();
193         value = GPOINTER_TO_INT (g_hash_table_lookup (mmap_map, base));
194         mono_mmap_unlock ();
195
196 #ifdef USE_WIN32_API
197         UnmapViewOfFile (base);
198         CloseHandle ((HANDLE) value);
199 #else
200         munmap (base, value);
201 #endif
202 }
203
204 static void
205 mono_raw_buffer_update_mmap (void *base, size_t size)
206 {
207 #ifdef USE_WIN32_API
208         FlushViewOfFile (base, size);
209 #else
210         msync (base, size, MS_SYNC);
211 #endif
212 }
213
214 void *
215 mono_raw_buffer_load (int fd, int is_writable, guint32 base, size_t size)
216 {
217         void *ptr;
218
219         ptr = mono_raw_buffer_load_mmap (fd, is_writable, base, size);
220         if (ptr == 0)
221                 ptr = mono_raw_buffer_load_malloc (fd, is_writable, base, size);
222         
223         return ptr;
224 }
225
226 void
227 mono_raw_buffer_update (void *buffer, size_t size)
228 {
229         char *mmap_base;
230         gboolean exists;
231
232         mmap_base =  (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment));
233
234         mono_mmap_lock ();
235         exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL;
236         mono_mmap_unlock ();
237         if (exists)
238                 mono_raw_buffer_update_mmap (mmap_base, size);
239 }
240
241 void
242 mono_raw_buffer_free (void *buffer)
243 {
244         char *mmap_base;
245         gboolean exists;
246
247         mmap_base = (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment));
248         
249         exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL;
250         if (exists)
251                 mono_raw_buffer_free_mmap (mmap_base);
252         else
253                 mono_raw_buffer_free_malloc (buffer);
254 }
255
256 /*
257  * mono_raw_buffer_set_make_unreadable:
258  *
259  *   Set whenever to make all mmaped memory unreadable. In conjuction with a
260  * SIGSEGV handler, this is useful to find out which pages the runtime tries to read.
261  */
262 void
263 mono_raw_buffer_set_make_unreadable (gboolean unreadable)
264 {
265         make_unreadable = unreadable;
266 }
267
268 typedef struct {
269         gboolean found;
270         void *ptr;
271 } FindMapUserData;
272
273 static void
274 find_map (void *start, guint32 size, gpointer user_data)
275 {
276         FindMapUserData *data = (FindMapUserData*)user_data;
277
278         if (!data->found)
279                 if (((guint8*)data->ptr >= (guint8*)start) && ((guint8*)data->ptr < (guint8*)start + size))
280                         data->found = TRUE;
281 }
282
283 /*
284  * mono_raw_buffer_is_pagefault:
285  *
286  *   Should be called from a SIGSEGV signal handler to find out whenever @ptr is
287  * within memory allocated by this module.
288  */
289 gboolean
290 mono_raw_buffer_is_pagefault (void *ptr)
291 {
292         FindMapUserData data;
293
294         if (!make_unreadable)
295                 return FALSE;
296
297         data.found = FALSE;
298         data.ptr = ptr;
299
300         mono_mmap_lock ();
301         g_hash_table_foreach (mmap_map, (GHFunc)find_map, &data);
302         mono_mmap_unlock ();
303
304         return data.found;
305 }
306
307 /*
308  * mono_raw_buffer_handle_pagefault:
309  *
310  *   Handle a pagefault caused by an unreadable page by making it readable again.
311  */
312 void
313 mono_raw_buffer_handle_pagefault (void *ptr)
314 {
315 #ifndef PLATFORM_WIN32
316         guint8* start = (guint8*)ROUND_DOWN (((gssize)ptr), alignment);
317         int res;
318
319         mono_mmap_lock ();
320         res = mprotect (start, alignment, PROT_READ);
321         g_assert (res == 0);
322
323         n_pagefaults ++;
324         mono_mmap_unlock ();
325 #endif
326 }
327
328 /*
329  * mono_raw_buffer_get_n_pagefaults:
330  *
331  *   Return the number of times handle_pagefault is called.
332  * To count the number of pagefaults caused by a block of code use code like this:
333  * 
334  *  int prev_pagefaults = mono_raw_buffer_get_n_pagefaults ();
335  *  <CODE>
336  *  int new_pagefaults = mono_raw_buffer_get_n_pagefaults () - prev_pagefaults;
337  */
338 guint32
339 mono_raw_buffer_get_n_pagefaults (void)
340 {
341         return n_pagefaults;
342 }