2009-12-07 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / dwarfwriter.c
index d99d97d29fca4cd3de2d34ee2992d8b6646f35c9..cefa085a4a3b81bb73bfd7b3e88a9cd986ca0567 100644 (file)
@@ -8,6 +8,8 @@
  */
 
 #include "config.h"
+
+#if !defined(DISABLE_AOT) && !defined(DISABLE_JIT)
 #include "dwarfwriter.h"
 
 #include <sys/types.h>
@@ -21,7 +23,7 @@
 #include <mono/metadata/debug-mono-symfile.h>
 #include <mono/utils/mono-compiler.h>
 
-#ifndef PLATFORM_WIN32
+#ifndef HOST_WIN32
 #include <mono/utils/freebsd-elf32.h>
 #include <mono/utils/freebsd-elf64.h>
 #endif
@@ -38,6 +40,9 @@ struct _MonoDwarfWriter
        FILE *il_file;
        int il_file_line_index, loclist_index;
        GSList *cie_program;
+       FILE *fp;
+       const char *temp_prefix;
+       gboolean emit_line, appending;
 };
 
 /*
@@ -46,14 +51,35 @@ struct _MonoDwarfWriter
  *   Create a DWARF writer object. WRITER is the underlying image writer this 
  * writer will emit to. IL_FILE is the file where IL code will be dumped to for
  * methods which have no line number info. It can be NULL.
+ * If APPENDING is TRUE, the output file will be in assembleable state after each
+ * call to the _emit_ functions. This is used for XDEBUG. If APPENDING is FALSE,
+ * a separate mono_dwarf_writer_close () call is needed to finish the emission of
+ * debug information.
  */
 MonoDwarfWriter*
-mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file)
+mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, int il_file_start_line, gboolean appending)
 {
        MonoDwarfWriter *w = g_new0 (MonoDwarfWriter, 1);
        
+       /*
+        * The appending flag is needed because we use subsections to order things in 
+        * the debug info, and:
+        * - apple's assembler doesn't support them
+        * - the binary writer has problems with subsections+alignment
+        * So instead of subsections, we use the _close () function in AOT mode,
+        * which writes out things in order.
+        */
+
        w->w = writer;
        w->il_file = il_file;
+       w->il_file_line_index = il_file_start_line;
+       w->appending = appending;
+
+       if (appending)
+               g_assert (img_writer_subsections_supported (w->w));
+
+       w->fp = img_writer_get_fp (w->w);
+       w->temp_prefix = img_writer_get_temp_label_prefix (w->w);
 
        return w;
 }
@@ -64,6 +90,12 @@ mono_dwarf_writer_destroy (MonoDwarfWriter *w)
        g_free (w);
 }
 
+int
+mono_dwarf_writer_get_il_file_line_index (MonoDwarfWriter *w)
+{
+       return w->il_file_line_index;
+}
+
 /* Wrappers around the image writer functions */
 
 static inline void
@@ -273,7 +305,8 @@ emit_cie (MonoDwarfWriter *w)
        emit_alignment (w, 8);
 
        /* Emit a CIE */
-       emit_symbol_diff (w, ".Lcie0_end", ".", -4); /* length */
+       emit_symbol_diff (w, ".Lcie0_end", ".Lcie0_start", 0); /* length */
+       emit_label (w, ".Lcie0_start");
        emit_int32 (w, 0xffffffff); /* CIE id */
        emit_byte (w, 3); /* version */
        emit_string (w, ""); /* augmention */
