2 * unwind.c: Stack Unwinding Interface
5 * Zoltan Varga (vargaz@gmail.com)
7 * (C) 2008 Novell, Inc.
24 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 };
25 #define NUM_REGS AMD64_NREG
30 static gboolean dwarf_reg_to_hw_reg_inited;
32 static int map_dwarf_reg_to_hw_reg [NUM_REGS];
35 * mono_hw_reg_to_dwarf_reg:
37 * Map the hardware register number REG to the register number used by DWARF.
40 mono_hw_reg_to_dwarf_reg (int reg)
43 return map_hw_reg_to_dwarf_reg [reg];
45 g_assert_not_reached ();
55 g_assert (sizeof (map_hw_reg_to_dwarf_reg) / sizeof (int) == NUM_REGS);
56 for (i = 0; i < NUM_REGS; ++i) {
57 map_dwarf_reg_to_hw_reg [mono_hw_reg_to_dwarf_reg (i)] = i;
60 mono_memory_barrier ();
61 dwarf_reg_to_hw_reg_inited = TRUE;
65 mono_dwarf_reg_to_hw_reg (int reg)
67 if (!dwarf_reg_to_hw_reg_inited)
70 return map_dwarf_reg_to_hw_reg [reg];
73 static G_GNUC_UNUSED void
74 encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf)
79 guint8 b = value & 0x7f;
81 if (value != 0) /* more bytes to come */
90 decode_uleb128 (guint8 *buf, guint8 **endbuf)
100 res = res | (((int)(b & 0x7f)) << shift);
112 * mono_unwind_ops_encode:
114 * Encode the unwind ops in UNWIND_OPS into the compact DWARF encoding.
115 * Return a pointer to malloc'ed memory.
118 mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len)
123 guint8 *buf, *p, *res;
125 p = buf = g_malloc0 (256);
129 for (; l; l = l->next) {
134 /* Convert the register from the hw encoding to the dwarf encoding */
135 reg = mono_hw_reg_to_dwarf_reg (op->reg);
137 /* Emit an advance_loc if neccesary */
138 if (op->when > loc) {
139 g_assert (op->when - loc < 32);
140 *p ++ = DW_CFA_advance_loc | (op->when - loc);
146 encode_uleb128 (reg, p, &p);
147 encode_uleb128 (op->val, p, &p);
149 case DW_CFA_def_cfa_offset:
151 encode_uleb128 (op->val, p, &p);
153 case DW_CFA_def_cfa_register:
155 encode_uleb128 (reg, p, &p);
158 *p ++ = DW_CFA_offset | reg;
159 encode_uleb128 (op->val / - 8, p, &p);
162 g_assert_not_reached ();
169 g_assert (p - buf < 256);
171 res = g_malloc (p - buf);
172 memcpy (res, buf, p - buf);
178 #define UNW_DEBUG(stmt) do { stmt; } while (0)
180 #define UNW_DEBUG(stmt) do { } while (0)
183 static G_GNUC_UNUSED void
184 print_dwarf_state (int cfa_reg, int cfa_offset, int ip, int nregs, Loc *locations)
188 printf ("\t%x: cfa=r%d+%d ", ip, cfa_reg, cfa_offset);
190 for (i = 0; i < nregs; ++i)
191 if (locations [i].loc_type == LOC_OFFSET)
192 printf ("r%d@%d(cfa) ", i, locations [i].offset);
197 * Given the state of the current frame as stored in REGS, execute the unwind
198 * operations in unwind_info until the location counter reaches POS. The result is
199 * stored back into REGS. OUT_CFA will receive the value of the CFA.
202 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
203 int data_align_factor,
204 guint8 *start_ip, guint8 *end_ip, guint8 *ip, gssize *regs,
205 int nregs, guint8 **out_cfa)
207 Loc locations [NUM_REGS];
208 int i, pos, reg, cfa_reg, cfa_offset;
212 g_assert (nregs <= NUM_REGS);
214 for (i = 0; i < nregs; ++i)
215 locations [i].loc_type = LOC_SAME;
219 while (pos < ip - start_ip && p < unwind_info + unwind_info_len) {
223 case DW_CFA_advance_loc:
224 UNW_DEBUG (print_dwarf_state (cfa_reg, cfa_offset, pos, nregs, locations));
229 reg = mono_dwarf_reg_to_hw_reg (*p & 0x3f);
231 locations [reg].loc_type = LOC_OFFSET;
232 locations [reg].offset = decode_uleb128 (p, &p) * data_align_factor;
239 cfa_reg = mono_dwarf_reg_to_hw_reg (decode_uleb128 (p, &p));
240 cfa_offset = decode_uleb128 (p, &p);
242 case DW_CFA_def_cfa_offset:
243 cfa_offset = decode_uleb128 (p, &p);
245 case DW_CFA_def_cfa_register:
246 cfa_reg = mono_dwarf_reg_to_hw_reg (decode_uleb128 (p, &p));
249 g_assert_not_reached ();
254 g_assert_not_reached ();
258 cfa_val = (guint8*)regs [cfa_reg] + cfa_offset;
259 for (i = 0; i < nregs; ++i) {
260 if (locations [i].loc_type == LOC_OFFSET)
261 regs [i] = *(gssize*)(cfa_val + locations [i].offset);