Copied remotely
[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 #include <unistd.h>
15 #include <errno.h>
16 #ifdef USE_WIN32_API
17 #include <windows.h>
18 #include <io.h>
19 #else
20 #include <sys/mman.h>
21 #endif
22 #include <sys/types.h>
23 #include <glib.h>
24 #include "rawbuffer.h"
25
26 #include <mono/io-layer/io-layer.h>
27
28 #define ROUND_DOWN(VALUE,SIZE)  ((VALUE) & ~((SIZE) - 1))
29 #define ROUND_UP(VALUE,SIZE)    (ROUND_DOWN((VALUE) + (SIZE) - 1, (SIZE)))
30 #if SIZEOF_VOID_P == 8
31 #define UINTPTR_TYPE guint64
32 #else
33 #define UINTPTR_TYPE guint32
34 #endif
35
36 static GHashTable *mmap_map = NULL;
37 static size_t alignment = 0;
38 static CRITICAL_SECTION mmap_mutex;
39
40 static void
41 get_alignment (void)
42 {
43 #ifdef USE_WIN32_API
44         SYSTEM_INFO info;
45
46         GetSystemInfo (&info);
47         alignment = info.dwAllocationGranularity;
48 #else
49         alignment = getpagesize ();
50 #endif
51 }
52
53 static void *
54 mono_raw_buffer_load_malloc (int fd, int is_writable, guint32 base, size_t size)
55 {
56         void *ptr;
57
58         ptr = g_malloc (size);
59         if (ptr == NULL)
60                 return NULL;
61
62         if (lseek (fd, base, 0) == (off_t) -1) {
63                 g_free (ptr);
64                 return NULL;
65         }
66
67         read (fd, ptr, size);
68         return ptr;
69 }
70
71 static void
72 mono_raw_buffer_free_malloc (void *base)
73 {
74         g_free (base);
75 }
76
77 void
78 mono_raw_buffer_init (void)
79 {
80         InitializeCriticalSection (&mmap_mutex);
81
82         get_alignment ();
83
84         mmap_map = g_hash_table_new (NULL, NULL);
85 }
86
87 static void *
88 mono_raw_buffer_load_mmap (int fd, int is_writable, guint32 base, size_t size)
89 {
90 #ifdef USE_WIN32_API
91         /* FileMapping implementation */
92
93         DWORD start, end;
94         int prot, access;
95         void *ptr;
96         HANDLE file, mapping;
97
98         start = ROUND_DOWN (base, alignment);
99         end = base + size;
100         
101         if (is_writable) {
102                 prot = PAGE_WRITECOPY;
103                 access = FILE_MAP_COPY;
104         }
105         else {
106                 prot = PAGE_READONLY;
107                 access = FILE_MAP_READ;
108         }
109
110         file = (HANDLE) _get_osfhandle (fd);
111         mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
112         if (mapping == NULL)
113                 return 0;
114
115         ptr = MapViewOfFile (mapping, access, 0, start, end - start);
116         if (ptr == NULL) {
117                 CloseHandle (mapping);
118                 return 0;
119         }
120
121         EnterCriticalSection (&mmap_mutex);
122         g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (mapping));
123         LeaveCriticalSection (&mmap_mutex);
124         
125         return ((char *)ptr) + (base - start);
126
127 #else
128         /* mmap implementation */
129
130
131         size_t start, end;
132         int prot = PROT_READ;
133         int flags = 0;
134         void *ptr;
135
136         start = ROUND_DOWN (base, alignment);
137         end = ROUND_UP (base + size, alignment);
138
139         if (is_writable){
140                 prot |= PROT_WRITE;
141                 flags = MAP_SHARED;
142         } else {
143                 flags = MAP_PRIVATE;
144         }
145
146         ptr = mmap (0, end - start, prot, flags, fd, start);
147
148         if (ptr == (void *) -1)
149                 return 0;
150
151         /* 
152          * This seems to prevent segmentation faults on Fedora Linux, no
153          * idea why :). See
154          * http://bugzilla.ximian.com/show_bug.cgi?id=49499
155          * for more info.
156          */
157         if (mprotect (ptr, end - start, prot | PROT_EXEC) != 0)
158                 g_warning (G_GNUC_PRETTY_FUNCTION
159                                    ": mprotect failed: %s", g_strerror (errno));
160         
161         EnterCriticalSection (&mmap_mutex);
162         g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (size));
163         LeaveCriticalSection (&mmap_mutex);
164
165         return ((char *)ptr) + (base - start);
166 #endif
167 }
168
169 static void
170 mono_raw_buffer_free_mmap (void *base)
171 {
172         int value;
173
174         EnterCriticalSection (&mmap_mutex);
175         value = GPOINTER_TO_INT (g_hash_table_lookup (mmap_map, base));
176         LeaveCriticalSection (&mmap_mutex);
177
178 #ifdef USE_WIN32_API
179         UnmapViewOfFile (base);
180         CloseHandle ((HANDLE) value);
181 #else
182         munmap (base, value);
183 #endif
184 }
185
186 static void
187 mono_raw_buffer_update_mmap (void *base, size_t size)
188 {
189 #ifdef USE_WIN32_API
190         FlushViewOfFile (base, size);
191 #else
192         msync (base, size, MS_SYNC);
193 #endif
194 }
195
196 void *
197 mono_raw_buffer_load (int fd, int is_writable, guint32 base, size_t size)
198 {
199         void *ptr;
200
201         ptr = mono_raw_buffer_load_mmap (fd, is_writable, base, size);
202         if (ptr == 0)
203                 ptr = mono_raw_buffer_load_malloc (fd, is_writable, base, size);
204         
205         return ptr;
206 }
207
208 void
209 mono_raw_buffer_update (void *buffer, size_t size)
210 {
211         char *mmap_base;
212         gboolean exists;
213
214         mmap_base =  (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment));
215
216         EnterCriticalSection (&mmap_mutex);
217         exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL;
218         LeaveCriticalSection (&mmap_mutex);
219         if (exists)
220                 mono_raw_buffer_update_mmap (mmap_base, size);
221 }
222
223 void
224 mono_raw_buffer_free (void *buffer)
225 {
226         char *mmap_base;
227         gboolean exists;
228
229         mmap_base = (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment));
230         
231         exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL;
232         if (exists)
233                 mono_raw_buffer_free_mmap (mmap_base);
234         else
235                 mono_raw_buffer_free_malloc (buffer);
236 }
237