2010-03-30 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / debug-mono-symfile.c
blob3ae35ac68d77308f0cc56f5e54756447995ccae6
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 } else {
127 MonoFileMap *f;
128 symfile->filename = g_strdup_printf ("%s.mdb", mono_image_get_filename (handle->image));
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);
140 mono_file_map_close (f);
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;
153 mono_debugger_unlock ();
154 return symfile;
157 void
158 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
160 if (!symfile)
161 return;
163 mono_debugger_lock ();
164 if (symfile->method_hash)
165 g_hash_table_destroy (symfile->method_hash);
167 if (symfile->raw_contents)
168 mono_file_unmap ((gpointer) symfile->raw_contents, symfile->raw_contents_handle);
170 if (symfile->filename)
171 g_free (symfile->filename);
172 g_free (symfile);
173 mono_debugger_unlock ();
176 static int
177 read_leb128 (const guint8 *ptr, const guint8 **rptr)
179 int ret = 0;
180 int shift = 0;
181 char b;
183 do {
184 b = *ptr++;
186 ret = ret | ((b & 0x7f) << shift);
187 shift += 7;
188 } while ((b & 0x80) == 0x80);
190 if (rptr)
191 *rptr = ptr;
193 return ret;
196 static gchar *
197 read_string (const guint8 *ptr)
199 int len = read_leb128 (ptr, &ptr);
200 return g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL);
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;
211 static gboolean
212 check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **location)
214 gchar *source_file = NULL;
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;
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);
230 source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)));
233 if (stm->last_line == 0) {
235 * The IL offset is less than the first IL offset which has a corresponding
236 * source line.
238 *location = NULL;
239 return TRUE;
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;
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.
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.
259 MonoDebugSourceLocation *
260 mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
262 MonoDebugSourceLocation *location = NULL;
263 MonoSymbolFile *symfile;
264 const unsigned char *ptr;
265 StatementMachine stm;
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
273 #define DW_LNE_end_sequence 1
274 #define DW_LNE_MONO_negate_is_hidden 0x40
276 #define DW_LNE_MONO__extensions_start 0x40
277 #define DW_LNE_MONO__extensions_end 0x7f
279 if ((symfile = minfo->handle->symfile) == NULL)
280 return NULL;
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;
287 mono_debugger_lock ();
289 ptr = symfile->raw_contents + minfo->lnt_offset;
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;
298 while (TRUE) {
299 guint8 opcode = *ptr++;
301 if (opcode == 0) {
302 guint8 size = *ptr++;
303 const unsigned char *end_ptr = ptr + size;
305 opcode = *ptr++;
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) {
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);
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;
344 } else {
345 opcode -= stm.opcode_base;
347 stm.offset += opcode / stm.line_range;
348 stm.line += stm.line_base + (opcode % stm.line_range);
350 if (check_line (&stm, offset, &location))
351 goto out_success;
355 error_out:
356 mono_debugger_unlock ();
357 return NULL;
359 out_success:
360 mono_debugger_unlock ();
361 return location;
364 static void
365 add_line (StatementMachine *stm, GPtrArray *il_offset_array, GPtrArray *line_number_array)
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));
374 * mono_debug_symfile_get_line_numbers:
376 * All the output parameters can be NULL.
378 void
379 mono_debug_symfile_get_line_numbers (MonoDebugMethodInfo *minfo, char **source_file, int *n_il_offsets, int **il_offsets, int **line_numbers)
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;
388 if (source_file)
389 *source_file = NULL;
390 if (n_il_offsets)
391 *n_il_offsets = 0;
393 if ((symfile = minfo->handle->symfile) == NULL)
394 return;
396 il_offset_array = g_ptr_array_new ();
397 line_number_array = g_ptr_array_new ();
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;
404 mono_debugger_lock ();
406 ptr = symfile->raw_contents + minfo->lnt_offset;
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;
415 while (TRUE) {
416 guint8 opcode = *ptr++;
418 if (opcode == 0) {
419 guint8 size = *ptr++;
420 const unsigned char *end_ptr = ptr + size;
422 opcode = *ptr++;
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) {
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);
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 ();
459 } else {
460 opcode -= stm.opcode_base;
462 stm.offset += opcode / stm.line_range;
463 stm.line += stm.line_base + (opcode % stm.line_range);
465 add_line (&stm, il_offset_array, line_number_array);
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);
475 if (source_file)
476 *source_file = read_string (stm.symfile->raw_contents + read32(&(se->_data_offset)));
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));
489 g_ptr_array_free (il_offset_array, TRUE);
490 g_ptr_array_free (line_number_array, TRUE);
492 mono_debugger_unlock ();
493 return;
496 gint32
497 _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offset)
499 int i;
501 if (!jit || !jit->line_numbers)
502 return -1;
504 for (i = jit->num_line_numbers - 1; i >= 0; i--) {
505 MonoDebugLineNumberEntry lne = jit->line_numbers [i];
507 if (lne.il_offset < 0)
508 continue;
509 if (lne.il_offset <= il_offset)
510 return lne.native_offset;
513 return 0;
516 static int
517 compare_method (const void *key, const void *object)
519 guint32 token = GPOINTER_TO_UINT (key);
520 MonoSymbolFileMethodEntry *me = (MonoSymbolFileMethodEntry*)object;
522 return token - read32(&(me->_token));
525 MonoDebugMethodInfo *
526 mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
528 MonoSymbolFileMethodEntry *first_ie, *ie;
529 MonoDebugMethodInfo *minfo;
530 MonoSymbolFile *symfile = handle->symfile;
532 if (!symfile->method_hash)
533 return NULL;
535 if (handle->image != mono_class_get_image (mono_method_get_class (method)))
536 return NULL;
538 mono_debugger_lock ();
540 minfo = g_hash_table_lookup (symfile->method_hash, method);
541 if (minfo) {
542 mono_debugger_unlock ();
543 return minfo;
546 first_ie = (MonoSymbolFileMethodEntry *)
547 (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset)));
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);
553 if (!ie) {
554 mono_debugger_unlock ();
555 return NULL;
558 minfo = g_new0 (MonoDebugMethodInfo, 1);
559 minfo->index = (ie - first_ie) + 1;
560 minfo->method = method;
561 minfo->handle = handle;
563 minfo->data_offset = read32 (&(ie->_data_offset));
564 minfo->lnt_offset = read32 (&(ie->_line_number_table));
566 g_hash_table_insert (symfile->method_hash, method, minfo);
568 mono_debugger_unlock ();
569 return minfo;
573 * mono_debug_symfile_lookup_locals:
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.
582 mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo, char ***names, int **indexes)
584 MonoSymbolFile *symfile = minfo->handle->symfile;
585 const guint8 *p;
586 int i, len, compile_unit_index, locals_offset, num_locals, index, block_index;
588 *names = NULL;
589 *indexes = NULL;
591 if (!symfile)
592 return -1;
594 p = symfile->raw_contents + minfo->data_offset;
596 compile_unit_index = read_leb128 (p, &p);
597 locals_offset = read_leb128 (p, &p);
599 p = symfile->raw_contents + locals_offset;
600 num_locals = read_leb128 (p, &p);
602 *names = g_new0 (char*, num_locals);
603 *indexes = g_new0 (int, num_locals);
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);
616 return num_locals;