merge -r 60439:60440
[mono.git] / mono / utils / mono-codeman.c
1 #include "config.h"
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <assert.h>
6 #include <glib.h>
7
8 #ifdef PLATFORM_WIN32
9 #include <windows.h>
10 #include <io.h>
11 #else
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/mman.h>
15 #include <fcntl.h>
16 #endif
17
18 #include "mono-codeman.h"
19
20 #ifdef PLATFORM_WIN32
21 #define FORCE_MALLOC
22 #endif
23
24 #define MIN_PAGES 16
25
26 #ifdef __ia64__
27 #define MIN_ALIGN 16
28 #else
29 #define MIN_ALIGN 8
30 #endif
31
32 /* if a chunk has less than this amount of free space it's considered full */
33 #define MAX_WASTAGE 32
34 #define MIN_BSIZE 32
35
36 #ifndef MAP_ANONYMOUS
37 #ifdef MAP_ANON
38 #define MAP_ANONYMOUS MAP_ANON
39 #else
40 #define FORCE_MALLOC
41 #endif
42 #endif
43
44 #ifdef __x86_64__
45 #define ARCH_MAP_FLAGS MAP_32BIT
46 #else
47 #define ARCH_MAP_FLAGS 0
48 #endif
49
50 typedef struct _CodeChunck CodeChunk;
51
52 enum {
53         CODE_FLAG_MMAP,
54         CODE_FLAG_MALLOC
55 };
56
57 struct _CodeChunck {
58         char *data;
59         int pos;
60         int size;
61         CodeChunk *next;
62         unsigned int flags: 8;
63         /* this number of bytes is available to resolve addresses far in memory */
64         unsigned int bsize: 24;
65 };
66
67 struct _MonoCodeManager {
68         int dynamic;
69         CodeChunk *current;
70         CodeChunk *full;
71 };
72
73 MonoCodeManager* 
74 mono_code_manager_new (void)
75 {
76         MonoCodeManager *cman = malloc (sizeof (MonoCodeManager));
77         if (!cman)
78                 return NULL;
79         cman->current = NULL;
80         cman->full = NULL;
81         cman->dynamic = 0;
82         return cman;
83 }
84
85 MonoCodeManager* 
86 mono_code_manager_new_dynamic (void)
87 {
88         MonoCodeManager *cman = mono_code_manager_new ();
89         cman->dynamic = 1;
90         return cman;
91 }
92
93
94 static void
95 free_chunklist (CodeChunk *chunk)
96 {
97         CodeChunk *dead;
98         for (; chunk; ) {
99                 dead = chunk;
100                 chunk = chunk->next;
101                 if (dead->flags == CODE_FLAG_MMAP) {
102 #ifndef FORCE_MALLOC
103                         munmap (dead->data, dead->size);
104 #endif
105                 } else if (dead->flags == CODE_FLAG_MALLOC) {
106                         free (dead->data);
107                 }
108                 free (dead);
109         }
110 }
111
112 void
113 mono_code_manager_destroy (MonoCodeManager *cman)
114 {
115         free_chunklist (cman->full);
116         free_chunklist (cman->current);
117         free (cman);
118 }
119
120 /* fill all the memory with the 0x2a (42) value */
121 void             
122 mono_code_manager_invalidate (MonoCodeManager *cman)
123 {
124         CodeChunk *chunk;
125
126 #if defined(__i386__) || defined(__x86_64__)
127         int fill_value = 0xcc; /* x86 break */
128 #else
129         int fill_value = 0x2a;
130 #endif
131
132         for (chunk = cman->current; chunk; chunk = chunk->next)
133                 memset (chunk->data, fill_value, chunk->size);
134         for (chunk = cman->full; chunk; chunk = chunk->next)
135                 memset (chunk->data, fill_value, chunk->size);
136 }
137
138 void
139 mono_code_manager_foreach (MonoCodeManager *cman, MonoCodeManagerFunc func, void *user_data)
140 {
141         CodeChunk *chunk;
142         for (chunk = cman->current; chunk; chunk = chunk->next) {
143                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
144                         return;
145         }
146         for (chunk = cman->full; chunk; chunk = chunk->next) {
147                 if (func (chunk->data, chunk->size, chunk->bsize, user_data))
148                         return;
149         }
150 }
151
152 static int
153 query_pagesize (void)
154 {
155 #ifdef PLATFORM_WIN32
156         SYSTEM_INFO info;
157         GetSystemInfo (&info);
158         return info.dwAllocationGranularity;
159 #else
160         return getpagesize ();
161 #endif
162 }
163
164 /* BIND_ROOM is the divisor for the chunck of code size dedicated
165  * to binding branches (branches not reachable with the immediate displacement)
166  * bind_size = size/BIND_ROOM;
167  * we should reduce it and make MIN_PAGES bigger for such systems
168  */
169 #if defined(__ppc__) || defined(__powerpc__)
170 #define BIND_ROOM 4
171 #endif
172 #if defined(__arm__)
173 #define BIND_ROOM 8
174 #endif
175
176 static CodeChunk*
177 new_codechunk (int dynamic, int size)
178 {
179         static int pagesize = 0;
180         int minsize, flags = CODE_FLAG_MMAP;
181         int chunk_size, bsize = 0;
182         CodeChunk *chunk;
183         void *ptr;
184
185 #ifdef FORCE_MALLOC
186         flags = CODE_FLAG_MALLOC;
187 #endif
188
189         if (!pagesize)
190                 pagesize = query_pagesize ();
191
192         if (dynamic) {
193                 chunk_size = size;
194                 flags = CODE_FLAG_MALLOC;
195         }
196         else {
197                 minsize = pagesize * MIN_PAGES;
198                 if (size < minsize)
199                         chunk_size = minsize;
200                 else {
201                         chunk_size = size;
202                         chunk_size += pagesize - 1;
203                         chunk_size &= ~ (pagesize - 1);
204                 }
205         }
206 #ifdef BIND_ROOM
207         bsize = chunk_size / BIND_ROOM;
208         if (bsize < MIN_BSIZE)
209                 bsize = MIN_BSIZE;
210         bsize += MIN_ALIGN -1;
211         bsize &= ~ (MIN_ALIGN - 1);
212         if (chunk_size - size < bsize) {
213                 chunk_size = size + bsize;
214                 chunk_size += pagesize - 1;
215                 chunk_size &= ~ (pagesize - 1);
216         }
217 #endif
218
219         /* does it make sense to use the mmap-like API? */
220         if (flags == CODE_FLAG_MALLOC) {
221                 ptr = malloc (chunk_size);
222                 if (!ptr)
223                         return NULL;
224
225         }
226         else {
227 #ifndef FORCE_MALLOC
228                 ptr = mmap (0, chunk_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|ARCH_MAP_FLAGS, -1, 0);
229                 if (ptr == (void*)-1) {
230                         int fd = open ("/dev/zero", O_RDONLY);
231                         if (fd != -1) {
232                                 ptr = mmap (0, chunk_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|ARCH_MAP_FLAGS, fd, 0);
233                                 close (fd);
234                         }
235                         if (ptr == (void*)-1) {
236                                 ptr = malloc (chunk_size);
237                                 if (!ptr)
238                                         return NULL;
239                                 flags = CODE_FLAG_MALLOC;
240                         }
241                 }
242 #else
243                 return NULL;
244 #endif
245         }
246
247         if (flags == CODE_FLAG_MALLOC) {
248                 /*
249                  * AMD64 processors maintain icache coherency only for pages which are 
250                  * marked executable.
251                  */
252 #ifndef PLATFORM_WIN32
253                 {
254                         char *page_start = (char *) (((gssize) (ptr)) & ~ (pagesize - 1));
255                         int pages = ((char*)ptr + chunk_size - page_start + pagesize - 1) / pagesize;
256                         int err = mprotect (page_start, pages * pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);
257                         assert (!err);
258                 }
259 #else
260                 {
261                         DWORD oldp;
262                         int err = VirtualProtect (ptr, chunk_size, PAGE_EXECUTE_READWRITE, &oldp);
263                         assert (err);
264                 }
265 #endif
266
267 #ifdef BIND_ROOM
268                         /* Make sure the thunks area is zeroed */
269                         memset (ptr, 0, bsize);
270 #endif
271         }
272
273         chunk = malloc (sizeof (CodeChunk));
274         if (!chunk) {
275                 if (flags == CODE_FLAG_MALLOC)
276                         free (ptr);
277 #ifndef FORCE_MALLOC
278                 else
279                         munmap (ptr, chunk_size);
280 #endif
281                 return NULL;
282         }
283         chunk->next = NULL;
284         chunk->size = chunk_size;
285         chunk->data = ptr;
286         chunk->flags = flags;
287         chunk->pos = bsize;
288         chunk->bsize = bsize;
289
290         /*printf ("code chunk at: %p\n", ptr);*/
291         return chunk;
292 }
293
294 void*
295 mono_code_manager_reserve (MonoCodeManager *cman, int size)
296 {
297         CodeChunk *chunk, *prev;
298         void *ptr;
299         
300         size += MIN_ALIGN;
301         size &= ~ (MIN_ALIGN - 1);
302
303         if (!cman->current) {
304                 cman->current = new_codechunk (cman->dynamic, size);
305                 if (!cman->current)
306                         return NULL;
307         }
308
309         for (chunk = cman->current; chunk; chunk = chunk->next) {
310                 if (chunk->pos + size <= chunk->size) {
311                         ptr = chunk->data + chunk->pos;
312                         chunk->pos += size;
313                         return ptr;
314                 }
315         }
316         /* 
317          * no room found, move one filled chunk to cman->full 
318          * to keep cman->current from growing too much
319          */
320         prev = NULL;
321         for (chunk = cman->current; chunk; prev = chunk, chunk = chunk->next) {
322                 if (chunk->pos + MIN_ALIGN * 4 <= chunk->size)
323                         continue;
324                 if (prev) {
325                         prev->next = chunk->next;
326                 } else {
327                         cman->current = chunk->next;
328                 }
329                 chunk->next = cman->full;
330                 cman->full = chunk;
331                 break;
332         }
333         chunk = new_codechunk (cman->dynamic, size);
334         if (!chunk)
335                 return NULL;
336         chunk->next = cman->current;
337         cman->current = chunk;
338         ptr = chunk->data + chunk->pos;
339         chunk->pos += size;
340         return ptr;
341 }
342
343 /* 
344  * if we reserved too much room for a method and we didn't allocate
345  * already from the code manager, we can get back the excess allocation.
346  */
347 void
348 mono_code_manager_commit (MonoCodeManager *cman, void *data, int size, int newsize)
349 {
350         newsize += MIN_ALIGN;
351         newsize &= ~ (MIN_ALIGN - 1);
352         size += MIN_ALIGN;
353         size &= ~ (MIN_ALIGN - 1);
354
355         if (cman->current && (size != newsize) && (data == cman->current->data + cman->current->pos - size)) {
356                 cman->current->pos -= size - newsize;
357         }
358 }
359