In .:
[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 #include <signal.h>
16 #ifdef HAVE_SYS_PARAM_H
17 #include <sys/param.h>
18 #endif
19 #include <sys/stat.h>
20 #include <mono/metadata/metadata.h>
21 #include <mono/metadata/tabledefs.h>
22 #include <mono/metadata/tokentype.h>
23 #include <mono/metadata/appdomain.h>
24 #include <mono/metadata/exception.h>
25 #include <mono/metadata/debug-helpers.h>
26 #include <mono/metadata/mono-debug.h>
27 #include <mono/metadata/debug-mono-symfile.h>
28 #include <mono/metadata/mono-debug-debugger.h>
29 #include <mono/metadata/mono-endian.h>
30 #include <mono/metadata/metadata-internals.h>
31 #include <mono/metadata/class-internals.h>
32 #include <mono/utils/mono-mmap.h>
33
34 #include <fcntl.h>
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 #define RANGE_TABLE_CHUNK_SIZE          256
40 #define CLASS_TABLE_CHUNK_SIZE          256
41 #define TYPE_TABLE_PTR_CHUNK_SIZE       256
42 #define TYPE_TABLE_CHUNK_SIZE           65536
43
44 static void
45 free_method_info (MonoDebugMethodInfo *minfo)
46 {
47         g_free (minfo);
48 }
49
50 static int
51 load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_debugger)
52 {
53         const char *ptr, *start;
54         gchar *guid;
55         guint64 magic;
56         int minor, major;
57
58         ptr = start = (const char*)symfile->raw_contents;
59         if (!ptr)
60                 return FALSE;
61
62         magic = read64(ptr);
63         ptr += sizeof(guint64);
64         if (magic != MONO_SYMBOL_FILE_MAGIC) {
65                 if (!in_the_debugger)
66                         g_warning ("Symbol file %s is not a mono symbol file", symfile->filename);
67                 return FALSE;
68         }
69
70         major = read32(ptr);
71         ptr += sizeof(guint32);
72         minor = read32(ptr);
73         ptr += sizeof(guint32);
74
75         /*
76          * 50.0 is the frozen version for Mono 2.0.
77          *
78          * Nobody except me (Martin) is allowed to check the minor version.
79          */
80         if (major != MONO_SYMBOL_FILE_MAJOR_VERSION) {
81                 if (!in_the_debugger)
82                         g_warning ("Symbol file %s has incorrect version (expected %d.%d, got %d)",
83                                    symfile->filename, MONO_SYMBOL_FILE_MAJOR_VERSION,
84                                    MONO_SYMBOL_FILE_MINOR_VERSION, major);
85                 return FALSE;
86         }
87
88         guid = mono_guid_to_string ((const guint8 *) ptr);
89         ptr += 16;
90
91         if (strcmp (handle->image->guid, guid)) {
92                 if (!in_the_debugger)
93                         g_warning ("Symbol file %s doesn't match image %s", symfile->filename,
94                                    handle->image_file);
95                 if (guid)
96                         g_free (guid);
97                 return FALSE;
98         }
99
100         symfile->major_version = major;
101         symfile->minor_version = minor;
102
103         symfile->offset_table = (MonoSymbolFileOffsetTable *) ptr;
104
105         symfile->method_hash = g_hash_table_new_full (
106                 g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_method_info);
107
108         g_free (guid);
109         return TRUE;
110 }
111
112 MonoSymbolFile *
113 mono_debug_open_mono_symbols (MonoDebugHandle *handle, const guint8 *raw_contents,
114                               int size, gboolean in_the_debugger)
115 {
116         MonoSymbolFile *symfile;
117
118         mono_debugger_lock ();
119         symfile = g_new0 (MonoSymbolFile, 1);
120
121         if (raw_contents != NULL) {
122                 unsigned char *p;
123                 symfile->raw_contents_size = size;
124                 symfile->raw_contents = p = g_malloc (size);
125                 memcpy (p, raw_contents, size);
126                 symfile->filename = g_strdup_printf ("LoadedFromMemory");
127         } else {
128                 MonoFileMap *f;
129                 symfile->filename = g_strdup_printf ("%s.mdb", mono_image_get_filename (handle->image));
130
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);
139                         }
140
141                         mono_file_map_close (f);
142                 }
143         }
144         
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;
152         }
153
154         mono_debugger_unlock ();
155         return symfile;
156 }
157
158 void
159 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
160 {
161         if (!symfile)
162                 return;
163
164         mono_debugger_lock ();
165         if (symfile->method_hash)
166                 g_hash_table_destroy (symfile->method_hash);
167
168         if (symfile->raw_contents)
169                 mono_file_unmap ((gpointer) symfile->raw_contents, symfile->raw_contents_handle);
170
171         if (symfile->filename)
172                 g_free (symfile->filename);
173         g_free (symfile);
174         mono_debugger_unlock ();
175 }
176
177 static int
178 read_leb128 (const guint8 *ptr, const guint8 **rptr)
179 {
180         int ret = 0;
181         int shift = 0;
182         char b;
183
184         do {
185                 b = *ptr++;
186                                 
187                 ret = ret | ((b & 0x7f) << shift);
188                 shift += 7;
189         } while ((b & 0x80) == 0x80);
190
191         if (rptr)
192                 *rptr = ptr;
193
194         return ret;
195 }
196
197 static gchar *
198 read_string (const guint8 *ptr)
199 {
200         int len = read_leb128 (ptr, &ptr);
201         return g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL);
202 }
203
204 typedef struct {
205         MonoSymbolFile *symfile;
206         int line_base, line_range, max_address_incr;
207         guint8 opcode_base;
208         guint32 last_line, last_file, last_offset;
209         guint32 line, file, offset;
210 } StatementMachine;
211
212 static gboolean
213 check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **location)
214 {
215         gchar *source_file = NULL;
216
217         if ((offset > 0) && (stm->offset <= offset)) {
218                 stm->last_offset = stm->offset;
219                 stm->last_file = stm->file;
220                 if (stm->line != 0xfeefee)
221                         stm->last_line = stm->line;
222                 return FALSE;
223         }
224
225         if (stm->last_file) {
226                 int offset = read32(&(stm->symfile->offset_table->_source_table_offset)) +
227                         (stm->last_file - 1) * sizeof (MonoSymbolFileSourceEntry);
228                 MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
229                         (stm->symfile->raw_contents + offset);
230
231                 source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)));
232         }
233
234         if (stm->last_line == 0) {
235                 /* 
236                  * The IL offset is less than the first IL offset which has a corresponding
237                  * source line.
238                  */
239                 *location = NULL;
240                 return TRUE;
241         }
242
243         *location = g_new0 (MonoDebugSourceLocation, 1);
244         (*location)->source_file = source_file;
245         (*location)->row = stm->last_line;
246         (*location)->il_offset = stm->last_offset;
247         return TRUE;
248 }
249
250 /**
251  * mono_debug_symfile_lookup_location:
252  * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
253  *         mono_debug_lookup_method().
254  * @offset: IL offset within the corresponding method's CIL code.
255  *
256  * This function is similar to mono_debug_lookup_location(), but we
257  * already looked up the method and also already did the
258  * `native address -> IL offset' mapping.
259  */
260 MonoDebugSourceLocation *
261 mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
262 {
263         MonoDebugSourceLocation *location = NULL;
264         MonoSymbolFile *symfile;
265         const unsigned char *ptr;
266         StatementMachine stm;
267
268 #define DW_LNS_copy 1
269 #define DW_LNS_advance_pc 2
270 #define DW_LNS_advance_line 3
271 #define DW_LNS_set_file 4
272 #define DW_LNS_const_add_pc 8
273
274 #define DW_LNE_end_sequence 1
275 #define DW_LNE_MONO_negate_is_hidden 0x40
276
277         if ((symfile = minfo->handle->symfile) == NULL)
278                 return NULL;
279
280         stm.line_base = read32 (&symfile->offset_table->_line_number_table_line_base);
281         stm.line_range = read32 (&symfile->offset_table->_line_number_table_line_range);
282         stm.opcode_base = (guint8) read32 (&symfile->offset_table->_line_number_table_opcode_base);
283         stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
284
285         mono_debugger_lock ();
286
287         ptr = symfile->raw_contents + minfo->lnt_offset;
288
289         stm.symfile = symfile;
290         stm.offset = stm.last_offset = 0;
291         stm.last_file = 0;
292         stm.last_line = 0;
293         stm.file = 1;
294         stm.line = 1;
295
296         while (TRUE) {
297                 guint8 opcode = *ptr++;
298
299                 if (opcode == 0) {
300                         guint8 size = *ptr++;
301                         const unsigned char *end_ptr = ptr + size;
302
303                         opcode = *ptr++;
304
305                         if (opcode == DW_LNE_end_sequence) {
306                                 if (check_line (&stm, -1, &location))
307                                         goto out_success;
308                                 break;
309                         } else if (opcode == DW_LNE_MONO_negate_is_hidden) {
310                                 ;
311                         } else {
312                                 g_warning ("Unknown extended opcode %x in LNT", opcode);
313                         }
314
315                         ptr = end_ptr;
316                         continue;
317                 } else if (opcode < stm.opcode_base) {
318                         switch (opcode) {
319                         case DW_LNS_copy:
320                                 if (check_line (&stm, offset, &location))
321                                         goto out_success;
322                                 break;
323                         case DW_LNS_advance_pc:
324                                 stm.offset += read_leb128 (ptr, &ptr);
325                                 break;
326                         case DW_LNS_advance_line:
327                                 stm.line += read_leb128 (ptr, &ptr);
328                                 break;
329                         case DW_LNS_set_file:
330                                 stm.file = read_leb128 (ptr, &ptr);
331                                 break;
332                         case DW_LNS_const_add_pc:
333                                 stm.offset += stm.max_address_incr;
334                                 break;
335                         default:
336                                 g_warning ("Unknown standard opcode %x in LNT", opcode);
337                                 goto error_out;
338                         }
339                 } else {
340                         opcode -= stm.opcode_base;
341
342                         stm.offset += opcode / stm.line_range;
343                         stm.line += stm.line_base + (opcode % stm.line_range);
344
345                         if (check_line (&stm, offset, &location))
346                                 goto out_success;
347                 }
348         }
349
350  error_out:
351         mono_debugger_unlock ();
352         return NULL;
353
354  out_success:
355         mono_debugger_unlock ();
356         return location;
357 }
358
359 gint32
360 _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offset)
361 {
362         int i;
363
364         if (!jit || !jit->line_numbers)
365                 return -1;
366
367         for (i = jit->num_line_numbers - 1; i >= 0; i--) {
368                 MonoDebugLineNumberEntry lne = jit->line_numbers [i];
369
370                 if (lne.il_offset < 0)
371                         continue;
372                 if (lne.il_offset <= il_offset)
373                         return lne.native_offset;
374         }
375
376         return 0;
377 }
378
379 static int
380 compare_method (const void *key, const void *object)
381 {
382         guint32 token = GPOINTER_TO_UINT (key);
383         MonoSymbolFileMethodEntry *me = (MonoSymbolFileMethodEntry*)object;
384
385         return token - read32(&(me->_token));
386 }
387
388 MonoDebugMethodInfo *
389 mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
390 {
391         MonoSymbolFileMethodEntry *first_ie, *ie;
392         MonoDebugMethodInfo *minfo;
393         MonoSymbolFile *symfile = handle->symfile;
394
395         if (!symfile->method_hash)
396                 return NULL;
397
398         if (handle->image != mono_class_get_image (mono_method_get_class (method)))
399                 return NULL;
400
401         mono_debugger_lock ();
402         first_ie = (MonoSymbolFileMethodEntry *)
403                 (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset)));
404
405         ie = bsearch (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie,
406                                    read32(&(symfile->offset_table->_method_count)),
407                                    sizeof (MonoSymbolFileMethodEntry), compare_method);
408
409         if (!ie) {
410                 mono_debugger_unlock ();
411                 return NULL;
412         }
413
414         minfo = g_new0 (MonoDebugMethodInfo, 1);
415         minfo->index = (ie - first_ie) + 1;
416         minfo->method = method;
417         minfo->handle = handle;
418
419         minfo->data_offset = read32 (&(ie->_data_offset));
420         minfo->lnt_offset = read32 (&(ie->_line_number_table));
421
422         g_hash_table_insert (symfile->method_hash, method, minfo);
423
424         mono_debugger_unlock ();
425         return minfo;
426 }
427
428 /*
429  * mono_debug_symfile_lookup_locals:
430  *
431  *   Return information about the local variables of MINFO from the symbol file.
432  * NAMES and INDEXES are set to g_malloc-ed arrays containing the local names and
433  * their IL indexes.
434  * Returns: the number of elements placed into the arrays, or -1 if there is no
435  * local variable info.
436  */
437 int
438 mono_debug_symfile_lookup_locals (MonoDebugMethodInfo *minfo, char ***names, int **indexes)
439 {
440         MonoSymbolFile *symfile = minfo->handle->symfile;
441         const guint8 *p;
442         int i, len, compile_unit_index, locals_offset, num_locals, index, block_index;
443
444         *names = NULL;
445         *indexes = NULL;
446
447         if (!symfile)
448                 return -1;
449
450         p = symfile->raw_contents + minfo->data_offset;
451
452         compile_unit_index = read_leb128 (p, &p);
453         locals_offset = read_leb128 (p, &p);
454
455         p = symfile->raw_contents + locals_offset;
456         num_locals = read_leb128 (p, &p);
457
458         *names = g_new0 (char*, num_locals);
459         *indexes = g_new0 (int, num_locals);
460
461         for (i = 0; i < num_locals; ++i) {
462                 index = read_leb128 (p, &p);
463                 (*indexes) [i] = index;
464                 len = read_leb128 (p, &p);
465                 (*names) [i] = g_malloc (len + 1);
466                 memcpy ((*names) [i], p, len);
467                 (*names) [i][len] = '\0';
468                 p += len;
469                 block_index = read_leb128 (p, &p);
470         }
471
472         return num_locals;
473 }
474