Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / mini / mini-unwind.h
1 /**
2  * \file
3  * Stack Unwinding Interface
4  *
5  * Authors:
6  *   Zoltan Varga (vargaz@gmail.com)
7  *
8  * (C) 2007 Novell, Inc.
9  */
10
11 #ifndef __MONO_UNWIND_H__
12 #define __MONO_UNWIND_H__
13
14 #include "mini.h"
15
16 /* This is the same as mgreg_t, except on 32 bit bit platforms with callee saved fp regs */
17 #ifndef mono_unwind_reg_t
18 #define mono_unwind_reg_t mgreg_t
19 #endif
20
21 /*
22  * This is a platform-independent interface for unwinding through stack frames 
23  * based on the Dwarf unwinding interface.
24  * See http://dwarfstd.org/Dwarf3.pdf, section "Call Frame Information".
25  */
26
27 /*
28  * CFA = Canonical Frame Address. By convention, this is the value of the stack pointer
29  * prior to the execution of the call instruction in the caller. I.e. on x86, it is
30  * esp + 4 on entry to a function. The value of the CFA does not change during execution
31  * of a function. There are two kinds of unwind directives:
32  * - those that describe how to compute the CFA at a given pc offset inside a function
33  * - those that describe where a given register is saved relative to the CFA.
34  */
35
36 /* Unwind ops */
37
38 /* The low 6 bits contain additional information */
39 #define DW_CFA_advance_loc        0x40
40 #define DW_CFA_offset             0x80
41 #define DW_CFA_restore            0xc0
42
43 #define DW_CFA_nop              0x00
44 #define DW_CFA_set_loc          0x01
45 #define DW_CFA_advance_loc1     0x02
46 #define DW_CFA_advance_loc2     0x03
47 #define DW_CFA_advance_loc4     0x04
48 #define DW_CFA_offset_extended  0x05
49 #define DW_CFA_restore_extended 0x06
50 #define DW_CFA_undefined        0x07
51 #define DW_CFA_same_value       0x08
52 #define DW_CFA_register         0x09
53 #define DW_CFA_remember_state   0x0a
54 #define DW_CFA_restore_state    0x0b
55 #define DW_CFA_def_cfa          0x0c
56 #define DW_CFA_def_cfa_register 0x0d
57 #define DW_CFA_def_cfa_offset   0x0e
58 #define DW_CFA_def_cfa_expression 0x0f
59 #define DW_CFA_expression       0x10
60 #define DW_CFA_offset_extended_sf 0x11
61 #define DW_CFA_def_cfa_sf       0x12
62 #define DW_CFA_def_cfa_offset_sf 0x13
63 #define DW_CFA_val_offset        0x14
64 #define DW_CFA_val_offset_sf     0x15
65 #define DW_CFA_val_expression    0x16
66 #define DW_CFA_lo_user           0x1c
67 #define DW_CFA_hi_user           0x3f
68
69 /*
70  * Mono extension, advance loc to a location stored outside the unwind info.
71  * This is required to make the unwind descriptors sharable, since otherwise each one would contain
72  * an advance_loc with a different offset just before the unwind ops for the epilog.
73  */
74 #define DW_CFA_mono_advance_loc DW_CFA_lo_user
75
76 /*
77  * Mono extension, Windows x64 unwind ABI needs some more details around sp alloc size and fp offset.
78  */
79 #if defined(TARGET_WIN32) && defined(TARGET_AMD64)
80 #define DW_CFA_mono_sp_alloc_info_win64 (DW_CFA_lo_user + 1)
81 #define DW_CFA_mono_fp_alloc_info_win64 (DW_CFA_lo_user + 2)
82 #endif
83
84 /* Represents one unwind instruction */
85 typedef struct {
86         guint8 op; /* One of DW_CFA_... */
87         guint16 reg; /* register number in the hardware encoding */
88         gint32 val; /* arbitrary value */
89         guint32 when; /* The offset _after_ the cpu instruction this unwind op belongs to */
90 } MonoUnwindOp;
91
92 /* 
93  * Macros for emitting MonoUnwindOp structures.
94  * These should be called _after_ emitting the cpu instruction the unwind op
95  * belongs to.
96  */
97
98 /* Set cfa to reg+offset */
99 #define mono_emit_unwind_op_def_cfa(cfg,ip,reg,offset) do { mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa, (reg), (offset)); (cfg)->cur_cfa_reg = (reg); (cfg)->cur_cfa_offset = (offset); } while (0)
100 /* Set cfa to reg+existing offset */
101 #define mono_emit_unwind_op_def_cfa_reg(cfg,ip,reg) do { mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa_register, (reg), (0)); (cfg)->cur_cfa_reg = (reg); } while (0)
102 /* Set cfa to existing reg+offset */
103 #define mono_emit_unwind_op_def_cfa_offset(cfg,ip,offset) do { mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa_offset, (0), (offset)); (cfg)->cur_cfa_offset = (offset); } while (0)
104 /* Reg is the same as it was on enter to the function */
105 #define mono_emit_unwind_op_same_value(cfg,ip,reg) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_same_value, (reg), 0)
106 /* Reg is saved at cfa+offset */
107 #define mono_emit_unwind_op_offset(cfg,ip,reg,offset) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_offset, (reg), (offset))
108 /* Save the unwind state into an implicit stack */
109 #define mono_emit_unwind_op_remember_state(cfg,ip) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_remember_state, 0, 0)
110 /* Restore the unwind state from the state stack */
111 #define mono_emit_unwind_op_restore_state(cfg,ip) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_restore_state, 0, 0)
112 /*
113  * Mark the current location as a location stored outside the unwind info, which will be passed
114  * explicitly to mono_unwind_frame () in the MARK_LOCATIONS argument. This allows the unwind info
115  * to be shared among multiple methods.
116  */
117 #define mono_emit_unwind_op_mark_loc(cfg,ip,n) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_mono_advance_loc, 0, (n))
118
119 #if defined(TARGET_WIN32) && defined(TARGET_AMD64)
120 #define mono_emit_unwind_op_sp_alloc(cfg,ip,size) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_mono_sp_alloc_info_win64, 0, (size))
121 #define mono_emit_unwind_op_fp_alloc(cfg,ip,reg,size) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_mono_fp_alloc_info_win64, (reg), (size))
122 #else
123 #define mono_emit_unwind_op_sp_alloc(cfg,ip,size)
124 #define mono_emit_unwind_op_fp_alloc(cfg,ip,reg,size)
125 #endif
126
127 /* Similar macros usable when a cfg is not available, like for trampolines */
128 #define mono_add_unwind_op_def_cfa(op_list,code,buf,reg,offset) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_def_cfa, (reg), (offset))); } while (0)
129 #define mono_add_unwind_op_def_cfa_reg(op_list,code,buf,reg) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_def_cfa_register, (reg), (0))); } while (0)
130 #define mono_add_unwind_op_def_cfa_offset(op_list,code,buf,offset) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_def_cfa_offset, 0, (offset))); } while (0)
131 #define mono_add_unwind_op_same_value(op_list,code,buf,reg) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_same_value, (reg), 0)); } while (0)
132 #define mono_add_unwind_op_offset(op_list,code,buf,reg,offset) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_offset, (reg), (offset))); } while (0)
133
134 #if defined(TARGET_WIN32) && defined(TARGET_AMD64)
135 #define mono_add_unwind_op_sp_alloc(op_list,code,buf,size) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_mono_sp_alloc_info_win64, 0, (size))); } while (0)
136 #define mono_add_unwind_op_fp_alloc(op_list,code,buf,reg,size) do { (op_list) = g_slist_append ((op_list), mono_create_unwind_op ((code) - (buf), DW_CFA_mono_fp_alloc_info_win64, (reg), (size))); } while (0)
137 #else
138 #define mono_add_unwind_op_sp_alloc(op_list,code,buf,size)
139 #define mono_add_unwind_op_fp_alloc(op_list,code,buf,reg,size)
140 #endif
141
142 #define mono_free_unwind_info(op_list) do { GSList *l; for (l = op_list; l; l = l->next) g_free (l->data); g_slist_free (op_list); op_list = NULL; } while (0)
143
144 /* Pointer Encoding in the .eh_frame */
145 enum {
146         DW_EH_PE_absptr = 0x00,
147         DW_EH_PE_omit = 0xff,
148
149         DW_EH_PE_udata4 = 0x03,
150         DW_EH_PE_sdata4 = 0x0b,
151         DW_EH_PE_sdata8 = 0x0c,
152
153         DW_EH_PE_pcrel = 0x10,
154         DW_EH_PE_textrel = 0x20,
155         DW_EH_PE_datarel = 0x30,
156         DW_EH_PE_funcrel = 0x40,
157         DW_EH_PE_aligned = 0x50,
158
159         DW_EH_PE_indirect = 0x80
160 };
161
162 int
163 mono_hw_reg_to_dwarf_reg (int reg);
164
165 int
166 mono_dwarf_reg_to_hw_reg (int reg);
167
168 int
169 mono_unwind_get_dwarf_data_align (void);
170
171 int
172 mono_unwind_get_dwarf_pc_reg (void);
173
174 guint8*
175 mono_unwind_ops_encode_full (GSList *unwind_ops, guint32 *out_len, gboolean enable_extensions);
176
177 guint8*
178 mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len);
179
180 void
181 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
182                                    guint8 *start_ip, guint8 *end_ip, guint8 *ip, guint8 **mark_locations,
183                                    mono_unwind_reg_t *regs, int nregs,
184                                    mgreg_t **save_locations, int save_locations_len,
185                                    guint8 **out_cfa);
186
187 void mono_unwind_init (void);
188
189 void mono_unwind_cleanup (void);
190
191 guint32 mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len);
192
193 guint8* mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len);
194
195 guint8* mono_unwind_decode_fde (guint8 *fde, guint32 *out_len, guint32 *code_len, MonoJitExceptionInfo **ex_info, guint32 *ex_info_len, gpointer **type_info, int *this_reg, int *this_offset) MONO_LLVM_INTERNAL;
196
197 /* Data retrieved from an LLVM Mono FDE entry */
198 typedef struct {
199         guint32 unw_info_len;
200         guint32 ex_info_len;
201         int type_info_len;
202         int this_reg;
203         int this_offset;
204 } MonoLLVMFDEInfo;
205
206 void
207 mono_unwind_decode_llvm_mono_fde (guint8 *fde, int fde_len, guint8 *cie, guint8 *code, MonoLLVMFDEInfo *res, MonoJitExceptionInfo *ei, gpointer *type_info, guint8 *unw_info) MONO_LLVM_INTERNAL;
208
209 GSList* mono_unwind_get_cie_program (void);
210
211 void mono_print_unwind_info (guint8 *unwind_info, int unwind_info_len) MONO_LLVM_INTERNAL;
212
213 #endif