Add a more functional (i.e. fewer-stubs) implementation of System.Data.Linq.
[mono.git] / mono / mini / unwind.c
1 /*
2  * unwind.c: Stack Unwinding Interface
3  *
4  * Authors:
5  *   Zoltan Varga (vargaz@gmail.com)
6  *
7  * (C) 2008 Novell, Inc.
8  */
9
10 #include "mini.h"
11 #include "unwind.h"
12
13 #include <mono/utils/mono-counters.h>
14 #include <mono/metadata/threads-types.h>
15
16 typedef enum {
17         LOC_SAME,
18         LOC_OFFSET
19 } LocType;
20
21 typedef struct {
22         LocType loc_type;
23         int offset;
24 } Loc;
25
26 typedef struct {
27         guint32 len;
28         guint8 info [MONO_ZERO_LEN_ARRAY];
29 } MonoUnwindInfo;
30
31 static CRITICAL_SECTION unwind_mutex;
32
33 static MonoUnwindInfo **cached_info;
34 static int cached_info_next, cached_info_size;
35 /* Statistics */
36 static int unwind_info_size;
37
38 #define unwind_lock() EnterCriticalSection (&unwind_mutex)
39 #define unwind_unlock() LeaveCriticalSection (&unwind_mutex)
40
41 #ifdef __x86_64__
42 static int map_hw_reg_to_dwarf_reg [] = { 0, 2, 1, 3, 7, 6, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
43 #define NUM_REGS AMD64_NREG
44 #define DWARF_DATA_ALIGN (-8)
45 #define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (AMD64_RIP))
46 #elif defined(__arm__)
47 // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf
48 static int map_hw_reg_to_dwarf_reg [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
49 #define NUM_REGS 16
50 #define DWARF_DATA_ALIGN (-4)
51 #define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (ARMREG_LR))
52 #elif defined (__i386__)
53 static int map_hw_reg_to_dwarf_reg [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
54 /* + 1 is for IP */
55 #define NUM_REGS X86_NREG + 1
56 #define DWARF_DATA_ALIGN (-4)
57 #define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (X86_NREG))
58 #else
59 static int map_hw_reg_to_dwarf_reg [16];
60 #define NUM_REGS 16
61 #define DWARF_DATA_ALIGN 0
62 #define DWARF_PC_REG -1
63 #endif
64
65 static gboolean dwarf_reg_to_hw_reg_inited;
66
67 static int map_dwarf_reg_to_hw_reg [NUM_REGS];
68
69 /*
70  * mono_hw_reg_to_dwarf_reg:
71  *
72  *   Map the hardware register number REG to the register number used by DWARF.
73  */
74 int
75 mono_hw_reg_to_dwarf_reg (int reg)
76 {
77         if (NUM_REGS == 0) {
78                 g_assert_not_reached ();
79                 return -1;
80         } else {
81                 return map_hw_reg_to_dwarf_reg [reg];
82         }
83 }
84
85 static void
86 init_reg_map (void)
87 {
88         int i;
89
90         g_assert (NUM_REGS > 0);
91         g_assert (sizeof (map_hw_reg_to_dwarf_reg) / sizeof (int) == NUM_REGS);
92         for (i = 0; i < NUM_REGS; ++i) {
93                 map_dwarf_reg_to_hw_reg [mono_hw_reg_to_dwarf_reg (i)] = i;
94         }
95
96         mono_memory_barrier ();
97         dwarf_reg_to_hw_reg_inited = TRUE;
98 }
99
100 static inline int
101 mono_dwarf_reg_to_hw_reg (int reg)
102 {
103         if (!dwarf_reg_to_hw_reg_inited)
104                 init_reg_map ();
105
106         return map_dwarf_reg_to_hw_reg [reg];
107 }
108
109 static G_GNUC_UNUSED void
110 encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf)
111 {
112         guint8 *p = buf;
113
114         do {
115                 guint8 b = value & 0x7f;
116                 value >>= 7;
117                 if (value != 0) /* more bytes to come */
118                         b |= 0x80;
119                 *p ++ = b;
120         } while (value);
121
122         *endbuf = p;
123 }
124
125 static inline guint32
126 decode_uleb128 (guint8 *buf, guint8 **endbuf)
127 {
128         guint8 *p = buf;
129         guint32 res = 0;
130         int shift = 0;
131
132         while (TRUE) {
133                 guint8 b = *p;
134                 p ++;
135
136                 res = res | (((int)(b & 0x7f)) << shift);
137                 if (!(b & 0x80))
138                         break;
139                 shift += 7;
140         }
141
142         *endbuf = p;
143
144         return res;
145 }
146
147 /*
148  * mono_unwind_ops_encode:
149  *
150  *   Encode the unwind ops in UNWIND_OPS into the compact DWARF encoding.
151  * Return a pointer to malloc'ed memory.
152  */
153 guint8*
154 mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len)
155 {
156         GSList *l;
157         MonoUnwindOp *op;
158         int loc;
159         guint8 *buf, *p, *res;
160
161         p = buf = g_malloc0 (256);
162
163         loc = 0;
164         l = unwind_ops;
165         for (; l; l = l->next) {
166                 int reg;
167
168                 op = l->data;
169
170                 /* Convert the register from the hw encoding to the dwarf encoding */
171                 reg = mono_hw_reg_to_dwarf_reg (op->reg);
172
173                 /* Emit an advance_loc if neccesary */
174                 while (op->when > loc) {
175                         if (op->when - loc < 32) {
176                                 *p ++ = DW_CFA_advance_loc | (op->when - loc);
177                                 loc = op->when;
178                         } else {
179                                 *p ++ = DW_CFA_advance_loc | (30);
180                                 loc += 30;
181                         }
182                 }                       
183
184                 switch (op->op) {
185                 case DW_CFA_def_cfa:
186                         *p ++ = op->op;
187                         encode_uleb128 (reg, p, &p);
188                         encode_uleb128 (op->val, p, &p);
189                         break;
190                 case DW_CFA_def_cfa_offset:
191                         *p ++ = op->op;
192                         encode_uleb128 (op->val, p, &p);
193                         break;
194                 case DW_CFA_def_cfa_register:
195                         *p ++ = op->op;
196                         encode_uleb128 (reg, p, &p);
197                         break;
198                 case DW_CFA_offset:
199                         *p ++ = DW_CFA_offset | reg;
200                         encode_uleb128 (op->val / DWARF_DATA_ALIGN, p, &p);
201                         break;
202                 default:
203                         g_assert_not_reached ();
204                         break;
205                 }
206         }
207         
208         g_assert (p - buf < 256);
209         *out_len = p - buf;
210         res = g_malloc (p - buf);
211         memcpy (res, buf, p - buf);
212         g_free (buf);
213         return res;
214 }
215
216 #if 0
217 #define UNW_DEBUG(stmt) do { stmt; } while (0)
218 #else
219 #define UNW_DEBUG(stmt) do { } while (0)
220 #endif
221
222 static G_GNUC_UNUSED void
223 print_dwarf_state (int cfa_reg, int cfa_offset, int ip, int nregs, Loc *locations)
224 {
225         int i;
226
227         printf ("\t%x: cfa=r%d+%d ", ip, cfa_reg, cfa_offset);
228
229         for (i = 0; i < nregs; ++i)
230                 if (locations [i].loc_type == LOC_OFFSET)
231                         printf ("r%d@%d(cfa) ", i, locations [i].offset);
232         printf ("\n");
233 }
234
235 /*
236  * Given the state of the current frame as stored in REGS, execute the unwind 
237  * operations in unwind_info until the location counter reaches POS. The result is 
238  * stored back into REGS. OUT_CFA will receive the value of the CFA.
239  * This function is signal safe.
240  */
241 void
242 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
243                                    guint8 *start_ip, guint8 *end_ip, guint8 *ip, gssize *regs, 
244                                    int nregs, guint8 **out_cfa) 
245 {
246         Loc locations [NUM_REGS];
247         int i, pos, reg, cfa_reg, cfa_offset;
248         guint8 *p;
249         guint8 *cfa_val;
250
251         g_assert (nregs <= NUM_REGS);
252
253         for (i = 0; i < nregs; ++i)
254                 locations [i].loc_type = LOC_SAME;
255
256         p = unwind_info;
257         pos = 0;
258         while (pos <= ip - start_ip && p < unwind_info + unwind_info_len) {
259                 int op = *p & 0xc0;
260
261                 switch (op) {
262                 case DW_CFA_advance_loc:
263                         UNW_DEBUG (print_dwarf_state (cfa_reg, cfa_offset, pos, nregs, locations));
264                         pos += *p & 0x3f;
265                         p ++;
266                         break;
267                 case DW_CFA_offset:
268                         reg = mono_dwarf_reg_to_hw_reg (*p & 0x3f);
269                         p ++;
270                         locations [reg].loc_type = LOC_OFFSET;
271                         locations [reg].offset = decode_uleb128 (p, &p) * DWARF_DATA_ALIGN;
272                         break;
273                 case 0: {
274                         int ext_op = *p;
275                         p ++;
276                         switch (ext_op) {
277                         case DW_CFA_def_cfa:
278                                 cfa_reg = mono_dwarf_reg_to_hw_reg (decode_uleb128 (p, &p));
279                                 cfa_offset = decode_uleb128 (p, &p);
280                                 break;
281                         case DW_CFA_def_cfa_offset:
282                                 cfa_offset = decode_uleb128 (p, &p);
283                                 break;
284                         case DW_CFA_def_cfa_register:
285                                 cfa_reg = mono_dwarf_reg_to_hw_reg (decode_uleb128 (p, &p));
286                                 break;
287                         default:
288                                 g_assert_not_reached ();
289                         }
290                         break;
291                 }
292                 default:
293                         g_assert_not_reached ();
294                 }
295         }
296
297         cfa_val = (guint8*)regs [cfa_reg] + cfa_offset;
298         for (i = 0; i < nregs; ++i) {
299                 if (locations [i].loc_type == LOC_OFFSET)
300                         regs [i] = *(gssize*)(cfa_val + locations [i].offset);
301         }
302
303         *out_cfa = cfa_val;
304 }
305
306 void
307 mono_unwind_init (void)
308 {
309         InitializeCriticalSection (&unwind_mutex);
310
311         mono_counters_register ("Unwind info size", MONO_COUNTER_JIT | MONO_COUNTER_INT, &unwind_info_size);
312 }
313
314 void
315 mono_unwind_cleanup (void)
316 {
317         int i;
318
319         DeleteCriticalSection (&unwind_mutex);
320
321         if (!cached_info)
322                 return;
323
324         for (i = 0; i < cached_info_next; ++i) {
325                 MonoUnwindInfo *cached = cached_info [i];
326
327                 g_free (cached);
328         }
329
330         g_free (cached_info);
331 }
332
333 /*
334  * mono_cache_unwind_info
335  *
336  *   Save UNWIND_INFO in the unwind info cache and return an id which can be passed
337  * to mono_get_cached_unwind_info to get a cached copy of the info.
338  * A copy is made of the unwind info.
339  * This function is useful for two reasons:
340  * - many methods have the same unwind info
341  * - MonoJitInfo->used_regs is an int so it can't store the pointer to the unwind info
342  */
343 guint32
344 mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len)
345 {
346         int i;
347         MonoUnwindInfo *info;
348
349         unwind_lock ();
350
351         if (cached_info == NULL) {
352                 cached_info_size = 16;
353                 cached_info = g_new0 (MonoUnwindInfo*, cached_info_size);
354         }
355
356         for (i = 0; i < cached_info_next; ++i) {
357                 MonoUnwindInfo *cached = cached_info [i];
358
359                 if (cached->len == unwind_info_len && memcmp (cached->info, unwind_info, unwind_info_len) == 0) {
360                         unwind_unlock ();
361                         return i;
362                 }
363         }
364
365         info = g_malloc (sizeof (MonoUnwindInfo) + unwind_info_len);
366         info->len = unwind_info_len;
367         memcpy (&info->info, unwind_info, unwind_info_len);
368
369         i = cached_info_next;
370         
371         if (cached_info_next >= cached_info_size) {
372                 MonoUnwindInfo **old_table, **new_table;
373
374                 /*
375                  * Have to resize the table, while synchronizing with 
376                  * mono_get_cached_unwind_info () using hazard pointers.
377                  */
378
379                 old_table = cached_info;
380                 new_table = g_new0 (MonoUnwindInfo*, cached_info_size * 2);
381
382                 memcpy (new_table, cached_info, cached_info_size * sizeof (MonoUnwindInfo*));
383
384                 mono_memory_barrier ();
385
386                 cached_info = new_table;
387
388                 mono_memory_barrier ();
389
390                 mono_thread_hazardous_free_or_queue (old_table, g_free);
391
392                 cached_info_size *= 2;
393         }
394
395         cached_info [cached_info_next ++] = info;
396
397         unwind_info_size += sizeof (MonoUnwindInfo) + unwind_info_len;
398
399         unwind_unlock ();
400         return i;
401 }
402
403 static gpointer
404 get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
405 {
406         gpointer p;
407
408         for (;;) {
409                 /* Get the pointer */
410                 p = *pp;
411                 /* If we don't have hazard pointers just return the
412                    pointer. */
413                 if (!hp)
414                         return p;
415                 /* Make it hazardous */
416                 mono_hazard_pointer_set (hp, hazard_index, p);
417                 /* Check that it's still the same.  If not, try
418                    again. */
419                 if (*pp != p) {
420                         mono_hazard_pointer_clear (hp, hazard_index);
421                         continue;
422                 }
423                 break;
424         }
425
426         return p;
427 }
428
429 /*
430  * This function is signal safe.
431  */
432 guint8*
433 mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len)
434 {
435         MonoUnwindInfo **table;
436         MonoUnwindInfo *info;
437         guint8 *data;
438         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
439
440         table = get_hazardous_pointer ((gpointer volatile*)&cached_info, hp, 0);
441
442         info = table [index];
443
444         *unwind_info_len = info->len;
445         data = info->info;
446
447         mono_hazard_pointer_clear (hp, 0);
448
449         return data;
450 }
451
452 /*
453  * mono_unwind_get_dwarf_data_align:
454  *
455  *   Return the data alignment used by the encoded unwind information.
456  */
457 int
458 mono_unwind_get_dwarf_data_align (void)
459 {
460         return DWARF_DATA_ALIGN;
461 }
462
463 /*
464  * mono_unwind_get_dwarf_pc_reg:
465  *
466  *   Return the dwarf register number of the register holding the ip of the
467  * previous frame.
468  */
469 int
470 mono_unwind_get_dwarf_pc_reg (void)
471 {
472         return DWARF_PC_REG;
473 }