+/*
+ * unwind.c: Stack Unwinding Interface
+ *
+ * Authors:
+ * Zoltan Varga (vargaz@gmail.com)
+ *
+ * (C) 2008 Novell, Inc.
+ */
+
+#include "mini.h"
+#include "unwind.h"
+
+#ifdef __x86_64__
+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 };
+#endif
+
+/*
+ * mono_hw_reg_to_dwarf_reg:
+ *
+ * Map the hardware register number REG to the register number used by DWARF.
+ */
+int
+mono_hw_reg_to_dwarf_reg (int reg)
+{
+#ifdef __x86_64__
+ return map_hw_reg_to_dwarf_reg [reg];
+#else
+ g_assert_not_reached ();
+ return -1;
+#endif
+}
+
+static G_GNUC_UNUSED void
+encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf)
+{
+ guint8 *p = buf;
+
+ do {
+ guint8 b = value & 0x7f;
+ value >>= 7;
+ if (value != 0) /* more bytes to come */
+ b |= 0x80;
+ *p ++ = b;
+ } while (value);
+
+ *endbuf = p;
+}
+
+/*
+ * mono_unwind_ops_encode:
+ *
+ * Encode the unwind ops in UNWIND_OPS into the compact DWARF encoding.
+ * Return a pointer to malloc'ed memory.
+ */
+guint8*
+mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len)
+{
+ GSList *l;
+ MonoUnwindOp *op;
+ int loc;
+ guint8 *buf, *p, *res;
+
+ p = buf = g_malloc0 (256);
+
+ loc = 0;
+ l = unwind_ops;
+ for (; l; l = l->next) {
+ int reg;
+
+ op = l->data;
+
+ /* Convert the register from the hw encoding to the dwarf encoding */
+ reg = mono_hw_reg_to_dwarf_reg (op->reg);
+
+ /* Emit an advance_loc if neccesary */
+ if (op->when > loc) {
+ g_assert (op->when - loc < 32);
+ *p ++ = DW_CFA_advance_loc | (op->when - loc);
+ }
+
+ switch (op->op) {
+ case DW_CFA_def_cfa:
+ *p ++ = op->op;
+ encode_uleb128 (reg, p, &p);
+ encode_uleb128 (op->val, p, &p);
+ break;
+ case DW_CFA_def_cfa_offset:
+ *p ++ = op->op;
+ encode_uleb128 (op->val, p, &p);
+ break;
+ case DW_CFA_def_cfa_register:
+ *p ++ = op->op;
+ encode_uleb128 (reg, p, &p);
+ break;
+ case DW_CFA_offset:
+ *p ++ = DW_CFA_offset | reg;
+ encode_uleb128 (op->val / - 8, p, &p);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ loc = op->when;
+ }
+
+ g_assert (p - buf < 256);
+ *out_len = p - buf;
+ res = g_malloc (p - buf);
+ memcpy (res, buf, p - buf);
+ g_free (buf);
+ return res;
+}