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