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