summaryrefslogtreecommitdiff
path: root/mono/mini/mini-unwind.h
blob: 6b6ba7ad2f94fb6989412733dfd1a098e2fd5e82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
 * mini-unwind.h: Stack Unwinding Interface
 *
 * Authors:
 *   Zoltan Varga (vargaz@gmail.com)
 *
 * (C) 2007 Novell, Inc.
 */

#ifndef __MONO_UNWIND_H__
#define __MONO_UNWIND_H__

#include "mini.h"

/*
 * This is a platform-independent interface for unwinding through stack frames 
 * based on the Dwarf unwinding interface.
 * See http://dwarfstd.org/Dwarf3.pdf, section "Call Frame Information".
 */

/*
 * CFA = Canonical Frame Address. By convention, this is the value of the stack pointer
 * prior to the execution of the call instruction in the caller. I.e. on x86, it is
 * esp + 4 on entry to a function. The value of the CFA does not change during execution
 * of a function. There are two kinds of unwind directives:
 * - those that describe how to compute the CFA at a given pc offset inside a function
 * - those that describe where a given register is saved relative to the CFA.
 */

/* Unwind ops */

/* The low 6 bits contain additional information */
#define DW_CFA_advance_loc        0x40
#define DW_CFA_offset             0x80
#define DW_CFA_restore            0xc0

#define DW_CFA_nop              0x00
#define DW_CFA_set_loc          0x01
#define DW_CFA_advance_loc1     0x02
#define DW_CFA_advance_loc2     0x03
#define DW_CFA_advance_loc4     0x04
#define DW_CFA_offset_extended  0x05
#define DW_CFA_restore_extended 0x06
#define DW_CFA_undefined        0x07
#define DW_CFA_same_value       0x08
#define DW_CFA_register         0x09
#define DW_CFA_remember_state   0x0a
#define DW_CFA_restore_state    0x0b
#define DW_CFA_def_cfa          0x0c
#define DW_CFA_def_cfa_register 0x0d
#define DW_CFA_def_cfa_offset   0x0e
#define DW_CFA_def_cfa_expression 0x0f
#define DW_CFA_expression       0x10
#define DW_CFA_offset_extended_sf 0x11
#define DW_CFA_def_cfa_sf       0x12
#define DW_CFA_def_cfa_offset_sf 0x13
#define DW_CFA_val_offset        0x14
#define DW_CFA_val_offset_sf     0x15
#define DW_CFA_val_expression    0x16
#define DW_CFA_lo_user           0x1c
#define DW_CFA_hi_user           0x3f

/* Represents one unwind instruction */
typedef struct {
	guint8 op; /* One of DW_CFA_... */
	guint16 reg; /* register number in the hardware encoding */
	gint32 val; /* arbitrary value */
	guint32 when; /* The offset _after_ the cpu instruction this unwind op belongs to */
} MonoUnwindOp;

/* 
 * Macros for emitting MonoUnwindOp structures.
 * These should be called _after_ emitting the cpu instruction the unwind op
 * belongs to.
 */

/* Set cfa to reg+offset */
#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)->cfa_reg = (reg); (cfg)->cfa_offset = (offset); } while (0)
/* Set cfa to reg+existing offset */
#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)->cfa_reg = (reg); } while (0)
/* Set cfa to existing reg+offset */
#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)->cfa_offset = (offset); } while (0)
/* Reg is the same as it was on enter to the function */
#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)
/* Reg is saved at cfa+offset */
#define mono_emit_unwind_op_offset(cfg,ip,reg,offset) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_offset, (reg), (offset))

/* Similar macros usable when a cfg is not available, like for trampolines */
#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)
#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)
#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)
#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)
#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)

/* Pointer Encoding in the .eh_frame */
enum {
	DW_EH_PE_absptr = 0x00,
	DW_EH_PE_omit = 0xff,

	DW_EH_PE_udata4 = 0x03,
	DW_EH_PE_sdata4 = 0x0b,
	DW_EH_PE_sdata8 = 0x0c,

	DW_EH_PE_pcrel = 0x10,
	DW_EH_PE_textrel = 0x20,
	DW_EH_PE_datarel = 0x30,
	DW_EH_PE_funcrel = 0x40,
	DW_EH_PE_aligned = 0x50,

	DW_EH_PE_indirect = 0x80
};

int
mono_hw_reg_to_dwarf_reg (int reg) MONO_INTERNAL;

int
mono_dwarf_reg_to_hw_reg (int reg) MONO_INTERNAL;

int
mono_unwind_get_dwarf_data_align (void) MONO_INTERNAL;

int
mono_unwind_get_dwarf_pc_reg (void) MONO_INTERNAL;

guint8*
mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len) MONO_INTERNAL;

void
mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
				   guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, int nregs,
				   mgreg_t **save_locations, int save_locations_len,
				   guint8 **out_cfa) MONO_INTERNAL;

void mono_unwind_init (void) MONO_INTERNAL;

void mono_unwind_cleanup (void) MONO_INTERNAL;

guint32 mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) MONO_INTERNAL;

guint8* mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len) MONO_INTERNAL;

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;

/* Data retrieved from an LLVM Mono FDE entry */
typedef struct {
	/* Malloc'ed */
	guint8 *unw_info;
	guint32 unw_info_len;
	MonoJitExceptionInfo *ex_info;
	guint32 ex_info_len;
	gpointer *type_info;
	int this_reg;
	int this_offset;
} MonoLLVMFDEInfo;

void
mono_unwind_decode_llvm_mono_fde (guint8 *fde, int fde_len, guint8 *cie, guint8 *code, MonoLLVMFDEInfo *res) MONO_INTERNAL;

GSList* mono_unwind_get_cie_program (void) MONO_INTERNAL;

void mono_print_unwind_info (guint8 *unwind_info, int unwind_info_len) MONO_LLVM_INTERNAL;

#endif