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