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