2 * debug-mono-symfile.c:
5 * Mono Project (http://www.mono-project.com)
7 * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
15 #ifdef HAVE_SYS_PARAM_H
16 #include <sys/param.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>
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
44 free_method_info (MonoDebugMethodInfo
*minfo
)
50 load_symfile (MonoDebugHandle
*handle
, MonoSymbolFile
*symfile
, gboolean in_the_debugger
)
52 const char *ptr
, *start
;
57 ptr
= start
= (const char*)symfile
->raw_contents
;
62 ptr
+= sizeof(guint64
);
63 if (magic
!= MONO_SYMBOL_FILE_MAGIC
) {
65 g_warning ("Symbol file %s is not a mono symbol file", symfile
->filename
);
70 ptr
+= sizeof(guint32
);
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
) {
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
);
87 guid
= mono_guid_to_string ((const guint8
*) ptr
);
90 if (strcmp (handle
->image
->guid
, guid
)) {
92 g_warning ("Symbol file %s doesn't match image %s", symfile
->filename
,
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
);
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
) {
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
;
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
));
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 ();
148 } else if (!in_the_debugger
) {
149 mono_debug_close_mono_symbol_file (symfile
);
150 mono_debugger_unlock ();
154 mono_debugger_unlock ();
159 mono_debug_close_mono_symbol_file (MonoSymbolFile
*symfile
)
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
);
172 mono_file_unmap ((gpointer
) symfile
->raw_contents
, symfile
->raw_contents_handle
);
175 if (symfile
->filename
)
176 g_free (symfile
->filename
);
178 mono_debugger_unlock ();
182 read_leb128 (const guint8
*ptr
, const guint8
**rptr
)
191 ret
= ret
| ((b
& 0x7f) << shift
);
193 } while ((b
& 0x80) == 0x80);
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
);
209 MonoSymbolFile
*symfile
;
210 int line_base
, line_range
, max_address_incr
;
212 guint32 last_line
, last_file
, last_offset
;
214 int line
, file
, offset
;
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
;
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
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
;
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
)
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;
305 stm
.is_hidden
= FALSE
;
308 guint8 opcode
= *ptr
++;
311 guint8 size
= *ptr
++;
312 const unsigned char *end_ptr
= ptr
+ size
;
316 if (opcode
== DW_LNE_end_sequence
) {
317 if (check_line (&stm
, -1, &location
))
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
326 g_warning ("Unknown extended opcode %x in LNT", opcode
);
331 } else if (opcode
< stm
.opcode_base
) {
334 if (check_line (&stm
, offset
, &location
))
337 case DW_LNS_advance_pc
:
338 stm
.offset
+= read_leb128 (ptr
, &ptr
);
340 case DW_LNS_advance_line
:
341 stm
.line
+= read_leb128 (ptr
, &ptr
);
343 case DW_LNS_set_file
:
344 stm
.file
= read_leb128 (ptr
, &ptr
);
346 case DW_LNS_const_add_pc
:
347 stm
.offset
+= stm
.max_address_incr
;
350 g_warning ("Unknown standard opcode %x in LNT", opcode
);
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
))
365 mono_debugger_unlock ();
369 mono_debugger_unlock ();
374 add_line (StatementMachine
*stm
, GPtrArray
*il_offset_array
, GPtrArray
*line_number_array
)
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
392 mono_debug_symfile_free_location (MonoDebugSourceLocation
*location
)
394 g_free (location
->source_file
);
399 * mono_debug_symfile_get_line_numbers:
401 * All the output parameters can be NULL.
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
;
411 GPtrArray
*il_offset_array
, *line_number_array
;
418 if ((symfile
= minfo
->handle
->symfile
) == NULL
)
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;
440 stm
.is_hidden
= FALSE
;
443 guint8 opcode
= *ptr
++;
446 guint8 size
= *ptr
++;
447 const unsigned char *end_ptr
= ptr
+ size
;
451 if (opcode
== DW_LNE_end_sequence
) {
452 add_line (&stm
, il_offset_array
, line_number_array
);
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
460 g_warning ("Unknown extended opcode %x in LNT", opcode
);
465 } else if (opcode
< stm
.opcode_base
) {
468 add_line (&stm
, il_offset_array
, line_number_array
);
470 case DW_LNS_advance_pc
:
471 stm
.offset
+= read_leb128 (ptr
, &ptr
);
473 case DW_LNS_advance_line
:
474 stm
.line
+= read_leb128 (ptr
, &ptr
);
476 case DW_LNS_set_file
:
477 stm
.file
= read_leb128 (ptr
, &ptr
);
479 case DW_LNS_const_add_pc
:
480 stm
.offset
+= stm
.max_address_incr
;
483 g_warning ("Unknown standard opcode %x in LNT", opcode
);
484 g_assert_not_reached ();
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
;
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
);
506 *source_file
= read_string (stm
.symfile
->raw_contents
+ read32(&(se
->_data_offset
)));
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 ();
527 _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo
*jit
, guint32 il_offset
)
531 if (!jit
|| !jit
->line_numbers
)
534 for (i
= jit
->num_line_numbers
- 1; i
>= 0; i
--) {
535 MonoDebugLineNumberEntry lne
= jit
->line_numbers
[i
];
537 if (lne
.il_offset
< 0)
539 if (lne
.il_offset
<= il_offset
)
540 return lne
.native_offset
;
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
)
565 if (handle
->image
!= mono_class_get_image (mono_method_get_class (method
)))
568 mono_debugger_lock ();
570 minfo
= g_hash_table_lookup (symfile
->method_hash
, method
);
572 mono_debugger_unlock ();
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
);
584 mono_debugger_unlock ();
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 ();
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 ().
610 mono_debug_symfile_lookup_locals (MonoDebugMethodInfo
*minfo
)
612 MonoSymbolFile
*symfile
= minfo
->handle
->symfile
;
614 int i
, len
, compile_unit_index
, locals_offset
, num_locals
, block_index
;
615 int namespace_id
, code_block_table_offset
;
616 MonoDebugLocalsInfo
*res
;
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';
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];
662 * mono_debug_symfile_free_locals:
664 * Free all the data allocated by mono_debug_symfile_lookup_locals ().
667 mono_debug_symfile_free_locals (MonoDebugLocalsInfo
*info
)
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
);