Merge pull request #2194 from alexrp/ppdb-status-crash
[mono.git] / mono / metadata / debug-mono-ppdb.c
1 /*
2  * debug-mono-ppdb.c: Support for the portable PDB symbol
3  * file format
4  *
5  *
6  * Author:
7  *      Mono Project (http://www.mono-project.com)
8  *
9  * Copyright 2015 Xamarin Inc (http://www.xamarin.com)
10  */
11
12 #include <config.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <mono/metadata/metadata.h>
18 #include <mono/metadata/tabledefs.h>
19 #include <mono/metadata/tokentype.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/metadata/mono-debug.h>
22 #include <mono/metadata/debug-mono-symfile.h>
23 #include <mono/metadata/mono-debug-debugger.h>
24 #include <mono/metadata/mono-endian.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/class-internals.h>
27 #include <mono/metadata/cil-coff.h>
28
29 #include "debug-mono-ppdb.h"
30
31 struct _MonoPPDBFile {
32         MonoImage *image;
33         GHashTable *doc_cache;
34 };
35
36 /* IMAGE_DEBUG_DIRECTORY structure */
37 typedef struct
38 {
39         gint32 characteristics;
40         gint32 time_date_stamp;
41         gint16 major_version;
42         gint16 minor_version;
43         gint32 type;
44         gint32 size_of_data;
45         gint32 address;
46         gint32 pointer;
47 }  ImageDebugDirectory;
48
49 typedef struct {
50         gint32 signature;
51         guint8 guid [16];
52         gint32 age;
53 } CodeviewDebugDirectory;
54
55 typedef struct {
56         guint8 guid [20];
57         guint32 entry_point;
58         guint64 referenced_tables;
59 } PdbStreamHeader;
60
61 static gboolean
62 get_pe_debug_guid (MonoImage *image, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp)
63 {
64         MonoPEDirEntry *debug_dir_entry;
65         ImageDebugDirectory *debug_dir;
66
67         debug_dir_entry = &((MonoCLIImageInfo*)image->image_info)->cli_header.datadir.pe_debug;
68         if (!debug_dir_entry->size)
69                 return FALSE;
70
71         int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva);
72         debug_dir = (ImageDebugDirectory*)(image->raw_data + offset);
73         if (debug_dir->type == 2 && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) {
74                 /* This is a 'CODEVIEW' debug directory */
75                 CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(image->raw_data + debug_dir->pointer);
76
77                 if (dir->signature == 0x53445352) {
78                         memcpy (out_guid, dir->guid, 16);
79                         *out_age = dir->age;
80                         *out_timestamp = debug_dir->time_date_stamp;
81                         return TRUE;
82                 }
83         }
84         return FALSE;
85 }
86
87 MonoPPDBFile*
88 mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size)
89 {
90         MonoImage *ppdb_image;
91         const char *filename;
92         char *s, *ppdb_filename;
93         MonoImageOpenStatus status;
94         guint8 pe_guid [16];
95         gint32 pe_age;
96         gint32 pe_timestamp;
97         MonoPPDBFile *ppdb;
98
99         if (raw_contents) {
100                 ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
101         } else {
102                 /* ppdb files drop the .exe/.dll extension */
103                 filename = mono_image_get_filename (image);
104                 if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe"))) {
105                         s = g_strdup (filename);
106                         s [strlen (filename) - 4] = '\0';
107                         ppdb_filename = g_strdup_printf ("%s.pdb", s);
108                         g_free (s);
109                 } else {
110                         ppdb_filename = g_strdup_printf ("%s.pdb", filename);
111                 }
112
113                 ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
114         }
115         if (!ppdb_image)
116                 return NULL;
117
118         /*
119          * Check that the images match.
120          * The same id is stored in the Debug Directory of the PE file, and in the
121          * #Pdb stream in the ppdb file.
122          */
123         if (get_pe_debug_guid (image, pe_guid, &pe_age, &pe_timestamp)) {
124                 PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
125
126                 g_assert (pdb_stream);
127
128                 /* The pdb id is a concentation of the pe guid and the timestamp */
129                 if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
130                         g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
131                                            image->name);
132                         mono_image_close (ppdb_image);
133                         return NULL;
134                 }
135         }
136
137         ppdb = g_new0 (MonoPPDBFile, 1);
138         ppdb->image = ppdb_image;
139
140         return ppdb;
141 }
142
143 void
144 mono_ppdb_close (MonoDebugHandle *handle)
145 {
146         MonoPPDBFile *ppdb = handle->ppdb;
147
148         mono_image_close (ppdb->image);
149         if (ppdb->doc_cache)
150                 g_hash_table_destroy (ppdb->doc_cache);
151         g_free (ppdb);
152 }
153
154 MonoDebugMethodInfo *
155 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
156 {
157         MonoDebugMethodInfo *minfo;
158
159         if (handle->image != mono_class_get_image (mono_method_get_class (method)))
160                 return NULL;
161
162         // FIXME: Cache
163
164         // FIXME: Methods without tokens
165
166         minfo = g_new0 (MonoDebugMethodInfo, 1);
167         minfo->index = 0;
168         minfo->method = method;
169         minfo->handle = handle;
170
171         return minfo;
172 }
173
174 static MonoDebugSourceInfo*
175 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
176 {
177         MonoTableInfo *tables = image->tables;
178         guint32 cols [MONO_DOCUMENT_SIZE];
179         const char *ptr;
180         const char *start;
181         const char *part_ptr;
182         int size, part_size, partidx, nparts;
183         char sep;
184         GString *s;
185         MonoDebugSourceInfo *res;
186
187         // FIXME: Cache
188
189         mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
190
191         ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
192         size = mono_metadata_decode_blob_size (ptr, &ptr);
193         start = ptr;
194
195         // FIXME: UTF8
196         sep = ptr [0];
197         ptr ++;
198
199         s = g_string_new ("");
200
201         nparts = 0;
202         while (ptr < start + size) {
203                 partidx = mono_metadata_decode_value (ptr, &ptr);
204                 if (nparts)
205                         g_string_append_c (s, sep);
206                 if (partidx) {
207                         part_ptr = mono_metadata_blob_heap (image, partidx);
208                         part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
209
210                         // FIXME: UTF8
211                         g_string_append_len (s, part_ptr, part_size);
212                 }
213                 nparts ++;
214         }
215
216         res = g_new0 (MonoDebugSourceInfo, 1);
217         res->source_file = g_string_free (s, FALSE);
218         res->guid = NULL;
219         res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
220
221         return res;
222 }
223
224 static char*
225 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
226 {
227         MonoDebugSourceInfo *info;
228
229         info = get_docinfo (ppdb, image, docidx);
230         return g_strdup (info->source_file);
231 }
232
233 /**
234  * mono_ppdb_lookup_location:
235  * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
236  *         mono_debug_lookup_method().
237  * @offset: IL offset within the corresponding method's CIL code.
238  *
239  * This function is similar to mono_debug_lookup_location(), but we
240  * already looked up the method and also already did the
241  * `native address -> IL offset' mapping.
242  */
243 MonoDebugSourceLocation *
244 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
245 {
246         MonoPPDBFile *ppdb = minfo->handle->ppdb;
247         MonoImage *image = ppdb->image;
248         MonoMethod *method = minfo->method;
249         MonoTableInfo *tables = image->tables;
250         guint32 cols [MONO_METHODBODY_SIZE];
251         const char *ptr;
252         const char *end;
253         char *docname;
254         int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
255         gboolean first = TRUE, first_non_hidden = TRUE;
256         MonoDebugSourceLocation *location;
257
258         g_assert (method->token);
259
260         idx = mono_metadata_token_index (method->token);
261
262         mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
263
264         docidx = cols [MONO_METHODBODY_DOCUMENT];
265
266         // FIXME:
267         g_assert (cols [MONO_METHODBODY_SEQ_POINTS]);
268         ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
269         size = mono_metadata_decode_blob_size (ptr, &ptr);
270         end = ptr + size;
271
272         /* Header */
273         /* LocalSignature */
274         mono_metadata_decode_value (ptr, &ptr);
275         if (docidx == 0)
276                 docidx = mono_metadata_decode_value (ptr, &ptr);
277         docname = get_docname (ppdb, image, docidx);
278
279         iloffset = 0;
280         start_line = 0;
281         start_col = 0;
282         while (ptr < end) {
283                 delta_il = mono_metadata_decode_value (ptr, &ptr);
284                 if (!first && delta_il == 0) {
285                         /* Document record */
286                         // FIXME:
287                         g_assert_not_reached ();
288                 }
289                 if (!first && iloffset + delta_il > offset)
290                         break;
291                 iloffset += delta_il;
292                 first = FALSE;
293
294                 delta_lines = mono_metadata_decode_value (ptr, &ptr);
295                 if (delta_lines == 0)
296                         delta_cols = mono_metadata_decode_value (ptr, &ptr);
297                 else
298                         delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
299                 if (delta_lines == 0 && delta_cols == 0)
300                         // FIXME:
301                         g_assert_not_reached ();
302                 if (first_non_hidden) {
303                         start_line = mono_metadata_decode_value (ptr, &ptr);
304                         start_col = mono_metadata_decode_value (ptr, &ptr);
305                 } else {
306                         adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
307                         adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
308                         start_line += adv_line;
309                         start_col += adv_col;
310                 }
311                 first_non_hidden = FALSE;
312         }
313
314         location = g_new0 (MonoDebugSourceLocation, 1);
315         location->source_file = docname;
316         location->row = start_line;
317         location->il_offset = iloffset;
318
319         return location;
320 }
321
322 void
323 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
324 {
325         MonoPPDBFile *ppdb = minfo->handle->ppdb;
326         MonoImage *image = ppdb->image;
327         MonoMethod *method = minfo->method;
328         MonoTableInfo *tables = image->tables;
329         guint32 cols [MONO_METHODBODY_SIZE];
330         const char *ptr;
331         const char *end;
332         MonoDebugSourceInfo *docinfo;
333         int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
334         gboolean first = TRUE, first_non_hidden = TRUE;
335         GArray *sps;
336         MonoSymSeqPoint sp;
337         GPtrArray *sfiles = NULL;
338         GPtrArray *sindexes = NULL;
339
340         if (source_file)
341                 *source_file = NULL;
342         if (source_file_list)
343                 *source_file_list = NULL;
344         if (source_files)
345                 *source_files = NULL;
346         if (seq_points)
347                 *seq_points = NULL;
348         if (n_seq_points)
349                 *n_seq_points = 0;
350
351         if (source_file_list)
352                 *source_file_list = sfiles = g_ptr_array_new ();
353         if (source_files)
354                 sindexes = g_ptr_array_new ();
355
356         if (!method->token)
357                 return;
358
359         method_idx = mono_metadata_token_index (method->token);
360
361         mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], method_idx-1, cols, MONO_METHODBODY_SIZE);
362
363         docidx = cols [MONO_METHODBODY_DOCUMENT];
364
365         if (!cols [MONO_METHODBODY_SEQ_POINTS])
366                 return;
367
368         ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
369         size = mono_metadata_decode_blob_size (ptr, &ptr);
370         end = ptr + size;
371
372         sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
373
374         /* Header */
375         /* LocalSignature */
376         mono_metadata_decode_value (ptr, &ptr);
377         if (docidx == 0)
378                 docidx = mono_metadata_decode_value (ptr, &ptr);
379         docinfo = get_docinfo (ppdb, image, docidx);
380
381         if (sfiles)
382                 g_ptr_array_add (sfiles, docinfo);
383
384         iloffset = 0;
385         start_line = 0;
386         start_col = 0;
387         while (ptr < end) {
388                 delta_il = mono_metadata_decode_value (ptr, &ptr);
389                 if (!first && delta_il == 0) {
390                         /* subsequent-document-record */
391                         docidx = mono_metadata_decode_value (ptr, &ptr);
392                         docinfo = get_docinfo (ppdb, image, docidx);
393                         if (sfiles)
394                                 g_ptr_array_add (sfiles, docinfo);
395                         continue;
396                 }
397                 iloffset += delta_il;
398                 first = FALSE;
399
400                 delta_lines = mono_metadata_decode_value (ptr, &ptr);
401                 if (delta_lines == 0)
402                         delta_cols = mono_metadata_decode_value (ptr, &ptr);
403                 else
404                         delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
405
406                 if (delta_lines == 0 && delta_cols == 0) {
407                         /* Hidden sequence point */
408                         continue;
409                 }
410
411                 if (first_non_hidden) {
412                         start_line = mono_metadata_decode_value (ptr, &ptr);
413                         start_col = mono_metadata_decode_value (ptr, &ptr);
414                 } else {
415                         adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
416                         adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
417                         start_line += adv_line;
418                         start_col += adv_col;
419                 }
420                 first_non_hidden = FALSE;
421
422                 memset (&sp, 0, sizeof (sp));
423                 sp.il_offset = iloffset;
424                 sp.line = start_line;
425                 sp.column = start_col;
426                 sp.end_line = start_line + delta_lines;
427                 sp.end_column = start_col + delta_cols;
428
429                 g_array_append_val (sps, sp);
430                 if (source_files)
431                         g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
432         }
433
434         if (n_seq_points) {
435                 *n_seq_points = sps->len;
436                 g_assert (seq_points);
437                 *seq_points = g_new (MonoSymSeqPoint, sps->len);
438                 memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
439         }
440
441         if (source_file)
442                 *source_file = g_strdup (((MonoDebugSourceInfo*)g_ptr_array_index (sfiles, 0))->source_file);
443         if (source_files) {
444                 *source_files = g_new (int, sps->len);
445                 for (i = 0; i < sps->len; ++i)
446                         (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
447                 g_ptr_array_free (sindexes, TRUE);
448         }
449
450         g_array_free (sps, TRUE);
451 }
452
453 MonoDebugLocalsInfo*
454 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
455 {
456         MonoPPDBFile *ppdb = minfo->handle->ppdb;
457         MonoImage *image = ppdb->image;
458         MonoTableInfo *tables = image->tables;
459         MonoMethod *method = minfo->method;
460         guint32 cols [MONO_LOCALSCOPE_SIZE];
461         guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
462         int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
463         MonoDebugLocalsInfo *res;
464         MonoMethodSignature *sig;
465
466         if (!method->token)
467                 return NULL;
468
469         sig = mono_method_signature (method);
470         if (!sig)
471                 return NULL;
472
473         method_idx = mono_metadata_token_index (method->token);
474
475         start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
476
477         if (!start_scope_idx)
478                 return NULL;
479
480         /* Compute number of locals and scopes */
481         scope_idx = start_scope_idx;
482         mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
483         locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
484         while (TRUE) {
485                 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
486                 if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
487                         break;
488                 scope_idx ++;
489         }
490         nscopes = scope_idx - start_scope_idx;
491         if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
492                 // FIXME:
493                 g_assert_not_reached ();
494                 locals_end_idx = -1;
495         } else {
496                 locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
497         }
498
499         res = g_new0 (MonoDebugLocalsInfo, 1);
500         res->num_blocks = nscopes;
501         res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
502         res->num_locals = locals_end_idx - locals_idx;
503         res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
504
505         lindex = 0;
506         for (sindex = 0; sindex < nscopes; ++sindex) {
507                 scope_idx = start_scope_idx + sindex;
508                 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
509
510                 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
511                 if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
512                         // FIXME:
513                         g_assert_not_reached ();
514                 } else {
515                         locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
516                 }
517
518                 res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
519                 res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
520
521                 //printf ("Scope: %s %d %d %d-%d\n", mono_method_full_name (method, 1), cols [MONO_LOCALSCOPE_STARTOFFSET], cols [MONO_LOCALSCOPE_LENGTH], locals_idx, locals_end_idx);
522
523                 for (i = locals_idx; i < locals_end_idx; ++i) {
524                         mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
525
526                         res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
527                         res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
528                         res->locals [lindex].block = &res->code_blocks [sindex];
529                         lindex ++;
530
531                         //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
532                 }
533         }
534
535         return res;
536 }