@@ -304,15 +337,18 @@ static void
 emit_fde (MonoDwarfWriter *w, int fde_index, char *start_symbol, char *end_symbol,
                  guint8 *code, guint32 code_size, GSList *unwind_ops, gboolean use_cie)
 {
-       char symbol [128];
+       char symbol1 [128];
+       char symbol2 [128];
        GSList *l;
        guint8 *uw_info;
        guint32 uw_info_len;
 
        emit_section_change (w, ".debug_frame", 0);
 
-       sprintf (symbol, ".Lfde%d_end", fde_index);
-       emit_symbol_diff (w, symbol, ".", -4); /* length */
+       sprintf (symbol1, ".Lfde%d_start", fde_index);
+       sprintf (symbol2, ".Lfde%d_end", fde_index);
+       emit_symbol_diff (w, symbol2, symbol1, 0); /* length */
+       emit_label (w, symbol1);
        emit_int32 (w, 0); /* CIE_pointer */
        if (start_symbol) {
                emit_pointer (w, start_symbol); /* initial_location */
@@ -345,9 +381,8 @@ emit_fde (MonoDwarfWriter *w, int fde_index, char *start_symbol, char *end_symbo
        emit_bytes (w, uw_info, uw_info_len);
        g_free (uw_info);
 
-       emit_alignment (w, sizeof (gpointer));
-       sprintf (symbol, ".Lfde%d_end", fde_index);
-       emit_label (w, symbol);
+       emit_alignment (w, sizeof (mgreg_t));
+       emit_label (w, symbol2);
 }
 
 /* Abbrevations */
@@ -367,6 +402,7 @@ emit_fde (MonoDwarfWriter *w, int fde_index, char *start_symbol, char *end_symbo
 #define ABBREV_REFERENCE_TYPE 14
 #define ABBREV_PARAM_LOCLIST 15
 #define ABBREV_INHERITANCE 16
+#define ABBREV_STRUCT_TYPE_NOCHILDREN 17
 
 static int compile_unit_attr [] = {
        DW_AT_producer     ,DW_FORM_string,
@@ -559,14 +595,6 @@ emit_line_number_file_name (MonoDwarfWriter *w, const char *name,
 static void
 emit_line_number_info_begin (MonoDwarfWriter *w)
 {
-       if (!w->il_file) {
-               /* FIXME: This doesn't seem to work with !xdebug */
-               emit_section_change (w, ".debug_line", 0);
-               emit_label (w, ".Ldebug_line_start");
-               emit_label (w, ".Ldebug_line_section_start");
-               return;
-       }
-
        /* Line number info header */
        /* 
         * GAS seems to emit its own data to the end of the first subsection, so we use
@@ -627,12 +655,34 @@ emit_line_number_info_begin (MonoDwarfWriter *w)
        emit_label (w, ".Ldebug_line_end");
 }
 
+/*
+ * Some assemblers like apple's do not support subsections, so we can't place
+ * .Ldebug_info_end at the end of the section using subsections. Instead, we 
+ * define it every time something gets added to the .debug_info section.
+ * The apple assember seems to use the last definition.
+ */
+static void
+emit_debug_info_end (MonoDwarfWriter *w)
+{
+       /* This doesn't seem to work/required with recent iphone sdk versions */
+#if 0
+       if (!img_writer_subsections_supported (w->w))
+               fprintf (w->fp, "\n.set %sdebug_info_end,.\n", w->temp_prefix);
+#endif
+}
+
 void
 mono_dwarf_writer_emit_base_info (MonoDwarfWriter *w, GSList *base_unwind_program)
 {
        char *s, *build_info;
        int i;
 
+       if (!img_writer_subsections_supported (w->w))
+               /* Can't emit line number info without subsections */
+               w->emit_line = FALSE;
+       else
+               w->emit_line = TRUE;
+
        w->cie_program = base_unwind_program;
 
        emit_section_change (w, ".debug_abbrev", 0);
@@ -648,6 +698,8 @@ mono_dwarf_writer_emit_base_info (MonoDwarfWriter *w, GSList *base_unwind_progra
                                           base_type_attr, G_N_ELEMENTS (base_type_attr));
        emit_dwarf_abbrev (w, ABBREV_STRUCT_TYPE, DW_TAG_class_type, TRUE, 
                                           struct_type_attr, G_N_ELEMENTS (struct_type_attr));
+       emit_dwarf_abbrev (w, ABBREV_STRUCT_TYPE_NOCHILDREN, DW_TAG_class_type, FALSE, 
+                                          struct_type_attr, G_N_ELEMENTS (struct_type_attr));
        emit_dwarf_abbrev (w, ABBREV_DATA_MEMBER, DW_TAG_member, FALSE, 
                                           data_member_attr, G_N_ELEMENTS (data_member_attr));
        emit_dwarf_abbrev (w, ABBREV_TYPEDEF, DW_TAG_typedef, FALSE, 
@@ -672,16 +724,19 @@ mono_dwarf_writer_emit_base_info (MonoDwarfWriter *w, GSList *base_unwind_progra
 
        emit_section_change (w, ".debug_info", 0);
        emit_label (w, ".Ldebug_info_start");
-       emit_symbol_diff (w, ".Ldebug_info_end", ".", -4); /* length */
+       emit_symbol_diff (w, ".Ldebug_info_end", ".Ldebug_info_begin", 0); /* length */
+       emit_label (w, ".Ldebug_info_begin");
        emit_int16 (w, 0x2); /* DWARF version 2 */
        emit_int32 (w, 0); /* .debug_abbrev offset */
        emit_byte (w, sizeof (gpointer)); /* address size */
 
-       /* Emit this into a separate section so it gets placed at the end */
-       emit_section_change (w, ".debug_info", 1);
-       emit_int32 (w, 0); /* close everything */
-       emit_label (w, ".Ldebug_info_end");
-       emit_section_change (w, ".debug_info", 0);
+       if (img_writer_subsections_supported (w->w) && w->appending) {
+               /* Emit this into a separate section so it gets placed at the end */
+               emit_section_change (w, ".debug_info", 1);
+               emit_byte (w, 0); /* close COMPILE_UNIT */
+               emit_label (w, ".Ldebug_info_end");
+               emit_section_change (w, ".debug_info", 0);
+       }
 
        /* Compilation unit */
        emit_uleb128 (w, ABBREV_COMPILE_UNIT);
@@ -707,16 +762,37 @@ mono_dwarf_writer_emit_base_info (MonoDwarfWriter *w, GSList *base_unwind_progra
                emit_string (w, basic_types [i].name);
        }
 
+       emit_debug_info_end (w);
+
        /* debug_loc section */
        emit_section_change (w, ".debug_loc", 0);
        emit_label (w, ".Ldebug_loc_start");
 
        /* debug_line section */
+       /*
+        * We emit some info even if emit_line is FALSE, as the
+        * apple linker seems to require a .debug_line section.
+        */
        emit_line_number_info_begin (w);
 
        emit_cie (w);
 }
 
+/*
+ * mono_dwarf_writer_close:
+ *
+ *   Finalize the emitted debugging info.
+ */
+void
+mono_dwarf_writer_close (MonoDwarfWriter *w)
+{
+       if (!w->appending) {
+               emit_section_change (w, ".debug_info", 0);
+               emit_byte (w, 0); /* close COMPILE_UNIT */
+               emit_label (w, ".Ldebug_info_end");
+       }
+}
+
 static const char* emit_type (MonoDwarfWriter *w, MonoType *t);
 
 /* Returns the local symbol pointing to the emitted debug info */
@@ -729,7 +805,7 @@ emit_class_dwarf_info (MonoDwarfWriter *w, MonoClass *klass, gboolean vtype)
        MonoClassField *field;
        const char *fdie;
        int k;
-       gboolean emit_namespace = FALSE;
+       gboolean emit_namespace = FALSE, has_children;
        GHashTable *cache;
 
        // FIXME: Appdomains
@@ -751,7 +827,7 @@ emit_class_dwarf_info (MonoDwarfWriter *w, MonoClass *klass, gboolean vtype)
        if (die)
                return die;
 
-       if (!((klass->byval_arg.type == MONO_TYPE_CLASS) || (klass->byval_arg.type == MONO_TYPE_OBJECT) || klass->enumtype || (klass->byval_arg.type == MONO_TYPE_VALUETYPE && vtype)))
+       if (!((klass->byval_arg.type == MONO_TYPE_CLASS) || (klass->byval_arg.type == MONO_TYPE_OBJECT) || klass->byval_arg.type == MONO_TYPE_GENERICINST || klass->enumtype || (klass->byval_arg.type == MONO_TYPE_VALUETYPE && vtype)))
                return NULL;
 
        /*
@@ -846,6 +922,8 @@ emit_class_dwarf_info (MonoDwarfWriter *w, MonoClass *klass, gboolean vtype)
                                g_assert_not_reached ();
                        }
                }
+
+               has_children = TRUE;
        } else {
                guint8 buf [128];
                guint8 *p;
@@ -865,9 +943,12 @@ emit_class_dwarf_info (MonoDwarfWriter *w, MonoClass *klass, gboolean vtype)
                        emit_type (w, field->type);
                }
 
+               iter = NULL;
+               has_children = parent_die || mono_class_get_fields (klass, &iter);
+
                emit_label (w, die);
 
-               emit_uleb128 (w, ABBREV_STRUCT_TYPE);
+               emit_uleb128 (w, has_children ? ABBREV_STRUCT_TYPE : ABBREV_STRUCT_TYPE_NOCHILDREN);
                emit_string (w, full_name);
                emit_uleb128 (w, klass->instance_size);
 
@@ -908,7 +989,8 @@ emit_class_dwarf_info (MonoDwarfWriter *w, MonoClass *klass, gboolean vtype)
        }
 
        /* Type end */
-       emit_uleb128 (w, 0x0);
+       if (has_children)
+               emit_uleb128 (w, 0x0);
 
        /* Add a typedef, so we can reference the type without a 'struct' in gdb */
        emit_uleb128 (w, ABBREV_TYPEDEF);
@@ -978,6 +1060,14 @@ emit_type (MonoDwarfWriter *w, MonoType *t)
                        else
                                tdie = ".LDIE_I4";
                        break;
+               case MONO_TYPE_GENERICINST:
+                       if (!MONO_TYPE_ISSTRUCT (t)) {
+                               emit_class_dwarf_info (w, klass, FALSE);
+                               tdie = g_hash_table_lookup (w->class_to_reference_die, klass);
+                       } else {
+                               tdie = ".LDIE_I4";
+                       }
+                       break;
                default:
                        tdie = ".LDIE_I4";
                        break;
@@ -1230,8 +1320,10 @@ compare_lne (MonoDebugLineNumberEntry *a, MonoDebugLineNumberEntry *b)
 }
 
 static void
-emit_line_number_info (MonoDwarfWriter *w, MonoMethod *method, guint8 *code,
-                                          guint32 code_size, MonoDebugMethodJitInfo *debug_info)
+emit_line_number_info (MonoDwarfWriter *w, MonoMethod *method, 
+                                          char *start_symbol, char *end_symbol,
+                                          guint8 *code, guint32 code_size,
+                                          MonoDebugMethodJitInfo *debug_info)
 {
        guint32 prev_line = 0;
        guint32 prev_native_offset = 0;
@@ -1244,14 +1336,15 @@ emit_line_number_info (MonoDwarfWriter *w, MonoMethod *method, guint8 *code,
        GArray *ln_array;
        int *native_to_il_offset = NULL;
 
-       if (!code)
-               // FIXME: The set_address op below only works with xdebug
+       if (!w->emit_line)
                return;
 
        minfo = mono_debug_lookup_method (method);
 
        /* Compute the native->IL offset mapping */
 
+       g_assert (code_size);
+
 #ifndef _EGLIB_MAJOR
        ln_array = g_array_sized_new (FALSE, FALSE, sizeof (MonoDebugLineNumberEntry), 
                                                                  debug_info->num_line_numbers);
@@ -1322,7 +1415,10 @@ emit_line_number_info (MonoDwarfWriter *w, MonoMethod *method, guint8 *code,
                                emit_byte (w, 0);
                                emit_byte (w, sizeof (gpointer) + 1);
                                emit_byte (w, DW_LNE_set_address);
-                               emit_pointer_value (w, code);
+                               if (start_symbol)
+                                       emit_pointer_unaligned (w, start_symbol);
+                               else
+                                       emit_pointer_value (w, code);
 
                                /* 
                                 * The prolog+initlocals region does not have a line number, this
@@ -1369,7 +1465,7 @@ emit_line_number_info (MonoDwarfWriter *w, MonoMethod *method, guint8 *code,
                emit_byte (w, 0);
                emit_byte (w, 1);
                emit_byte (w, DW_LNE_end_sequence);
-       } else if (code) {
+       } else if (!start_symbol) {
                /* No debug info, XDEBUG mode */
                char *name, *dis;
                const guint8 *ip = header->code;
@@ -1642,6 +1738,8 @@ mono_dwarf_writer_emit_method (MonoDwarfWriter *w, MonoCompile *cfg, MonoMethod
 
        emit_line (w);
 
+       emit_debug_info_end (w);
+
        /* Emit unwind info */
        if (unwind_info) {
                emit_fde (w, w->fde_index, start_symbol, end_symbol, code, code_size, unwind_info, TRUE);
@@ -1649,10 +1747,9 @@ mono_dwarf_writer_emit_method (MonoDwarfWriter *w, MonoCompile *cfg, MonoMethod
        }
 
        /* Emit line number info */
-       if (code && debug_info)
-               /* != could happen when using --regression */
-               if (debug_info->code_start == code)
-                       emit_line_number_info (w, method, code, code_size, debug_info);
+       /* != could happen when using --regression */
+       if (debug_info && (debug_info->code_start == code))
+               emit_line_number_info (w, method, start_symbol, end_symbol, code, code_size, debug_info);
 
        emit_line (w);
 }
@@ -1675,7 +1772,10 @@ mono_dwarf_writer_emit_trampoline (MonoDwarfWriter *w, const char *tramp_name, c
        /* Subprogram end */
        emit_uleb128 (w, 0x0);
 
+       emit_debug_info_end (w);
+
        /* Emit unwind info */
        emit_fde (w, w->fde_index, start_symbol, end_symbol, code, code_size, unwind_info, FALSE);
        w->fde_index ++;
 }
+#endif /* End of: !defined(DISABLE_AOT) && !defined(DISABLE_JIT) */