Wed Feb 24 15:47:16 CET 2010 Paolo Molaro <lupus@ximian.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 "mini-unwind.h"
12
13 #include <mono/utils/mono-counters.h>
14 #include <mono/metadata/threads-types.h>
15 #include <mono/metadata/mono-endian.h>
16
17 typedef enum {
18         LOC_SAME,
19         LOC_OFFSET
20 } LocType;
21
22 typedef struct {
23         LocType loc_type;
24         int offset;
25 } Loc;
26
27 typedef struct {
28         guint32 len;
29         guint8 info [MONO_ZERO_LEN_ARRAY];
30 } MonoUnwindInfo;
31
32 static CRITICAL_SECTION unwind_mutex;
33
34 static MonoUnwindInfo **cached_info;
35 static int cached_info_next, cached_info_size;
36 /* Statistics */
37 static int unwind_info_size;
38
39 #define unwind_lock() EnterCriticalSection (&unwind_mutex)
40 #define unwind_unlock() LeaveCriticalSection (&unwind_mutex)
41
42 #ifdef TARGET_AMD64
43 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 };
44 #define NUM_REGS AMD64_NREG
45 #define DWARF_DATA_ALIGN (-8)
46 #define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (AMD64_RIP))
47 #elif defined(TARGET_ARM)
48 // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0040a/IHI0040A_aadwarf.pdf
49 static int map_hw_reg_to_dwarf_reg [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
50 #define NUM_REGS 16
51 #define DWARF_DATA_ALIGN (-4)
52 #define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (ARMREG_LR))
53 #elif defined (TARGET_X86)
54 static int map_hw_reg_to_dwarf_reg [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
55 /* + 1 is for IP */
56 #define NUM_REGS X86_NREG + 1
57 #define DWARF_DATA_ALIGN (-4)
58 #define DWARF_PC_REG (mono_hw_reg_to_dwarf_reg (X86_NREG))
59 #elif defined (TARGET_POWERPC)
60 // http://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi-1.9.html
61 static int map_hw_reg_to_dwarf_reg [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 
62                                                                                   9, 10, 11, 12, 13, 14, 15, 16,
63                                                                                   17, 18, 19, 20, 21, 22, 23, 24,
64                                                                                   25, 26, 27, 28, 29, 30, 31 };
65 #define NUM_REGS 110
66 #define DWARF_DATA_ALIGN (-(gint32)sizeof (mgreg_t))
67 #define DWARF_PC_REG 108
68 #else
69 static int map_hw_reg_to_dwarf_reg [16];
70 #define NUM_REGS 16
71 #define DWARF_DATA_ALIGN 0
72 #define DWARF_PC_REG -1
73 #endif
74
75 static gboolean dwarf_reg_to_hw_reg_inited;
76
77 static int map_dwarf_reg_to_hw_reg [NUM_REGS];
78
79 /*
80  * mono_hw_reg_to_dwarf_reg:
81  *
82  *   Map the hardware register number REG to the register number used by DWARF.
83  */
84 int
85 mono_hw_reg_to_dwarf_reg (int reg)
86 {
87 #ifdef TARGET_POWERPC
88         if (reg == ppc_lr)
89                 return 108;
90         else
91                 g_assert (reg < NUM_REGS);
92 #endif
93
94         if (NUM_REGS == 0) {
95                 g_assert_not_reached ();
96                 return -1;
97         } else {
98                 return map_hw_reg_to_dwarf_reg [reg];
99         }
100 }
101
102 static void
103 init_reg_map (void)
104 {
105         int i;
106
107         g_assert (NUM_REGS > 0);
108         for (i = 0; i < sizeof (map_hw_reg_to_dwarf_reg) / sizeof (int); ++i) {
109                 map_dwarf_reg_to_hw_reg [mono_hw_reg_to_dwarf_reg (i)] = i;
110         }
111
112 #ifdef TARGET_POWERPC
113         map_dwarf_reg_to_hw_reg [DWARF_PC_REG] = ppc_lr;
114 #endif
115
116         mono_memory_barrier ();
117         dwarf_reg_to_hw_reg_inited = TRUE;
118 }
119
120 static inline int
121 mono_dwarf_reg_to_hw_reg (int reg)
122 {
123         if (!dwarf_reg_to_hw_reg_inited)
124                 init_reg_map ();
125
126         return map_dwarf_reg_to_hw_reg [reg];
127 }
128
129 static G_GNUC_UNUSED void
130 encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf)
131 {
132         guint8 *p = buf;
133
134         do {
135                 guint8 b = value & 0x7f;
136                 value >>= 7;
137                 if (value != 0) /* more bytes to come */
138                         b |= 0x80;
139                 *p ++ = b;
140         } while (value);
141
142         *endbuf = p;
143 }
144
145 static G_GNUC_UNUSED void
146 encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf)
147 {
148         gboolean more = 1;
149         gboolean negative = (value < 0);
150         guint32 size = 32;
151         guint8 byte;
152         guint8 *p = buf;
153
154         while (more) {
155                 byte = value & 0x7f;
156                 value >>= 7;
157                 /* the following is unnecessary if the
158                  * implementation of >>= uses an arithmetic rather
159                  * than logical shift for a signed left operand
160                  */
161                 if (negative)
162                         /* sign extend */
163                         value |= - (1 <<(size - 7));
164                 /* sign bit of byte is second high order bit (0x40) */
165                 if ((value == 0 && !(byte & 0x40)) ||
166                         (value == -1 && (byte & 0x40)))
167                         more = 0;
168                 else
169                         byte |= 0x80;
170                 *p ++= byte;
171         }
172
173         *endbuf = p;
174 }
175
176 static inline guint32
177 decode_uleb128 (guint8 *buf, guint8 **endbuf)
178 {
179         guint8 *p = buf;
180         guint32 res = 0;
181         int shift = 0;
182
183         while (TRUE) {
184                 guint8 b = *p;
185                 p ++;
186
187                 res = res | (((int)(b & 0x7f)) << shift);
188                 if (!(b & 0x80))
189                         break;
190                 shift += 7;
191         }
192
193         *endbuf = p;
194
195         return res;
196 }
197
198 static inline gint32
199 decode_sleb128 (guint8 *buf, guint8 **endbuf)
200 {
201         guint8 *p = buf;
202         gint32 res = 0;
203         int shift = 0;
204
205         while (TRUE) {
206                 guint8 b = *p;
207                 p ++;
208
209                 res = res | (((int)(b & 0x7f)) << shift);
210                 shift += 7;
211                 if (!(b & 0x80)) {
212                         if (shift < 32 && (b & 0x40))
213                                 res |= - (1 << shift);
214                         break;
215                 }
216         }
217
218         *endbuf = p;
219
220         return res;
221 }
222
223 /*
224  * mono_unwind_ops_encode:
225  *
226  *   Encode the unwind ops in UNWIND_OPS into the compact DWARF encoding.
227  * Return a pointer to malloc'ed memory.
228  */
229 guint8*
230 mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len)
231 {
232         GSList *l;
233         MonoUnwindOp *op;
234         int loc;
235         guint8 *buf, *p, *res;
236
237         p = buf = g_malloc0 (4096);
238
239         loc = 0;
240         l = unwind_ops;
241         for (; l; l = l->next) {
242                 int reg;
243
244                 op = l->data;
245
246                 /* Convert the register from the hw encoding to the dwarf encoding */
247                 reg = mono_hw_reg_to_dwarf_reg (op->reg);
248
249                 /* Emit an advance_loc if neccesary */
250                 while (op->when > loc) {
251                         if (op->when - loc < 32) {
252                                 *p ++ = DW_CFA_advance_loc | (op->when - loc);
253                                 loc = op->when;
254                         } else {
255                                 *p ++ = DW_CFA_advance_loc | (30);
256                                 loc += 30;
257                         }
258                 }                       
259
260                 switch (op->op) {
261                 case DW_CFA_def_cfa:
262                         *p ++ = op->op;
263                         encode_uleb128 (reg, p, &p);
264                         encode_uleb128 (op->val, p, &p);
265                         break;
266                 case DW_CFA_def_cfa_offset:
267                         *p ++ = op->op;
268                         encode_uleb128 (op->val, p, &p);
269                         break;
270                 case DW_CFA_def_cfa_register:
271                         *p ++ = op->op;
272                         encode_uleb128 (reg, p, &p);
273                         break;
274                 case DW_CFA_offset:
275                         if (reg > 63) {
276                                 *p ++ = DW_CFA_offset_extended_sf;
277                                 encode_uleb128 (reg, p, &p);
278                                 encode_sleb128 (op->val / DWARF_DATA_ALIGN, p, &p);
279                         } else {
280                                 *p ++ = DW_CFA_offset | reg;
281                                 encode_uleb128 (op->val / DWARF_DATA_ALIGN, p, &p);
282                         }
283                         break;
284                 default:
285                         g_assert_not_reached ();
286                         break;
287                 }
288         }
289         
290         g_assert (p - buf < 4096);
291         *out_len = p - buf;
292         res = g_malloc (p - buf);
293         memcpy (res, buf, p - buf);
294         g_free (buf);
295         return res;
296 }
297
298 #if 0
299 #define UNW_DEBUG(stmt) do { stmt; } while (0)
300 #else
301 #define UNW_DEBUG(stmt) do { } while (0)
302 #endif
303
304 static G_GNUC_UNUSED void
305 print_dwarf_state (int cfa_reg, int cfa_offset, int ip, int nregs, Loc *locations)
306 {
307         int i;
308
309         printf ("\t%x: cfa=r%d+%d ", ip, cfa_reg, cfa_offset);
310
311         for (i = 0; i < nregs; ++i)
312                 if (locations [i].loc_type == LOC_OFFSET)
313                         printf ("r%d@%d(cfa) ", i, locations [i].offset);
314         printf ("\n");
315 }
316
317 /*
318  * Given the state of the current frame as stored in REGS, execute the unwind 
319  * operations in unwind_info until the location counter reaches POS. The result is 
320  * stored back into REGS. OUT_CFA will receive the value of the CFA.
321  * This function is signal safe.
322  */
323 void
324 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
325                                    guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, 
326                                    int nregs, guint8 **out_cfa)
327 {
328         Loc locations [NUM_REGS];
329         int i, pos, reg, cfa_reg, cfa_offset;
330         guint8 *p;
331         guint8 *cfa_val;
332
333         for (i = 0; i < NUM_REGS; ++i)
334                 locations [i].loc_type = LOC_SAME;
335
336         p = unwind_info;
337         pos = 0;
338         cfa_reg = -1;
339         cfa_offset = -1;
340         while (pos <= ip - start_ip && p < unwind_info + unwind_info_len) {
341                 int op = *p & 0xc0;
342
343                 switch (op) {
344                 case DW_CFA_advance_loc:
345                         UNW_DEBUG (print_dwarf_state (cfa_reg, cfa_offset, pos, nregs, locations));
346                         pos += *p & 0x3f;
347                         p ++;
348                         break;
349                 case DW_CFA_offset:
350                         reg = *p & 0x3f;
351                         p ++;
352                         locations [reg].loc_type = LOC_OFFSET;
353                         locations [reg].offset = decode_uleb128 (p, &p) * DWARF_DATA_ALIGN;
354                         break;
355                 case 0: {
356                         int ext_op = *p;
357                         p ++;
358                         switch (ext_op) {
359                         case DW_CFA_def_cfa:
360                                 cfa_reg = decode_uleb128 (p, &p);
361                                 cfa_offset = decode_uleb128 (p, &p);
362                                 break;
363                         case DW_CFA_def_cfa_offset:
364                                 cfa_offset = decode_uleb128 (p, &p);
365                                 break;
366                         case DW_CFA_def_cfa_register:
367                                 cfa_reg = decode_uleb128 (p, &p);
368                                 break;
369                         case DW_CFA_offset_extended_sf:
370                                 reg = decode_uleb128 (p, &p);
371                                 locations [reg].loc_type = LOC_OFFSET;
372                                 locations [reg].offset = decode_sleb128 (p, &p) * DWARF_DATA_ALIGN;
373                                 break;
374                         case DW_CFA_advance_loc4:
375                                 pos += read32 (p);
376                                 p += 4;
377                                 break;
378                         default:
379                                 g_assert_not_reached ();
380                         }
381                         break;
382                 }
383                 default:
384                         g_assert_not_reached ();
385                 }
386         }
387
388         cfa_val = (guint8*)regs [mono_dwarf_reg_to_hw_reg (cfa_reg)] + cfa_offset;
389         for (i = 0; i < NUM_REGS; ++i) {
390                 if (locations [i].loc_type == LOC_OFFSET) {
391                         int hreg = mono_dwarf_reg_to_hw_reg (i);
392                         g_assert (hreg < nregs);
393                         regs [hreg] = *(mgreg_t*)(cfa_val + locations [i].offset);
394                 }
395         }
396
397         *out_cfa = cfa_val;
398 }
399
400 void
401 mono_unwind_init (void)
402 {
403         InitializeCriticalSection (&unwind_mutex);
404
405         mono_counters_register ("Unwind info size", MONO_COUNTER_JIT | MONO_COUNTER_INT, &unwind_info_size);
406 }
407
408 void
409 mono_unwind_cleanup (void)
410 {
411         int i;
412
413         DeleteCriticalSection (&unwind_mutex);
414
415         if (!cached_info)
416                 return;
417
418         for (i = 0; i < cached_info_next; ++i) {
419                 MonoUnwindInfo *cached = cached_info [i];
420
421                 g_free (cached);
422         }
423
424         g_free (cached_info);
425 }
426
427 /*
428  * mono_cache_unwind_info
429  *
430  *   Save UNWIND_INFO in the unwind info cache and return an id which can be passed
431  * to mono_get_cached_unwind_info to get a cached copy of the info.
432  * A copy is made of the unwind info.
433  * This function is useful for two reasons:
434  * - many methods have the same unwind info
435  * - MonoJitInfo->used_regs is an int so it can't store the pointer to the unwind info
436  */
437 guint32
438 mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len)
439 {
440         int i;
441         MonoUnwindInfo *info;
442
443         unwind_lock ();
444
445         if (cached_info == NULL) {
446                 cached_info_size = 16;
447                 cached_info = g_new0 (MonoUnwindInfo*, cached_info_size);
448         }
449
450         for (i = 0; i < cached_info_next; ++i) {
451                 MonoUnwindInfo *cached = cached_info [i];
452
453                 if (cached->len == unwind_info_len && memcmp (cached->info, unwind_info, unwind_info_len) == 0) {
454                         unwind_unlock ();
455                         return i;
456                 }
457         }
458
459         info = g_malloc (sizeof (MonoUnwindInfo) + unwind_info_len);
460         info->len = unwind_info_len;
461         memcpy (&info->info, unwind_info, unwind_info_len);
462
463         i = cached_info_next;
464         
465         if (cached_info_next >= cached_info_size) {
466                 MonoUnwindInfo **old_table, **new_table;
467
468                 /*
469                  * Have to resize the table, while synchronizing with 
470                  * mono_get_cached_unwind_info () using hazard pointers.
471                  */
472
473                 old_table = cached_info;
474                 new_table = g_new0 (MonoUnwindInfo*, cached_info_size * 2);
475
476                 memcpy (new_table, cached_info, cached_info_size * sizeof (MonoUnwindInfo*));
477
478                 mono_memory_barrier ();
479
480                 cached_info = new_table;
481
482                 mono_memory_barrier ();
483
484                 mono_thread_hazardous_free_or_queue (old_table, g_free);
485
486                 cached_info_size *= 2;
487         }
488
489         cached_info [cached_info_next ++] = info;
490
491         unwind_info_size += sizeof (MonoUnwindInfo) + unwind_info_len;
492
493         unwind_unlock ();
494         return i;
495 }
496
497 static gpointer
498 get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
499 {
500         gpointer p;
501
502         for (;;) {
503                 /* Get the pointer */
504                 p = *pp;
505                 /* If we don't have hazard pointers just return the
506                    pointer. */
507                 if (!hp)
508                         return p;
509                 /* Make it hazardous */
510                 mono_hazard_pointer_set (hp, hazard_index, p);
511                 /* Check that it's still the same.  If not, try
512                    again. */
513                 if (*pp != p) {
514                         mono_hazard_pointer_clear (hp, hazard_index);
515                         continue;
516                 }
517                 break;
518         }
519
520         return p;
521 }
522
523 /*
524  * This function is signal safe.
525  */
526 guint8*
527 mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len)
528 {
529         MonoUnwindInfo **table;
530         MonoUnwindInfo *info;
531         guint8 *data;
532         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
533
534         table = get_hazardous_pointer ((gpointer volatile*)&cached_info, hp, 0);
535
536         info = table [index];
537
538         *unwind_info_len = info->len;
539         data = info->info;
540
541         mono_hazard_pointer_clear (hp, 0);
542
543         return data;
544 }
545
546 /*
547  * mono_unwind_get_dwarf_data_align:
548  *
549  *   Return the data alignment used by the encoded unwind information.
550  */
551 int
552 mono_unwind_get_dwarf_data_align (void)
553 {
554         return DWARF_DATA_ALIGN;
555 }
556
557 /*
558  * mono_unwind_get_dwarf_pc_reg:
559  *
560  *   Return the dwarf register number of the register holding the ip of the
561  * previous frame.
562  */
563 int
564 mono_unwind_get_dwarf_pc_reg (void)
565 {
566         return DWARF_PC_REG;
567 }
568
569 static void
570 decode_cie_op (guint8 *p, guint8 **endp)
571 {
572         int op = *p & 0xc0;
573
574         switch (op) {
575         case DW_CFA_advance_loc:
576                 p ++;
577                 break;
578         case DW_CFA_offset:
579                 p ++;
580                 decode_uleb128 (p, &p);
581                 break;
582         case 0: {
583                 int ext_op = *p;
584                 p ++;
585                 switch (ext_op) {
586                 case DW_CFA_def_cfa:
587                         decode_uleb128 (p, &p);
588                         decode_uleb128 (p, &p);
589                         break;
590                 case DW_CFA_def_cfa_offset:
591                         decode_uleb128 (p, &p);
592                         break;
593                 case DW_CFA_def_cfa_register:
594                         decode_uleb128 (p, &p);
595                         break;
596                 case DW_CFA_advance_loc4:
597                         p += 4;
598                         break;
599                 default:
600                         g_assert_not_reached ();
601                 }
602                 break;
603         }
604         default:
605                 g_assert_not_reached ();
606         }
607
608         *endp = p;
609 }
610
611 /* Pointer Encoding in the .eh_frame */
612 enum {
613         DW_EH_PE_absptr = 0x00,
614         DW_EH_PE_omit = 0xff,
615
616         DW_EH_PE_udata4 = 0x03,
617         DW_EH_PE_sdata4 = 0x0b,
618         DW_EH_PE_sdata8 = 0x0c,
619
620         DW_EH_PE_pcrel = 0x10,
621         DW_EH_PE_textrel = 0x20,
622         DW_EH_PE_datarel = 0x30,
623         DW_EH_PE_funcrel = 0x40,
624         DW_EH_PE_aligned = 0x50,
625
626         DW_EH_PE_indirect = 0x80
627 };
628
629 static gint64
630 read_encoded_val (guint32 encoding, guint8 *p, guint8 **endp)
631 {
632         gint64 res;
633
634         switch (encoding & 0xf) {
635         case DW_EH_PE_sdata8:
636                 res = *(gint64*)p;
637                 p += 8;
638                 break;
639         case DW_EH_PE_sdata4:
640                 res = *(gint32*)p;
641                 p += 4;
642                 break;
643         default:
644                 g_assert_not_reached ();
645         }
646
647         *endp = p;
648         return res;
649 }
650
651 /*
652  * decode_lsda:
653  *
654  *   Decode the Language Specific Data Area generated by LLVM.
655  */
656 static void
657 decode_lsda (guint8 *lsda, guint8 *code, MonoJitExceptionInfo **ex_info, guint32 *ex_info_len, gpointer **type_info)
658 {
659         gint32 ttype_offset, call_site_length;
660         gint32 ttype_encoding, call_site_encoding;
661         guint8 *ttype, *action_table, *call_site, *p;
662         int i, ncall_sites;
663
664         /*
665          * LLVM generates a c++ style LSDA, which can be decoded by looking at
666          * eh_personality.cc in gcc.
667          */
668         p = lsda;
669
670         /* Read @LPStart */
671         g_assert (*p == DW_EH_PE_omit);
672         p ++;
673
674         /* Read @TType */
675         ttype_encoding = *p;
676         p ++;
677         ttype_offset = decode_uleb128 (p, &p);
678         ttype = p + ttype_offset;
679
680         /* Read call-site table */
681         call_site_encoding = *p;
682         g_assert (call_site_encoding == DW_EH_PE_udata4);
683         p ++;
684         call_site_length = decode_uleb128 (p, &p);
685         call_site = p;
686         p += call_site_length;
687         action_table = p;
688
689         /* Calculate the size of our table */
690         ncall_sites = 0;
691         p = call_site;
692         while (p < action_table) {
693                 int block_start_offset, block_size, landing_pad, action_offset;
694
695                 block_start_offset = ((guint32*)p) [0];
696                 block_size = ((guint32*)p) [1];
697                 landing_pad = ((guint32*)p) [2];
698                 p += 3 * sizeof (guint32);
699                 action_offset = decode_uleb128 (p, &p);
700
701                 /* landing_pad == 0 means the region has no landing pad */
702                 if (landing_pad)
703                         ncall_sites ++;
704         }
705
706         if (ex_info) {
707                 *ex_info = g_malloc0 (ncall_sites * sizeof (MonoJitExceptionInfo));
708                 *ex_info_len = ncall_sites;
709         }
710
711         if (type_info)
712                 *type_info = g_malloc0 (ncall_sites * sizeof (gpointer));
713
714         p = call_site;
715         i = 0;
716         while (p < action_table) {
717                 int block_start_offset, block_size, landing_pad, action_offset, type_offset;
718                 guint8 *action, *tinfo;
719
720                 block_start_offset = ((guint32*)p) [0];
721                 block_size = ((guint32*)p) [1];
722                 landing_pad = ((guint32*)p) [2];
723                 p += 3 * sizeof (guint32);
724                 action_offset = decode_uleb128 (p, &p);
725
726                 action = action_table + action_offset - 1;
727
728                 type_offset = decode_sleb128 (action, &action);
729
730                 if (landing_pad) {
731                         //printf ("BLOCK: %p-%p %p, %d\n", code + block_start_offset, code + block_start_offset + block_size, code + landing_pad, action_offset);
732
733                         if (ttype_encoding == DW_EH_PE_absptr) {
734                                 guint8 *ttype_entry = (ttype - (type_offset * sizeof (gpointer)));
735                                 tinfo = *(gpointer*)ttype_entry;
736                         } else if (ttype_encoding == (DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4)) {
737                                 guint8 *ttype_entry = (ttype - (type_offset * 4));
738                                 gint32 offset = *(gint32*)ttype_entry;
739                                 guint8 *stub = ttype_entry + offset;
740                                 tinfo = *(gpointer*)stub;
741                         } else {
742                                 g_assert_not_reached ();
743                         }
744
745                         if (ex_info) {
746                                 if (*type_info)
747                                         (*type_info) [i] = tinfo;
748                                 (*ex_info)[i].try_start = code + block_start_offset;
749                                 (*ex_info)[i].try_end = code + block_start_offset + block_size;
750                                 (*ex_info)[i].handler_start = code + landing_pad;
751
752                         }
753                         i ++;
754                 }
755         }
756 }
757
758 /*
759  * mono_unwind_decode_fde:
760  *
761  *   Decode a DWARF FDE entry, returning the unwind opcodes.
762  * If not NULL, EX_INFO is set to a malloc-ed array of MonoJitExceptionInfo structures,
763  * only try_start, try_end and handler_start is set.
764  * If not NULL, TYPE_INFO is set to a malloc-ed array containing the ttype table from the
765  * LSDA.
766  */
767 guint8*
768 mono_unwind_decode_fde (guint8 *fde, guint32 *out_len, guint32 *code_len, MonoJitExceptionInfo **ex_info, guint32 *ex_info_len, gpointer **type_info)
769 {
770         guint8 *p, *cie, *fde_current, *fde_aug, *code, *fde_cfi, *cie_cfi;
771         gint32 fde_len, cie_offset, pc_begin, pc_range, aug_len, fde_data_len;
772         gint32 cie_len, cie_id, cie_version, code_align, data_align, return_reg;
773         gint32 i, cie_aug_len, buf_len;
774         char *cie_aug_str;
775         guint8 *buf;
776
777         /* 
778          * http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
779          */
780
781         /* Decode FDE */
782
783         p = fde;
784         // FIXME: Endianess ?
785         fde_len = *(guint32*)p;
786         g_assert (fde_len != 0xffffffff && fde_len != 0);
787         p += 4;
788         cie_offset = *(guint32*)p;
789         cie = p - cie_offset;
790         p += 4;
791         fde_current = p;
792
793         /* Decode CIE */
794         p = cie;
795         cie_len = *(guint32*)p;
796         p += 4;
797         cie_id = *(guint32*)p;
798         g_assert (cie_id == 0);
799         p += 4;
800         cie_version = *p;
801         g_assert (cie_version == 1);
802         p += 1;
803         cie_aug_str = (char*)p;
804         p += strlen (cie_aug_str) + 1;
805         code_align = decode_uleb128 (p, &p);
806         data_align = decode_sleb128 (p, &p);
807         return_reg = decode_uleb128 (p, &p);
808         if (strstr (cie_aug_str, "z")) {
809                 cie_aug_len = decode_uleb128 (p, &p);
810
811                 g_assert (!strcmp (cie_aug_str, "zR") || !strcmp (cie_aug_str, "zPLR"));
812
813                 /* Check that the augmention is what we expect */
814                 if (!strcmp (cie_aug_str, "zPLR")) {
815                         guint8 *cie_aug = p;
816                         guint32 p_encoding;
817
818                         /* P */
819                         p_encoding = *p;
820                         p ++;
821                         read_encoded_val (p_encoding, p, &p);
822
823                         /* L */
824                         g_assert ((*p == (DW_EH_PE_sdata4|DW_EH_PE_pcrel)) || (*p == (DW_EH_PE_sdata8|DW_EH_PE_pcrel)));
825                         p ++;
826                         /* R */
827                         g_assert (*p == (DW_EH_PE_sdata4|DW_EH_PE_pcrel));
828                         p ++;
829
830                         g_assert (p - cie_aug == cie_aug_len);
831                         
832                         p = cie_aug;
833                 }
834                 p += cie_aug_len;
835         }
836         cie_cfi = p;
837
838         /* Continue decoding FDE */
839         p = fde_current;
840         /* DW_EH_PE_sdata4|DW_EH_PE_pcrel encoding */
841         pc_begin = *(gint32*)p;
842         code = p + pc_begin;
843         p += 4;
844         pc_range = *(guint32*)p;
845         p += 4;
846         aug_len = decode_uleb128 (p, &p);
847         fde_aug = p;
848         p += aug_len;
849         fde_cfi = p;
850         fde_data_len = fde + 4 + fde_len - p;
851
852         if (code_len)
853                 *code_len = pc_range;
854
855         if (ex_info) {
856                 *ex_info = NULL;
857                 *ex_info_len = 0;
858         }
859
860         /* Decode FDE augmention */
861         if (aug_len) {
862                 gint32 lsda_offset;
863                 guint8 *lsda;
864
865                 /* sdata|pcrel encoding */
866                 if (aug_len == 4)
867                         lsda_offset = *(gint64*)fde_aug;
868                 else if (aug_len == 8)
869                         lsda_offset = *(gint32*)fde_aug;
870                 else
871                         g_assert_not_reached ();
872                 if (lsda_offset != 0) {
873                         lsda = fde_aug + *(gint32*)fde_aug;
874
875                         decode_lsda (lsda, code, ex_info, ex_info_len, type_info);
876                 }
877         }
878
879
880         /* Make sure the FDE uses the same constants as we do */
881         g_assert (code_align == 1);
882         g_assert (data_align == DWARF_DATA_ALIGN);
883         g_assert (return_reg == DWARF_PC_REG);
884
885         buf_len = (cie + cie_len + 4 - cie_cfi) + (fde + fde_len + 4 - fde_cfi);
886         buf = g_malloc0 (buf_len);
887
888         i = 0;
889         p = cie_cfi;
890         while (p < cie + cie_len + 4) {
891                 if (*p == DW_CFA_nop)
892                         break;
893                 else
894                         decode_cie_op (p, &p);
895         }
896         memcpy (buf + i, cie_cfi, p - cie_cfi);
897         i += p - cie_cfi;
898
899         p = fde_cfi;
900         while (p < fde + fde_len + 4) {
901                 if (*p == DW_CFA_nop)
902                         break;
903                 else
904                         decode_cie_op (p, &p);
905         }
906         memcpy (buf + i, fde_cfi, p - fde_cfi);
907         i += p - fde_cfi;
908         g_assert (i <= buf_len);
909
910         *out_len = i;
911
912         return g_realloc (buf, i);
913 }
914
915 /*
916  * mono_unwind_get_cie_program:
917  *
918  *   Get the unwind bytecode for the DWARF CIE.
919  */
920 GSList*
921 mono_unwind_get_cie_program (void)
922 {
923 #ifdef TARGET_AMD64
924         return mono_arch_get_cie_program ();
925 #elif defined(TARGET_POWERPC)
926         GSList *l = NULL;
927
928         mono_add_unwind_op_def_cfa (l, (guint8*)NULL, (guint8*)NULL, ppc_r1, 0);
929
930         return l;
931 #else
932         return NULL;
933 #endif
934 }