2008-06-09 Martin Baulig <martin@ximian.com>
[mono.git] / mono / metadata / debug-mono-symfile.c
1 #include <config.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <signal.h>
7 #ifdef HAVE_SYS_PARAM_H
8 #include <sys/param.h>
9 #endif
10 #include <sys/stat.h>
11 #include <mono/metadata/metadata.h>
12 #include <mono/metadata/tabledefs.h>
13 #include <mono/metadata/rawbuffer.h>
14 #include <mono/metadata/tokentype.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/exception.h>
17 #include <mono/metadata/debug-helpers.h>
18 #include <mono/metadata/mono-debug.h>
19 #include <mono/metadata/debug-mono-symfile.h>
20 #include <mono/metadata/mono-debug-debugger.h>
21 #include <mono/metadata/mono-endian.h>
22 #include <mono/metadata/metadata-internals.h>
23 #include <mono/metadata/class-internals.h>
24
25 #include <fcntl.h>
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29
30 #define RANGE_TABLE_CHUNK_SIZE          256
31 #define CLASS_TABLE_CHUNK_SIZE          256
32 #define TYPE_TABLE_PTR_CHUNK_SIZE       256
33 #define TYPE_TABLE_CHUNK_SIZE           65536
34
35 static void
36 free_method_info (MonoDebugMethodInfo *minfo)
37 {
38         g_free (minfo);
39 }
40
41 static gchar *
42 get_class_name (MonoClass *klass)
43 {
44         MonoClass *nested_in = mono_class_get_nesting_type (klass);
45         const char *name_space;
46         if (nested_in) {
47                 gchar *parent_name = get_class_name (nested_in);
48                 gchar *name = g_strdup_printf ("%s.%s", parent_name, mono_class_get_name (klass));
49                 g_free (parent_name);
50                 return name;
51         }
52
53         name_space = mono_class_get_namespace (klass);
54         return g_strdup_printf ("%s%s%s", name_space,
55                                 name_space [0] ? "." : "", mono_class_get_name (klass));
56 }
57
58 static int
59 load_symfile (MonoDebugHandle *handle, MonoSymbolFile *symfile, gboolean in_the_debugger)
60 {
61         const char *ptr, *start;
62         gchar *guid;
63         guint64 magic;
64         int minor, major;
65
66         ptr = start = (const char*)symfile->raw_contents;
67         if (!ptr)
68                 return FALSE;
69
70         magic = read64(ptr);
71         ptr += sizeof(guint64);
72         if (magic != MONO_SYMBOL_FILE_MAGIC) {
73                 if (!in_the_debugger)
74                         g_warning ("Symbol file %s is not a mono symbol file", symfile->filename);
75                 return FALSE;
76         }
77
78         major = read32(ptr);
79         ptr += sizeof(guint32);
80         minor = read32(ptr);
81         ptr += sizeof(guint32);
82
83         if (major != MONO_SYMBOL_FILE_MAJOR_VERSION) {
84                 if (!in_the_debugger)
85                         g_warning ("Symbol file %s has incorrect version (expected %d, got %d)",
86                                    symfile->filename, MONO_SYMBOL_FILE_MAJOR_VERSION, major);
87                 return FALSE;
88         }
89
90         if (minor != MONO_SYMBOL_FILE_MINOR_VERSION) {
91                 if (!in_the_debugger)
92                         g_warning ("Symbol file %s has incorrect version "
93                                    "(expected %d.%d, got %d.%d)", symfile->filename,
94                                    MONO_SYMBOL_FILE_MAJOR_VERSION, MONO_SYMBOL_FILE_MINOR_VERSION,
95                                    major, minor);
96                 return FALSE;
97         }
98
99         guid = mono_guid_to_string ((const guint8 *) ptr);
100         ptr += 16;
101
102         if (strcmp (handle->image->guid, guid)) {
103                 if (!in_the_debugger)
104                         g_warning ("Symbol file %s doesn't match image %s", symfile->filename,
105                                    handle->image_file);
106                 if (guid)
107                         g_free (guid);
108                 return FALSE;
109         }
110
111         symfile->major_version = major;
112         symfile->minor_version = minor;
113
114         symfile->offset_table = (MonoSymbolFileOffsetTable *) ptr;
115
116         symfile->method_hash = g_hash_table_new_full (
117                 g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_method_info);
118
119         g_free (guid);
120         return TRUE;
121 }
122
123 MonoSymbolFile *
124 mono_debug_open_mono_symbols (MonoDebugHandle *handle, const guint8 *raw_contents,
125                               int size, gboolean in_the_debugger)
126 {
127         MonoSymbolFile *symfile;
128         FILE* f;
129
130         mono_debugger_lock ();
131         symfile = g_new0 (MonoSymbolFile, 1);
132
133         if (raw_contents != NULL) {
134                 unsigned char *p;
135                 symfile->raw_contents_size = size;
136                 symfile->raw_contents = p = g_malloc (size);
137                 memcpy (p, raw_contents, size);
138                 symfile->filename = g_strdup_printf ("LoadedFromMemory");
139         } else {
140                 symfile->filename = g_strdup_printf ("%s.mdb", mono_image_get_filename (handle->image));
141
142                 if ((f = fopen (symfile->filename, "rb"))) {
143                         struct stat stat_buf;
144                         
145                         if (fstat (fileno (f), &stat_buf) < 0) {
146                                 if (!in_the_debugger)
147                                         g_warning ("stat of %s failed: %s",
148                                                    symfile->filename,  g_strerror (errno));
149                         } else {
150                                 symfile->raw_contents_size = stat_buf.st_size;
151                                 symfile->raw_contents = mono_raw_buffer_load (fileno (f), FALSE, 0, stat_buf.st_size);
152                         }
153
154                         fclose (f);
155                 }
156         }
157         
158         if (load_symfile (handle, symfile, in_the_debugger)) {
159                 mono_debugger_unlock ();
160                 return symfile;
161         } else if (!in_the_debugger) {
162                 mono_debug_close_mono_symbol_file (symfile);
163                 mono_debugger_unlock ();
164                 return NULL;
165         }
166
167         mono_debugger_unlock ();
168         return symfile;
169 }
170
171 void
172 mono_debug_close_mono_symbol_file (MonoSymbolFile *symfile)
173 {
174         if (!symfile)
175                 return;
176
177         mono_debugger_lock ();
178         if (symfile->method_hash)
179                 g_hash_table_destroy (symfile->method_hash);
180
181         if (symfile->raw_contents)
182                 mono_raw_buffer_free ((gpointer) symfile->raw_contents);
183
184         if (symfile->filename)
185                 g_free (symfile->filename);
186         g_free (symfile);
187         mono_debugger_unlock ();
188 }
189
190 static int
191 read_leb128 (const guint8 *ptr, const guint8 **rptr)
192 {
193         int ret = 0;
194         int shift = 0;
195         char b;
196
197         do {
198                 b = *ptr++;
199                                 
200                 ret = ret | ((b & 0x7f) << shift);
201                 shift += 7;
202         } while ((b & 0x80) == 0x80);
203
204         if (rptr)
205                 *rptr = ptr;
206
207         return ret;
208 }
209
210 static gchar *
211 read_string (const guint8 *ptr)
212 {
213         int len = read_leb128 (ptr, &ptr);
214         return g_filename_from_utf8 ((const char *) ptr, len, NULL, NULL, NULL);
215 }
216
217 typedef struct {
218         MonoSymbolFile *symfile;
219         int line_base, line_range, max_address_incr;
220         guint8 opcode_base;
221         guint32 last_line, last_file, last_offset;
222         guint32 line, file, offset;
223 } StatementMachine;
224
225 static gboolean
226 check_line (StatementMachine *stm, int offset, MonoDebugSourceLocation **location)
227 {
228         gchar *source_file = NULL;
229
230         if ((offset > 0) && (stm->offset <= offset)) {
231                 stm->last_offset = stm->offset;
232                 stm->last_file = stm->file;
233                 if (stm->line != 0xfeefee)
234                         stm->last_line = stm->line;
235                 return FALSE;
236         }
237
238         if (stm->last_file) {
239                 int offset = read32(&(stm->symfile->offset_table->_source_table_offset)) +
240                         (stm->last_file - 1) * sizeof (MonoSymbolFileSourceEntry);
241                 MonoSymbolFileSourceEntry *se = (MonoSymbolFileSourceEntry *)
242                         (stm->symfile->raw_contents + offset);
243
244                 source_file = read_string (stm->symfile->raw_contents + read32(&(se->_data_offset)));
245         }
246
247         *location = g_new0 (MonoDebugSourceLocation, 1);
248         (*location)->source_file = source_file;
249         (*location)->row = stm->last_line;
250         (*location)->il_offset = stm->last_offset;
251         return TRUE;
252 }
253
254 /**
255  * mono_debug_symfile_lookup_location:
256  * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
257  *         mono_debug_lookup_method().
258  * @offset: IL offset within the corresponding method's CIL code.
259  *
260  * This function is similar to mono_debug_lookup_location(), but we
261  * already looked up the method and also already did the
262  * `native address -> IL offset' mapping.
263  */
264 MonoDebugSourceLocation *
265 mono_debug_symfile_lookup_location (MonoDebugMethodInfo *minfo, guint32 offset)
266 {
267         MonoDebugSourceLocation *location = NULL;
268         MonoSymbolFile *symfile;
269         const unsigned char *ptr;
270         StatementMachine stm;
271
272 #define DW_LNS_copy 1
273 #define DW_LNS_advance_pc 2
274 #define DW_LNS_advance_line 3
275 #define DW_LNS_set_file 4
276 #define DW_LNS_const_add_pc 8
277
278 #define DW_LNE_end_sequence 1
279 #define DW_LNE_MONO_negate_is_hidden 0x40
280
281         if ((symfile = minfo->handle->symfile) == NULL)
282                 return NULL;
283
284         stm.line_base = symfile->offset_table->_line_number_table_line_base;
285         stm.line_range = symfile->offset_table->_line_number_table_line_range;
286         stm.opcode_base = (guint8) symfile->offset_table->_line_number_table_opcode_base;
287         stm.max_address_incr = (255 - stm.opcode_base) / stm.line_range;
288
289         mono_debugger_lock ();
290
291         ptr = symfile->raw_contents + minfo->lnt_offset;
292
293         stm.symfile = symfile;
294         stm.offset = stm.last_offset = 0;
295         stm.file = stm.last_file = 1;
296         stm.line = stm.last_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 {
314                                 g_warning ("Unknown extended opcode %x in LNT", opcode);
315                         }
316
317                         ptr = end_ptr;
318                         continue;
319                 } else if (opcode < stm.opcode_base) {
320                         switch (opcode) {
321                         case DW_LNS_copy:
322                                 if (check_line (&stm, offset, &location))
323                                         goto out_success;
324                                 break;
325                         case DW_LNS_advance_pc:
326                                 stm.offset += read_leb128 (ptr, &ptr);
327                                 break;
328                         case DW_LNS_advance_line:
329                                 stm.line += read_leb128 (ptr, &ptr);
330                                 break;
331                         case DW_LNS_set_file:
332                                 stm.file = read_leb128 (ptr, &ptr);
333                                 break;
334                         case DW_LNS_const_add_pc:
335                                 stm.offset += stm.max_address_incr;
336                                 break;
337                         default:
338                                 g_warning ("Unknown standard opcode %x in LNT", opcode);
339                                 goto error_out;
340                         }
341                 } else {
342                         opcode -= stm.opcode_base;
343
344                         stm.offset += opcode / stm.line_range;
345                         stm.line += stm.line_base + (opcode % stm.line_range);
346
347                         if (check_line (&stm, offset, &location))
348                                 goto out_success;
349                 }
350         }
351
352  error_out:
353         mono_debugger_unlock ();
354         return NULL;
355
356  out_success:
357         mono_debugger_unlock ();
358         return location;
359 }
360
361 gint32
362 _mono_debug_address_from_il_offset (MonoDebugMethodJitInfo *jit, guint32 il_offset)
363 {
364         int i;
365
366         if (!jit || !jit->line_numbers)
367                 return -1;
368
369         for (i = jit->num_line_numbers - 1; i >= 0; i--) {
370                 MonoDebugLineNumberEntry lne = jit->line_numbers [i];
371
372                 if (lne.il_offset < 0)
373                         continue;
374                 if (lne.il_offset <= il_offset)
375                         return lne.native_offset;
376         }
377
378         return 0;
379 }
380
381 static int
382 compare_method (const void *key, const void *object)
383 {
384         guint32 token = GPOINTER_TO_UINT (key);
385         MonoSymbolFileMethodEntry *me = (MonoSymbolFileMethodEntry*)object;
386
387         return token - read32(&(me->_token));
388 }
389
390 MonoDebugMethodInfo *
391 mono_debug_symfile_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
392 {
393         MonoSymbolFileMethodEntry *first_ie, *ie;
394         MonoDebugMethodInfo *minfo;
395         MonoSymbolFile *symfile = handle->symfile;
396
397         if (!symfile->method_hash)
398                 return NULL;
399
400         if (handle->image != mono_class_get_image (mono_method_get_class (method)))
401                 return NULL;
402
403         mono_debugger_lock ();
404         first_ie = (MonoSymbolFileMethodEntry *)
405                 (symfile->raw_contents + read32(&(symfile->offset_table->_method_table_offset)));
406
407         ie = bsearch (GUINT_TO_POINTER (mono_method_get_token (method)), first_ie,
408                                    read32(&(symfile->offset_table->_method_count)),
409                                    sizeof (MonoSymbolFileMethodEntry), compare_method);
410
411         if (!ie) {
412                 mono_debugger_unlock ();
413                 return NULL;
414         }
415
416         minfo = g_new0 (MonoDebugMethodInfo, 1);
417         minfo->index = (ie - first_ie) + 1;
418         minfo->method = method;
419         minfo->handle = handle;
420
421         minfo->data_offset = read32 (&(ie->_data_offset));
422         minfo->lnt_offset = read32 (&(ie->_line_number_table));
423
424         g_hash_table_insert (symfile->method_hash, method, minfo);
425
426         mono_debugger_unlock ();
427         return minfo;
428 }