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