* unwind.h: New file, contains definitions for stack unwinding.
* mini.c (mono_emit_unwind_op): New helper function to append an unwind op
to cfg->unwind_ops.
* aot-compiler.c: Generalize the emitting of unwind information to use the
information in cfg->unwind_ops.
* mini-amd64.c (mono_arch_emit_prolog): Emit unwind info.
svn path=/trunk/mono/; revision=118941
2008-11-15 Zoltan Varga <vargaz@gmail.com>
+ * unwind.h: New file, contains definitions for stack unwinding.
+
+ * mini.c (mono_emit_unwind_op): New helper function to append an unwind op
+ to cfg->unwind_ops.
+
+ * aot-compiler.c: Generalize the emitting of unwind information to use the
+ information in cfg->unwind_ops.
+
+ * mini-amd64.c (mono_arch_emit_prolog): Emit unwind info.
+
* aot-compiler.c: Emit dwarf unwind information so gdb can unwind through
AOT method frames. Enable writing symbols for methods by default.
abcremoval2.c \
regalloc2.c \
simd-methods.h \
- simd-intrinsics.c
+ simd-intrinsics.c \
+ unwind.h
test_sources = \
basic-calls.cs \
#include <mono/metadata/gc-internal.h>
#include <mono/metadata/method-builder.h>
#include <mono/metadata/monitor.h>
+#include <mono/metadata/mempool-internals.h>
#include <mono/utils/mono-logger.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-time.h>
}
cfg->patch_info = patches;
}
+ /* Make a copy of the unwind info */
+ {
+ GSList *l, *unwind_ops;
+ MonoUnwindOp *op;
+
+ unwind_ops = NULL;
+ for (l = cfg->unwind_ops; l; l = l->next) {
+ op = mono_mempool_alloc (acfg->mempool, sizeof (MonoUnwindOp));
+ memcpy (op, l->data, sizeof (MonoUnwindOp));
+ unwind_ops = g_slist_prepend_mempool (acfg->mempool, unwind_ops, op);
+ }
+ cfg->unwind_ops = g_slist_reverse (unwind_ops);
+ }
/* Free some fields used by cfg to conserve memory */
mono_mempool_destroy (cfg->mempool);
/* Emitting DWARF debug information */
/*****************************************/
-/* 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
-
static G_GNUC_UNUSED void
emit_uleb128 (MonoAotCompile *acfg, guint32 value)
{
}
}
+#ifdef __x86_64__
+static int hw_reg_to_dwarf_reg [] = { 0, 2, 1, 3, 7, 6, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+#endif
+
static void
emit_dwarf_info (MonoAotCompile *acfg)
{
#ifdef __x86_64__
emit_byte (acfg, DW_CFA_def_cfa);
- /* For some reason, 7 is %rsp */
- emit_uleb128 (acfg, 7); /* reg=%rsp */
+ emit_uleb128 (acfg, hw_reg_to_dwarf_reg [AMD64_RSP]);
emit_uleb128 (acfg, 8); /* offset=8 */
emit_byte (acfg, DW_CFA_offset | AMD64_RIP);
emit_uleb128 (acfg, 1); /* offset=-8 */
/* DIEs for methods */
for (i = 0; i < acfg->nmethods; ++i) {
MonoCompile *cfg = acfg->cfgs [i];
+ GSList *l;
+ MonoUnwindOp *op;
+ int loc;
if (!cfg)
continue;
emit_symbol_diff (acfg, symbol2, symbol, 0); /* address_range */
emit_int32 (acfg, 0);
+ /* Convert the list of MonoUnwindOps to the format used by DWARF */
+ loc = 0;
+ l = cfg->unwind_ops;
#ifdef __x86_64__
- // FIXME:
- if (cfg->arch.omit_fp && cfg->arch.stack_alloc_size < 127) {
- emit_byte (acfg, DW_CFA_advance_loc | 4); /* size of alu_reg_imm () */
- emit_byte (acfg, DW_CFA_def_cfa_offset);
- emit_uleb128 (acfg, cfg->arch.stack_alloc_size + 8);
- }
+ /* Skip the first two ops which are in the CIE */
+ l = l->next->next;
+#endif
+ for (; l; l = l->next) {
+ op = l->data;
+
+ /* Convert the register from the hw encoding to the dwarf encoding */
+#ifdef __x86_64__
+ op->reg = hw_reg_to_dwarf_reg [op->reg];
#else
- g_assert_not_reached ();
+ g_assert_not_reached ();
#endif
+ /* Emit an advance_loc if neccesary */
+ if (op->when > loc) {
+ g_assert (op->when - loc < 32);
+ emit_byte (acfg, DW_CFA_advance_loc | (op->when - loc));
+ }
+
+ switch (op->op) {
+ case DW_CFA_def_cfa:
+ emit_byte (acfg, op->op);
+ emit_uleb128 (acfg, op->reg);
+ emit_uleb128 (acfg, op->val);
+ break;
+ case DW_CFA_def_cfa_offset:
+ emit_byte (acfg, op->op);
+ emit_uleb128 (acfg, op->val);
+ break;
+ case DW_CFA_def_cfa_register:
+ emit_byte (acfg, op->op);
+ emit_uleb128 (acfg, op->reg);
+ break;
+ case DW_CFA_offset:
+ emit_byte (acfg, DW_CFA_offset | op->reg);
+ emit_uleb128 (acfg, op->val / - 8);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ loc = op->when;
+ }
emit_alignment (acfg, sizeof (gpointer));
sprintf (symbol, ".Ldie%d_end", i);
MonoBasicBlock *bb;
MonoMethodSignature *sig;
MonoInst *ins;
- int alloc_size, pos, max_offset, i, quad, max_epilog_size;
+ int alloc_size, pos, max_offset, i, cfa_offset, quad, max_epilog_size;
guint8 *code;
CallInfo *cinfo;
gint32 lmf_offset = cfg->arch.lmf_offset;
/* Amount of stack space allocated by register saving code */
pos = 0;
+ /* Offset between RSP and the CFA */
+ cfa_offset = 0;
+
/*
* The prolog consists of the following parts:
* FP present:
* - save callee saved regs using moves
*/
+ // CFA = sp + 8
+ cfa_offset = 8;
+ mono_emit_unwind_op_def_cfa (cfg, code, AMD64_RSP, 8);
+ // IP saved at CFA - 8
+ mono_emit_unwind_op_offset (cfg, code, AMD64_RIP, -cfa_offset);
async_exc_point (code);
if (!cfg->arch.omit_fp) {
amd64_push_reg (code, AMD64_RBP);
+ cfa_offset += 8;
+ mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+ mono_emit_unwind_op_offset (cfg, code, AMD64_RBP, - cfa_offset);
async_exc_point (code);
#ifdef PLATFORM_WIN32
mono_arch_unwindinfo_add_push_nonvol (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
#endif
amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof (gpointer));
+ mono_emit_unwind_op_def_cfa_reg (cfg, code, AMD64_RBP);
async_exc_point (code);
#ifdef PLATFORM_WIN32
mono_arch_unwindinfo_add_set_fpreg (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
/* Save callee saved registers */
if (!cfg->arch.omit_fp && !method->save_lmf) {
+ int offset = cfa_offset;
+
for (i = 0; i < AMD64_NREG; ++i)
if (AMD64_IS_CALLEE_SAVED_REG (i) && (cfg->used_int_regs & (1 << i))) {
amd64_push_reg (code, i);
pos += sizeof (gpointer);
+ offset += 8;
+ mono_emit_unwind_op_offset (cfg, code, i, - offset);
async_exc_point (code);
}
}
guint32 remaining_size = alloc_size;
while (remaining_size >= 0x1000) {
amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 0x1000);
+ if (cfg->arch.omit_fp) {
+ cfa_offset += 0x1000;
+ mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+ }
async_exc_point (code);
#ifdef PLATFORM_WIN32
if (cfg->arch.omit_fp)
}
if (remaining_size) {
amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, remaining_size);
- async_exc_point (code);
+ if (cfg->arch.omit_fp) {
+ cfa_offset += remaining_size;
+ mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+ async_exc_point (code);
+ }
#ifdef PLATFORM_WIN32
if (cfg->arch.omit_fp)
mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, remaining_size);
}
#else
amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, alloc_size);
- async_exc_point (code);
+ if (cfg->arch.omit_fp) {
+ cfa_offset += alloc_size;
+ mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+ async_exc_point (code);
+ }
#endif
}
for (i = 0; i < AMD64_NREG; ++i)
if (AMD64_IS_CALLEE_SAVED_REG (i) && (cfg->used_int_regs & (1 << i))) {
amd64_mov_membase_reg (code, AMD64_RSP, save_area_offset, i, 8);
+ mono_emit_unwind_op_offset (cfg, code, i, - (cfa_offset - save_area_offset));
save_area_offset += 8;
async_exc_point (code);
}
--- /dev/null
+/*
+ * 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".
+ * Currently, this is only used for emitting unwind info in AOT files.
+ */
+
+/* CFA = Canonical Frame Address */
+
+/* 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_... */
+ guint8 reg; /* register number in the hardware encoding */
+ guint32 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) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa, (reg), (offset))
+/* Set cfa to reg+existing offset */
+#define mono_emit_unwind_op_def_cfa_reg(cfg,ip,reg) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa_register, (reg), (0))
+/* Set cfa to existing reg+offset */
+#define mono_emit_unwind_op_def_cfa_offset(cfg,ip,offset) mono_emit_unwind_op (cfg, (ip) - (cfg)->native_code, DW_CFA_def_cfa_offset, (0), (offset))
+/* 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))
+
+#endif