2010-06-21 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / debug-mono-symfile.c
blob8d3f6f1460909c3cd25a34198a29dd7449d5e6f6
1 /*
2 * debug-mono-symfile.c:
4 * Author:
5 * Mono Project (http://www.mono-project.com)
7 * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
8 */
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>
33 #include <fcntl.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
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
43 static void
44 free_method_info (MonoDebugMethodInfo *minfo)
46 g_free (minfo);
49 static int
50 load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_debugger)
52 const char *ptr, *start;
53 gchar *guid;
54 guint64 magic;
55 int minor, major;
57 ptr = start = (const char*)symfile->raw_contents;
58 if (!ptr)
59 return FALSE;
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;
69 major = read32(ptr);
70 ptr += sizeof(guint32);
71 minor = read32(ptr);
72 ptr += sizeof(guint32);
75 * 50.0 is the frozen version for Mono 2.0.
77 * Nobody except me (Martin) is allowed to check the minor version.
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;
87 guid = mono_guid_to_string ((const guint8 *) ptr);
88 ptr += 16;
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;
99 symfile->major_version = major;
100 symfile->minor_version = minor;
102 symfile->offset_table = (MonoSymbolFileOffsetTable *) ptr;
104 symfile->method_hash = g_hash_table_new_full (
105 g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_method_info);
107 g_free (guid);
108 return TRUE;
111 MonoSymbolFile *
112 mono_debug_open_mono_symbols (MonoDebugHandle *handle, const guint8 *raw_contents,
113 int size, gboolean in_the_debugger)
115 MonoSymbolFile *symfile;
117 mono_debugger_lock ();
118 symfile = g_new0 (MonoSymbolFile, 1);
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 symfile->was_loaded_from_memory = TRUE;
127 } else {
128 MonoFileMap *f;
129 symfile->filename = g_strdup_printf ("%s.mdb", mono_image_get_filename (handle->image));
130 symfile->was_loaded_from_memory = FALSE;
131 if ((f = mono_file_map_open (symfile->filename))) {
132 symfile->raw_contents_size = mono_file_map_size (f);
133 if (symfile->raw_contents_size == 0) {
134 if (!in_the_debugger)
135 g_warning ("stat of %s failed: %s",
136 symfile->filename, g_strerror (errno));
137 } else {
138 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);
141 mono_file_map_close (f);
145 if (load_symfile (handle, symfile, in_the_debugger)) {
146 mono_debugger_unlock ();
147 return symfile;
148 } else if (!in_the_debugger) {
149 mono_debug_close_mono_symbol_file (symfile);
150 mono_debugger_unlock ();
151 return NULL;
154 mono_debugger_unlock ();
155 return symfile;
158 void
159 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
161 if (!symfile)
162 return;
164 mono_debugger_lock ();
165 if (symfile->method_hash)
166 g_hash_table_destroy (symfile->method_hash);
168 if (symfile->raw_contents) {
169 if (symfile->was_loaded_from_memory)
170 g_free ((gpointer)symfile->raw_contents);
171 else
172 mono_file_unmap ((gpointer) symfile->raw_contents, symfile->raw_contents_handle);
175 if (symfile->filename)
176 g_free (symfile->filename);
177 g_free (symfile);
178 mono_debugger_unlock ();
181 static int
182 read_leb128 (const guint8 *ptr, const guint8 **rptr)
184 int ret = 0;
185 int shift = 0;
186 char b;
188 do {
189 b = *ptr++;
191 ret = ret | ((b & 0x7f) << shift);
192 shift += 7;
193 } while ((b & 0x80) == 0x80);
195 if (rptr)
196 *rptr = ptr;
198 return ret;
201 static gchar *
202 read_string (const guint8 *ptr)
204 int len = read_leb128 (ptr, &ptr);
205 return g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL);
208 typedef struct {
209 MonoSymbolFile *symfile;
210 int line_base, line_range, max_address_incr;
211 guint8 opcode_base;
212 guint32 last_line, last_file, last_offset;
213 guint32 first_file;
214 int line, file, offset;
215 gboolean is_hidden;
216 } StatementMachine;
218 static gboolean
219 check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **location)
221 gchar *source_file = NULL;
223 if (stm->offset <= offset) {
224 stm->last_offset = stm->offset;
225 stm->last_file = stm->file;
226 if (stm->line != 0xfeefee)
227 stm->last_line = stm->line;
228 return FALSE;
231 if (stm->last_file) {
232 int offset = read32(&(stm->symfile->offset_table->_source_table_offset)) +
233 (stm->last_file - 1) * sizeof (MonoSymbolFileSourceEntry);
234 MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
235 (stm->symfile->raw_contents + offset);
237 source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)));
240 if (stm->last_line == 0) {
242 * The IL offset is less than the first IL offset which has a corresponding
243 * source line.
245 *location = NULL;
246 return TRUE;
249 *location = g_new0 (MonoDebugSourceLocation, 1);
250 (*location)->source_file = source_file;
251 (*location)->row = stm->last_line;
252 (*location)->il_offset = stm->last_offset;
253 return TRUE;
257 * mono_debug_symfile_lookup_location:
258 * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
259 * mono_debug_lookup_method().
260 * @offset: IL offset within the corresponding method's CIL code.
262 * This function is similar to mono_debug_lookup_location(), but we
263 * already looked up the method and also already did the
264 * `native address -> IL offset' mapping.
266 MonoDebugSourceLocation *
267 mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
269 MonoDebugSourceLocation *location = NULL;
270 MonoSymbolFile *symfile;
271 const unsigned char *ptr;
272 StatementMachine stm;
274 #define DW_LNS_copy 1
275 #define DW_LNS_advance_pc 2
276 #define DW_LNS_advance_line 3
277 #define DW_LNS_set_file 4
278 #define DW_LNS_const_add_pc 8
280 #define DW_LNE_end_sequence 1
281 #define DW_LNE_MONO_negate_is_hidden 0x40
283 #define DW_LNE_MONO__extensions_start 0x40
284 #define DW_LNE_MONO__extensions_end 0x7f
286 if ((symfile = minfo->handle->symfile) == NULL)
287 return NULL;
289 stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base);
290 stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range);
291 stm.opcode_base = (guint8) read32 (&symfile->offset_table->_line_number_table_opcode_base);
292 stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
294 mono_debugger_lock ();
296 ptr = symfile->raw_contents + minfo->lnt_offset;
298 stm.symfile = symfile;
299 stm.offset = stm.last_offset = 0;
300 stm.last_file = 0;
301 stm.last_line = 0;
302 stm.first_file = 0;
303 stm.file = 1;
304 stm.line = 1;
305 stm.is_hidden = FALSE;
307 while (TRUE) {
308 guint8 opcode = *ptr++;
310 if (opcode == 0) {
311 guint8 size = *ptr++;
312 const unsigned char *end_ptr = ptr + size;
314 opcode = *ptr++;
316 if (opcode == DW_LNE_end_sequence) {
317 if (check_line (&stm, -1, &location))
318 goto out_success;
319 break;
320 } else if (opcode == DW_LNE_MONO_negate_is_hidden) {
321 stm.is_hidden = !stm.is_hidden;
322 } else if ((opcode >= DW_LNE_MONO__extensions_start) &&
323 (opcode <= DW_LNE_MONO__extensions_end)) {
324 ; // reserved for future extensions
325 } else {
326 g_warning ("Unknown extended opcode %x in LNT", opcode);
329 ptr = end_ptr;
330 continue;
331 } else if (opcode < stm.opcode_base) {
332 switch (opcode) {
333 case DW_LNS_copy:
334 if (check_line (&stm, offset, &location))
335 goto out_success;
336 break;
337 case DW_LNS_advance_pc:
338 stm.offset += read_leb128 (ptr, &ptr);
339 break;
340 case DW_LNS_advance_line:
341 stm.line += read_leb128 (ptr, &ptr);
342 break;
343 case DW_LNS_set_file:
344 stm.file = read_leb128 (ptr, &ptr);
345 break;
346 case DW_LNS_const_add_pc:
347 stm.offset += stm.max_address_incr;
348 break;
349 default:
350 g_warning ("Unknown standard opcode %x in LNT", opcode);
351 goto error_out;
353 } else {
354 opcode -= stm.opcode_base;
356 stm.offset += opcode / stm.line_range;
357 stm.line += stm.line_base + (opcode % stm.line_range);
359 if (check_line (&stm, offset, &location))
360 goto out_success;
364 error_out:
365 mono_debugger_unlock ();
366 return NULL;
368 out_success:
369 mono_debugger_unlock ();
370 return location;
373 static void
374 add_line (StatementMachine *stm, GPtrArray *il_offset_array, GPtrArray *line_number_array)
376 if (stm->line > 0) {
377 g_ptr_array_add (il_offset_array, GUINT_TO_POINTER (stm->offset));
378 g_ptr_array_add (line_number_array, GUINT_TO_POINTER (stm->line));
381 if (!stm->is_hidden && !stm->first_file)
382 stm->first_file = stm->file;
386 * mono_debug_symfile_free_location:
388 * Free a MonoDebugSourceLocation returned by
389 * mono_debug_symfile_lookup_location
391 void
392 mono_debug_symfile_free_location (MonoDebugSourceLocation *location)
394 g_free (location->source_file);
395 g_free (location);
399 * mono_debug_symfile_get_line_numbers:
401 * All the output parameters can be NULL.
403 void
404 mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_file, int *n_il_offsets, int **il_offsets, int **line_numbers)
406 // FIXME: Unify this with mono_debug_symfile_lookup_location
407 MonoSymbolFile *symfile;
408 const unsigned char *ptr;
409 StatementMachine stm;
410 guint32 i;
411 GPtrArray *il_offset_array, *line_number_array;
413 if (source_file)
414 *source_file = NULL;
415 if (n_il_offsets)
416 *n_il_offsets = 0;
418 if ((symfile = minfo->handle->symfile) == NULL)
419 return;
421 il_offset_array = g_ptr_array_new ();
422 line_number_array = g_ptr_array_new ();
424 stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base);
425 stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range);
426 stm.opcode_base = (guint8) read32 (&symfile->offset_table->_line_number_table_opcode_base);
427 stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
429 mono_debugger_lock ();
431 ptr = symfile->raw_contents + minfo->lnt_offset;
433 stm.symfile = symfile;
434 stm.offset = stm.last_offset = 0;
435 stm.last_file = 0;
436 stm.last_line = 0;
437 stm.first_file = 0;
438 stm.file = 1;
439 stm.line = 1;
440 stm.is_hidden = FALSE;
442 while (TRUE) {
443 guint8 opcode = *ptr++;
445 if (opcode == 0) {
446 guint8 size = *ptr++;
447 const unsigned char *end_ptr = ptr + size;
449 opcode = *ptr++;
451 if (opcode == DW_LNE_end_sequence) {
452 add_line (&stm, il_offset_array, line_number_array);
453 break;
454 } else if (opcode == DW_LNE_MONO_negate_is_hidden) {
455 stm.is_hidden = !stm.is_hidden;
456 } else if ((opcode >= DW_LNE_MONO__extensions_start) &&
457 (opcode <= DW_LNE_MONO__extensions_end)) {
458 ; // reserved for future extensions
459 } else {
460 g_warning ("Unknown extended opcode %x in LNT", opcode);
463 ptr = end_ptr;
464 continue;
465 } else if (opcode < stm.opcode_base) {
466 switch (opcode) {
467 case DW_LNS_copy:
468 add_line (&stm, il_offset_array, line_number_array);
469 break;
470 case DW_LNS_advance_pc:
471 stm.offset += read_leb128 (ptr, &ptr);
472 break;
473 case DW_LNS_advance_line:
474 stm.line += read_leb128 (ptr, &ptr);
475 break;
476 case DW_LNS_set_file:
477 stm.file = read_leb128 (ptr, &ptr);
478 break;
479 case DW_LNS_const_add_pc:
480 stm.offset += stm.max_address_incr;
481 break;
482 default:
483 g_warning ("Unknown standard opcode %x in LNT", opcode);
484 g_assert_not_reached ();
486 } else {
487 opcode -= stm.opcode_base;
489 stm.offset += opcode / stm.line_range;
490 stm.line += stm.line_base + (opcode % stm.line_range);
492 add_line (&stm, il_offset_array, line_number_array);
496 if (!stm.file && stm.first_file)
497 stm.file = stm.first_file;
499 if (stm.file) {
500 int offset = read32(&(stm.symfile->offset_table->_source_table_offset)) +
501 (stm.file - 1) * sizeof (MonoSymbolFileSourceEntry);
502 MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
503 (stm.symfile->raw_contents + offset);
505 if (source_file)
506 *source_file = read_string (stm.symfile->raw_contents + read32(&(se->_data_offset)));
509 if (n_il_offsets)
510 *n_il_offsets = il_offset_array->len;
511 if (il_offsets && line_numbers) {
512 *il_offsets = g_malloc (il_offset_array->len * sizeof (int));
513 *line_numbers = g_malloc (il_offset_array->len * sizeof (int));
514 for (i = 0; i < il_offset_array->len; ++i) {
515 (*il_offsets) [i] = GPOINTER_TO_UINT (g_ptr_array_index (il_offset_array, i));
516 (*line_numbers) [i] = GPOINTER_TO_UINT (g_ptr_array_index (line_number_array, i));
519 g_ptr_array_free (il_offset_array, TRUE);
520 g_ptr_array_free (line_number_array, TRUE);
522 mono_debugger_unlock ();
523 return;
526 gint32
527 _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offset)
529 int i;
531 if (!jit || !jit->line_numbers)
532 return -1;
534 for (i = jit->num_line_numbers - 1; i >= 0; i--) {
535 MonoDebugLineNumberEntry lne = jit->line_numbers [i];
537 if (lne.il_offset < 0)
538 continue;
539 if (lne.il_offset <= il_offset)
540 return lne.native_offset;
543 return 0;
546 static int
547 compare_method (const void *key, const void *object)
549 guint32 token = GPOINTER_TO_UINT (key);
550 MonoSymbolFileMethodEntry *me = (MonoSymbolFileMethodEntry*)object;
552 return token - read32(&(me->_token));
555 MonoDebugMethodInfo *
556 mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
558 MonoSymbolFileMethodEntry *first_ie, *ie;
559 MonoDebugMethodInfo *minfo;
560 MonoSymbolFile *symfile = handle->symfile;
562 if (!symfile->method_hash)
563 return NULL;
565 if (handle->image != mono_class_get_image (mono_method_get_class (method)))
566 return NULL;
568 mono_debugger_lock ();
570 minfo = g_hash_table_lookup (symfile->method_hash, method);
571 if (minfo) {
572 mono_debugger_unlock ();
573 return minfo;
576 first_ie = (MonoSymbolFileMethodEntry *)
577 (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset)));
579 ie = bsearch (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie,
580 read32(&(symfile->offset_table->_method_count)),
581 sizeof (MonoSymbolFileMethodEntry), compare_method);
583 if (!ie) {
584 mono_debugger_unlock ();
585 return NULL;
588 minfo = g_new0 (MonoDebugMethodInfo, 1);
589 minfo->index = (ie - first_ie) + 1;
590 minfo->method = method;
591 minfo->handle = handle;
593 minfo->data_offset = read32 (&(ie->_data_offset));
594 minfo->lnt_offset = read32 (&(ie->_line_number_table));
596 g_hash_table_insert (symfile->method_hash, method, minfo);
598 mono_debugger_unlock ();
599 return minfo;
603 * mono_debug_symfile_lookup_locals:
605 * Return information about the local variables of MINFO from the symbol file.
606 * Return NULL if no information can be found.
607 * The result should be freed using mono_debug_symfile_free_locals ().
609 MonoDebugLocalsInfo*
610 mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo)
612 MonoSymbolFile *symfile = minfo->handle->symfile;
613 const guint8 *p;
614 int i, len, compile_unit_index, locals_offset, num_locals, block_index;
615 int namespace_id, code_block_table_offset;
616 MonoDebugLocalsInfo *res;
618 if (!symfile)
619 return NULL;
621 p = symfile->raw_contents + minfo->data_offset;
623 compile_unit_index = read_leb128 (p, &p);
624 locals_offset = read_leb128 (p, &p);
625 namespace_id = read_leb128 (p, &p);
626 code_block_table_offset = read_leb128 (p, &p);
628 res = g_new0 (MonoDebugLocalsInfo, 1);
630 p = symfile->raw_contents + code_block_table_offset;
631 res->num_blocks = read_leb128 (p, &p);
632 res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
633 for (i = 0; i < res->num_blocks; ++i) {
634 res->code_blocks [i].type = read_leb128 (p, &p);
635 res->code_blocks [i].parent = read_leb128 (p, &p);
636 res->code_blocks [i].start_offset = read_leb128 (p, &p);
637 res->code_blocks [i].end_offset = read_leb128 (p, &p);
640 p = symfile->raw_contents + locals_offset;
641 num_locals = read_leb128 (p, &p);
643 res->num_locals = num_locals;
644 res->locals = g_new0 (MonoDebugLocalVar, num_locals);
646 for (i = 0; i < num_locals; ++i) {
647 res->locals [i].index = read_leb128 (p, &p);
648 len = read_leb128 (p, &p);
649 res->locals [i].name = g_malloc (len + 1);
650 memcpy (res->locals [i].name, p, len);
651 res->locals [i].name [len] = '\0';
652 p += len;
653 block_index = read_leb128 (p, &p);
654 if (block_index >= 1 && block_index <= res->num_blocks)
655 res->locals [i].block = &res->code_blocks [block_index - 1];
658 return res;
662 * mono_debug_symfile_free_locals:
664 * Free all the data allocated by mono_debug_symfile_lookup_locals ().
666 void
667 mono_debug_symfile_free_locals (MonoDebugLocalsInfo *info)
669 int i;
671 for (i = 0; i < info->num_locals; ++i)
672 g_free (info->locals [i].name);
673 g_free (info->locals);
674 g_free (info->code_blocks);
675 g_free (info);