2010-02-22 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / debug-mono-symfile.c
1 /*
2  * debug-mono-symfile.c: 
3  *
4  * Author:
5  *      Mono Project (http://www.mono-project.com)
6  *
7  * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
8  */
9
10 #include <config.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <errno.h>
14 #include <string.h>
15 #ifdef HAVE_SYS_PARAM_H
16 #include <sys/param.h>
17 #endif
18 #include <sys/stat.h>
19 #include <mono/metadata/metadata.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/tokentype.h>
22 #include <mono/metadata/appdomain.h>
23 #include <mono/metadata/exception.h>
24 #include <mono/metadata/debug-helpers.h>
25 #include <mono/metadata/mono-debug.h>
26 #include <mono/metadata/debug-mono-symfile.h>
27 #include <mono/metadata/mono-debug-debugger.h>
28 #include <mono/metadata/mono-endian.h>
29 #include <mono/metadata/metadata-internals.h>
30 #include <mono/metadata/class-internals.h>
31 #include <mono/utils/mono-mmap.h>
32
33 #include <fcntl.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #define RANGE_TABLE_CHUNK_SIZE          256
39 #define CLASS_TABLE_CHUNK_SIZE          256
40 #define TYPE_TABLE_PTR_CHUNK_SIZE       256
41 #define TYPE_TABLE_CHUNK_SIZE           65536
42
43 static void
44 free_method_info (MonoDebugMethodInfo *minfo)
45 {
46         g_free (minfo);
47 }
48
49 static int
50 load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_debugger)
51 {
52         const char *ptr, *start;
53         gchar *guid;
54         guint64 magic;
55         int minor, major;
56
57         ptr = start = (const char*)symfile->raw_contents;
58         if (!ptr)
59                 return FALSE;
60
61         magic = read64(ptr);
62         ptr += sizeof(guint64);
63         if (magic != MONO_SYMBOL_FILE_MAGIC) {
64                 if (!in_the_debugger)
65                         g_warning ("Symbol file %s is not a mono symbol file", symfile->filename);
66                 return FALSE;
67         }
68
69         major = read32(ptr);
70         ptr += sizeof(guint32);
71         minor = read32(ptr);
72         ptr += sizeof(guint32);
73
74         /*
75          * 50.0 is the frozen version for Mono 2.0.
76          *
77          * Nobody except me (Martin) is allowed to check the minor version.
78          */
79         if (major != MONO_SYMBOL_FILE_MAJOR_VERSION) {
80                 if (!in_the_debugger)
81                         g_warning ("Symbol file %s has incorrect version (expected %d.%d, got %d)",
82                                    symfile->filename, MONO_SYMBOL_FILE_MAJOR_VERSION,
83                                    MONO_SYMBOL_FILE_MINOR_VERSION, major);
84                 return FALSE;
85         }
86
87         guid = mono_guid_to_string ((const guint8 *) ptr);
88         ptr += 16;
89
90         if (strcmp (handle->image->guid, guid)) {
91                 if (!in_the_debugger)
92                         g_warning ("Symbol file %s doesn't match image %s", symfile->filename,
93                                    handle->image_file);
94                 if (guid)
95                         g_free (guid);
96                 return FALSE;
97         }
98
99         symfile->major_version = major;
100         symfile->minor_version = minor;
101
102         symfile->offset_table = (MonoSymbolFileOffsetTable *) ptr;
103
104         symfile->method_hash = g_hash_table_new_full (
105                 g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_method_info);
106
107         g_free (guid);
108         return TRUE;
109 }
110
111 MonoSymbolFile *
112 mono_debug_open_mono_symbols (MonoDebugHandle *handle, const guint8 *raw_contents,
113                               int size, gboolean in_the_debugger)
114 {
115         MonoSymbolFile *symfile;
116
117         mono_debugger_lock ();
118         symfile = g_new0 (MonoSymbolFile, 1);
119
120         if (raw_contents != NULL) {
121                 unsigned char *p;
122                 symfile->raw_contents_size = size;
123                 symfile->raw_contents = p = g_malloc (size);
124                 memcpy (p, raw_contents, size);
125                 symfile->filename = g_strdup_printf ("LoadedFromMemory");
126         } else {
127                 MonoFileMap *f;
128                 symfile->filename = g_strdup_printf ("%s.mdb", mono_image_get_filename (handle->image));
129
130                 if ((f = mono_file_map_open (symfile->filename))) {
131                         symfile->raw_contents_size = mono_file_map_size (f);
132                         if (symfile->raw_contents_size == 0) {
133                                 if (!in_the_debugger)
134                                         g_warning ("stat of %s failed: %s",
135                                                    symfile->filename,  g_strerror (errno));
136                         } else {
137                                 symfile->raw_contents = mono_file_map (symfile->raw_contents_size, MONO_MMAP_READ|MONO_MMAP_PRIVATE, mono_file_map_fd (f), 0, &symfile->raw_contents_handle);
138                         }
139
140                         mono_file_map_close (f);
141                 }
142         }
143         
144         if (load_symfile (handle, symfile, in_the_debugger)) {
145                 mono_debugger_unlock ();
146                 return symfile;
147         } else if (!in_the_debugger) {
148                 mono_debug_close_mono_symbol_file (symfile);
149                 mono_debugger_unlock ();
150                 return NULL;
151         }
152
153         mono_debugger_unlock ();
154         return symfile;
155 }
156
157 void
158 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
159 {
160         if (!symfile)
161                 return;
162
163         mono_debugger_lock ();
164         if (symfile->method_hash)
165                 g_hash_table_destroy (symfile->method_hash);
166
167         if (symfile->raw_contents)
168                 mono_file_unmap ((gpointer) symfile->raw_contents, symfile->raw_contents_handle);
169
170         if (symfile->filename)
171                 g_free (symfile->filename);
172         g_free (symfile);
173         mono_debugger_unlock ();
174 }
175
176 static int
177 read_leb128 (const guint8 *ptr, const guint8 **rptr)
178 {
179         int ret = 0;
180         int shift = 0;
181         char b;
182
183         do {
184                 b = *ptr++;
185                                 
186                 ret = ret | ((b & 0x7f) << shift);
187                 shift += 7;
188         } while ((b & 0x80) == 0x80);
189
190         if (rptr)
191                 *rptr = ptr;
192
193         return ret;
194 }
195
196 static gchar *
197 read_string (const guint8 *ptr)
198 {
199         int len = read_leb128 (ptr, &ptr);
200         return g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL);
201 }
202
203 typedef struct {
204         MonoSymbolFile *symfile;
205         int line_base, line_range, max_address_incr;
206         guint8 opcode_base;
207         guint32 last_line, last_file, last_offset;
208         int line, file, offset;
209 } StatementMachine;
210
211 static gboolean
212 check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **location)
213 {
214         gchar *source_file = NULL;
215
216         if (stm->offset <= offset) {
217                 stm->last_offset = stm->offset;
218                 stm->last_file = stm->file;
219                 if (stm->line != 0xfeefee)
220                         stm->last_line = stm->line;
221                 return FALSE;
222         }
223
224         if (stm->last_file) {
225                 int offset = read32(&(stm->symfile->offset_table->_source_table_offset)) +
226                         (stm->last_file - 1) * sizeof (MonoSymbolFileSourceEntry);
227                 MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
228                         (stm->symfile->raw_contents + offset);
229
230                 source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)));
231         }
232
233         if (stm->last_line == 0) {
234                 /* 
235                  * The IL offset is less than the first IL offset which has a corresponding
236                  * source line.
237                  */
238                 *location = NULL;
239                 return TRUE;
240         }
241
242         *location = g_new0 (MonoDebugSourceLocation, 1);
243         (*location)->source_file = source_file;
244         (*location)->row = stm->last_line;
245         (*location)->il_offset = stm->last_offset;
246         return TRUE;
247 }
248
249 /**
250  * mono_debug_symfile_lookup_location:
251  * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
252  *         mono_debug_lookup_method().
253  * @offset: IL offset within the corresponding method's CIL code.
254  *
255  * This function is similar to mono_debug_lookup_location(), but we
256  * already looked up the method and also already did the
257  * `native address -> IL offset' mapping.
258  */
259 MonoDebugSourceLocation *
260 mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
261 {
262         MonoDebugSourceLocation *location = NULL;
263         MonoSymbolFile *symfile;
264         const unsigned char *ptr;
265         StatementMachine stm;
266
267 #define DW_LNS_copy 1
268 #define DW_LNS_advance_pc 2
269 #define DW_LNS_advance_line 3
270 #define DW_LNS_set_file 4
271 #define DW_LNS_const_add_pc 8
272
273 #define DW_LNE_end_sequence 1
274 #define DW_LNE_MONO_negate_is_hidden 0x40
275
276 #define DW_LNE_MONO__extensions_start 0x40
277 #define DW_LNE_MONO__extensions_end 0x7f
278
279         if ((symfile = minfo->handle->symfile) == NULL)
280                 return NULL;
281
282         stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base);
283         stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range);
284         stm.opcode_base = (guint8) read32 (&symfile->offset_table->_line_number_table_opcode_base);
285         stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
286
287         mono_debugger_lock ();
288
289         ptr = symfile->raw_contents + minfo->lnt_offset;
290
291         stm.symfile = symfile;
292         stm.offset = stm.last_offset = 0;
293         stm.last_file = 0;
294         stm.last_line = 0;
295         stm.file = 1;
296         stm.line = 1;
297
298         while (TRUE) {
299                 guint8 opcode = *ptr++;
300
301                 if (opcode == 0) {
302                         guint8 size = *ptr++;
303                         const unsigned char *end_ptr = ptr + size;
304
305                         opcode = *ptr++;
306
307                         if (opcode == DW_LNE_end_sequence) {
308                                 if (check_line (&stm, -1, &location))
309                                         goto out_success;
310                                 break;
311                         } else if (opcode == DW_LNE_MONO_negate_is_hidden) {
312                                 ;
313                         } else if ((opcode >= DW_LNE_MONO__extensions_start) &&
314                                    (opcode <= DW_LNE_MONO__extensions_end)) {
315                                 ; // reserved for future extensions
316                         } else {
317                                 g_warning ("Unknown extended opcode %x in LNT", opcode);
318                         }
319
320                         ptr = end_ptr;
321                         continue;
322                 } else if (opcode < stm.opcode_base) {
323                         switch (opcode) {
324                         case DW_LNS_copy:
325                                 if (check_line (&stm, offset, &location))
326                                         goto out_success;
327                                 break;
328                         case DW_LNS_advance_pc:
329                                 stm.offset += read_leb128 (ptr, &ptr);
330                                 break;
331                         case DW_LNS_advance_line:
332                                 stm.line += read_leb128 (ptr, &ptr);
333                                 break;
334                         case DW_LNS_set_file:
335                                 stm.file = read_leb128 (ptr, &ptr);
336                                 break;
337                         case DW_LNS_const_add_pc:
338                                 stm.offset += stm.max_address_incr;
339                                 break;
340                         default:
341                                 g_warning ("Unknown standard opcode %x in LNT", opcode);
342                                 goto error_out;
343                         }
344                 } else {
345                         opcode -= stm.opcode_base;
346
347                         stm.offset += opcode / stm.line_range;
348                         stm.line += stm.line_base + (opcode % stm.line_range);
349
350                         if (check_line (&stm, offset, &location))
351                                 goto out_success;
352                 }
353         }
354
355  error_out:
356         mono_debugger_unlock ();
357         return NULL;
358
359  out_success:
360         mono_debugger_unlock ();
361         return location;
362 }
363
364 static void
365 add_line (StatementMachine *stm, GPtrArray *il_offset_array, GPtrArray *line_number_array)
366 {
367         if (stm->line > 0) {
368                 g_ptr_array_add (il_offset_array, GUINT_TO_POINTER (stm->offset));
369                 g_ptr_array_add (line_number_array, GUINT_TO_POINTER (stm->line));
370         }
371 }
372
373 /*
374  * mono_debug_symfile_get_line_numbers:
375  *
376  *   All the output parameters can be NULL.
377  */ 
378 void
379 mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_file, int *n_il_offsets, int **il_offsets, int **line_numbers)
380 {
381         // FIXME: Unify this with mono_debug_symfile_lookup_location
382         MonoSymbolFile *symfile;
383         const unsigned char *ptr;
384         StatementMachine stm;
385         guint32 i;
386         GPtrArray *il_offset_array, *line_number_array;
387
388         if (source_file)
389                 *source_file = NULL;
390         if (n_il_offsets)
391                 *n_il_offsets = 0;
392
393         if ((symfile = minfo->handle->symfile) == NULL)
394                 return;
395
396         il_offset_array = g_ptr_array_new ();
397         line_number_array = g_ptr_array_new ();
398
399         stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base);
400         stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range);
401         stm.opcode_base = (guint8) read32 (&symfile->offset_table->_line_number_table_opcode_base);
402         stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
403
404         mono_debugger_lock ();
405
406         ptr = symfile->raw_contents + minfo->lnt_offset;
407
408         stm.symfile = symfile;
409         stm.offset = stm.last_offset = 0;
410         stm.last_file = 0;
411         stm.last_line = 0;
412         stm.file = 1;
413         stm.line = 1;
414
415         while (TRUE) {
416                 guint8 opcode = *ptr++;
417
418                 if (opcode == 0) {
419                         guint8 size = *ptr++;
420                         const unsigned char *end_ptr = ptr + size;
421
422                         opcode = *ptr++;
423
424                         if (opcode == DW_LNE_end_sequence) {
425                                 add_line (&stm, il_offset_array, line_number_array);
426                                 break;
427                         } else if (opcode == DW_LNE_MONO_negate_is_hidden) {
428                                 ;
429                         } else if ((opcode >= DW_LNE_MONO__extensions_start) &&
430                                    (opcode <= DW_LNE_MONO__extensions_end)) {
431                                 ; // reserved for future extensions
432                         } else {
433                                 g_warning ("Unknown extended opcode %x in LNT", opcode);
434                         }
435
436                         ptr = end_ptr;
437                         continue;
438                 } else if (opcode < stm.opcode_base) {
439                         switch (opcode) {
440                         case DW_LNS_copy:
441                                 add_line (&stm, il_offset_array, line_number_array);
442                                 break;
443                         case DW_LNS_advance_pc:
444                                 stm.offset += read_leb128 (ptr, &ptr);
445                                 break;
446                         case DW_LNS_advance_line:
447                                 stm.line += read_leb128 (ptr, &ptr);
448                                 break;
449                         case DW_LNS_set_file:
450                                 stm.file = read_leb128 (ptr, &ptr);
451                                 break;
452                         case DW_LNS_const_add_pc:
453                                 stm.offset += stm.max_address_incr;
454                                 break;
455                         default:
456                                 g_warning ("Unknown standard opcode %x in LNT", opcode);
457                                 g_assert_not_reached ();
458                         }
459                 } else {
460                         opcode -= stm.opcode_base;
461
462                         stm.offset += opcode / stm.line_range;
463                         stm.line += stm.line_base + (opcode % stm.line_range);
464
465                         add_line (&stm, il_offset_array, line_number_array);
466                 }
467         }
468
469         if (stm.file) {
470                 int offset = read32(&(stm.symfile->offset_table->_source_table_offset)) +
471                         (stm.file - 1) * sizeof (MonoSymbolFileSourceEntry);
472                 MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
473                         (stm.symfile->raw_contents + offset);
474
475                 if (source_file)
476                         *source_file = read_string (stm.symfile->raw_contents + read32(&(se->_data_offset)));
477         }
478
479         if (n_il_offsets)
480                 *n_il_offsets = il_offset_array->len;
481         if (il_offsets && line_numbers) {
482                 *il_offsets = g_malloc (il_offset_array->len * sizeof (int));
483                 *line_numbers = g_malloc (il_offset_array->len * sizeof (int));
484                 for (i = 0; i < il_offset_array->len; ++i) {
485                         (*il_offsets) [i] = GPOINTER_TO_UINT (g_ptr_array_index (il_offset_array, i));
486                         (*line_numbers) [i] = GPOINTER_TO_UINT (g_ptr_array_index (line_number_array, i));
487                 }
488         }
489         g_ptr_array_free (il_offset_array, TRUE);
490         g_ptr_array_free (line_number_array, TRUE);
491
492         mono_debugger_unlock ();
493         return;
494 }
495
496 gint32
497 _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offset)
498 {
499         int i;
500
501         if (!jit || !jit->line_numbers)
502                 return -1;
503
504         for (i = jit->num_line_numbers - 1; i >= 0; i--) {
505                 MonoDebugLineNumberEntry lne = jit->line_numbers [i];
506
507                 if (lne.il_offset < 0)
508                         continue;
509                 if (lne.il_offset <= il_offset)
510                         return lne.native_offset;
511         }
512
513         return 0;
514 }
515
516 static int
517 compare_method (const void *key, const void *object)
518 {
519         guint32 token = GPOINTER_TO_UINT (key);
520         MonoSymbolFileMethodEntry *me = (MonoSymbolFileMethodEntry*)object;
521
522         return token - read32(&(me->_token));
523 }
524
525 MonoDebugMethodInfo *
526 mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
527 {
528         MonoSymbolFileMethodEntry *first_ie, *ie;
529         MonoDebugMethodInfo *minfo;
530         MonoSymbolFile *symfile = handle->symfile;
531
532         if (!symfile->method_hash)
533                 return NULL;
534
535         if (handle->image != mono_class_get_image (mono_method_get_class (method)))
536                 return NULL;
537
538         mono_debugger_lock ();
539
540         minfo = g_hash_table_lookup (symfile->method_hash, method);
541         if (minfo) {
542                 mono_debugger_unlock ();
543                 return minfo;
544         }
545
546         first_ie = (MonoSymbolFileMethodEntry *)
547                 (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset)));
548
549         ie = bsearch (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie,
550                                    read32(&(symfile->offset_table->_method_count)),
551                                    sizeof (MonoSymbolFileMethodEntry), compare_method);
552
553         if (!ie) {
554                 mono_debugger_unlock ();
555                 return NULL;
556         }
557
558         minfo = g_new0 (MonoDebugMethodInfo, 1);
559         minfo->index = (ie - first_ie) + 1;
560         minfo->method = method;
561         minfo->handle = handle;
562
563         minfo->data_offset = read32 (&(ie->_data_offset));
564         minfo->lnt_offset = read32 (&(ie->_line_number_table));
565
566         g_hash_table_insert (symfile->method_hash, method, minfo);
567
568         mono_debugger_unlock ();
569         return minfo;
570 }
571
572 /*
573  * mono_debug_symfile_lookup_locals:
574  *
575  *   Return information about the local variables of MINFO from the symbol file.
576  * NAMES and INDEXES are set to g_malloc-ed arrays containing the local names and
577  * their IL indexes.
578  * Returns: the number of elements placed into the arrays, or -1 if there is no
579  * local variable info.
580  */
581 int
582 mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo, char ***names, int **indexes)
583 {
584         MonoSymbolFile *symfile = minfo->handle->symfile;
585         const guint8 *p;
586         int i, len, compile_unit_index, locals_offset, num_locals, index, block_index;
587
588         *names = NULL;
589         *indexes = NULL;
590
591         if (!symfile)
592                 return -1;
593
594         p = symfile->raw_contents + minfo->data_offset;
595
596         compile_unit_index = read_leb128 (p, &p);
597         locals_offset = read_leb128 (p, &p);
598
599         p = symfile->raw_contents + locals_offset;
600         num_locals = read_leb128 (p, &p);
601
602         *names = g_new0 (char*, num_locals);
603         *indexes = g_new0 (int, num_locals);
604
605         for (i = 0; i < num_locals; ++i) {
606                 index = read_leb128 (p, &p);
607                 (*indexes) [i] = index;
608                 len = read_leb128 (p, &p);
609                 (*names) [i] = g_malloc (len + 1);
610                 memcpy ((*names) [i], p, len);
611                 (*names) [i][len] = '\0';
612                 p += len;
613                 block_index = read_leb128 (p, &p);
614         }
615
616         return num_locals;
617 }
618