[image] Create MonoImageStorage to own the image raw data (#13892)
[mono-project.git] / mono / metadata / debug-mono-ppdb.c
blob53bae708609e08634632b996f25acd0c131fcd3b
1 /**
2 * \file
3 * Support for the portable PDB symbol
4 * file format
7 * Author:
8 * Mono Project (http://www.mono-project.com)
10 * Copyright 2015 Xamarin Inc (http://www.xamarin.com)
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
14 #include <config.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <mono/metadata/metadata.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/tokentype.h>
22 #include <mono/metadata/debug-helpers.h>
23 #include <mono/metadata/mono-debug.h>
24 #include <mono/metadata/debug-internals.h>
25 #include <mono/metadata/mono-endian.h>
26 #include <mono/metadata/metadata-internals.h>
27 #include <mono/metadata/class-internals.h>
28 #include <mono/metadata/cil-coff.h>
29 #include <mono/utils/bsearch.h>
30 #include <mono/utils/mono-logger-internals.h>
32 #include "debug-mono-ppdb.h"
34 struct _MonoPPDBFile {
35 MonoImage *image;
36 GHashTable *doc_hash;
37 GHashTable *method_hash;
40 /* IMAGE_DEBUG_DIRECTORY structure */
41 typedef struct
43 gint32 characteristics;
44 gint32 time_date_stamp;
45 gint16 major_version;
46 gint16 minor_version;
47 gint32 type;
48 gint32 size_of_data;
49 gint32 address;
50 gint32 pointer;
51 } ImageDebugDirectory;
53 typedef struct {
54 gint32 signature;
55 guint8 guid [16];
56 gint32 age;
57 } CodeviewDebugDirectory;
59 typedef struct {
60 guint8 guid [20];
61 guint32 entry_point;
62 guint64 referenced_tables;
63 } PdbStreamHeader;
65 enum {
66 MONO_HAS_CUSTOM_DEBUG_METHODDEF = 0,
67 MONO_HAS_CUSTOM_DEBUG_MODULE = 7,
68 MONO_HAS_CUSTOM_DEBUG_BITS = 5,
69 MONO_HAS_CUSTOM_DEBUG_MASK = 0x1f
72 static gboolean
73 get_pe_debug_guid (MonoImage *image, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp)
75 MonoPEDirEntry *debug_dir_entry;
76 ImageDebugDirectory *debug_dir;
78 debug_dir_entry = &image->image_info->cli_header.datadir.pe_debug;
79 if (!debug_dir_entry->size)
80 return FALSE;
82 int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva);
83 debug_dir = (ImageDebugDirectory*)(m_image_get_raw_data (image) + offset);
84 if (debug_dir->type == 2 && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) {
85 /* This is a 'CODEVIEW' debug directory */
86 CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(m_image_get_raw_data (image) + debug_dir->pointer);
88 if (dir->signature == 0x53445352) {
89 memcpy (out_guid, dir->guid, 16);
90 *out_age = dir->age;
91 *out_timestamp = debug_dir->time_date_stamp;
92 return TRUE;
95 return FALSE;
98 static void
99 doc_free (gpointer key)
101 MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key;
103 g_free (info->source_file);
104 g_free (info);
107 static MonoPPDBFile*
108 create_ppdb_file (MonoImage *ppdb_image)
110 MonoPPDBFile *ppdb;
112 ppdb = g_new0 (MonoPPDBFile, 1);
113 ppdb->image = ppdb_image;
114 ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free);
115 ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free);
116 return ppdb;
119 MonoPPDBFile*
120 mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size)
122 MonoImage *ppdb_image = NULL;
123 const char *filename;
124 char *s, *ppdb_filename;
125 MonoImageOpenStatus status;
126 guint8 pe_guid [16];
127 gint32 pe_age;
128 gint32 pe_timestamp;
130 if (image->tables [MONO_TABLE_DOCUMENT].rows) {
131 /* Embedded ppdb */
132 mono_image_addref (image);
133 return create_ppdb_file (image);
136 if (!get_pe_debug_guid (image, pe_guid, &pe_age, &pe_timestamp)) {
137 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Image '%s' has no debug directory.", image->name);
138 return NULL;
141 if (raw_contents) {
142 if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0)
143 ppdb_image = mono_image_open_from_data_internal ((char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
144 } else {
145 /* ppdb files drop the .exe/.dll extension */
146 filename = mono_image_get_filename (image);
147 if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) {
148 s = g_strdup (filename);
149 s [strlen (filename) - 4] = '\0';
150 ppdb_filename = g_strdup_printf ("%s.pdb", s);
151 g_free (s);
152 } else {
153 ppdb_filename = g_strdup_printf ("%s.pdb", filename);
156 ppdb_image = mono_image_open_metadata_only (ppdb_filename, &status);
157 if (!ppdb_image)
158 g_free (ppdb_filename);
160 if (!ppdb_image)
161 return NULL;
164 * Check that the images match.
165 * The same id is stored in the Debug Directory of the PE file, and in the
166 * #Pdb stream in the ppdb file.
168 PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
170 g_assert (pdb_stream);
172 /* The pdb id is a concentation of the pe guid and the timestamp */
173 if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
174 g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
175 image->name);
176 mono_image_close (ppdb_image);
177 return NULL;
180 return create_ppdb_file (ppdb_image);
183 void
184 mono_ppdb_close (MonoDebugHandle *handle)
186 MonoPPDBFile *ppdb = handle->ppdb;
188 mono_image_close (ppdb->image);
189 g_hash_table_destroy (ppdb->doc_hash);
190 g_hash_table_destroy (ppdb->method_hash);
191 g_free (ppdb);
194 MonoDebugMethodInfo *
195 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
197 MonoDebugMethodInfo *minfo;
198 MonoPPDBFile *ppdb = handle->ppdb;
200 if (handle->image != mono_class_get_image (mono_method_get_class (method)))
201 return NULL;
203 mono_debugger_lock ();
205 minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method);
206 if (minfo) {
207 mono_debugger_unlock ();
208 return minfo;
211 minfo = g_new0 (MonoDebugMethodInfo, 1);
212 minfo->index = 0;
213 minfo->method = method;
214 minfo->handle = handle;
216 g_hash_table_insert (ppdb->method_hash, method, minfo);
218 mono_debugger_unlock ();
220 return minfo;
223 static MonoDebugSourceInfo*
224 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
226 MonoTableInfo *tables = image->tables;
227 guint32 cols [MONO_DOCUMENT_SIZE];
228 const char *ptr;
229 const char *start;
230 const char *part_ptr;
231 int size, part_size, partidx, nparts;
232 char sep;
233 GString *s;
234 MonoDebugSourceInfo *res, *cached;
236 mono_debugger_lock ();
237 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
238 mono_debugger_unlock ();
239 if (cached)
240 return cached;
242 mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
244 ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
245 size = mono_metadata_decode_blob_size (ptr, &ptr);
246 start = ptr;
248 // FIXME: UTF8
249 sep = ptr [0];
250 ptr ++;
252 s = g_string_new ("");
254 nparts = 0;
255 while (ptr < start + size) {
256 partidx = mono_metadata_decode_value (ptr, &ptr);
257 if (nparts)
258 g_string_append_c (s, sep);
259 if (partidx) {
260 part_ptr = mono_metadata_blob_heap (image, partidx);
261 part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
263 // FIXME: UTF8
264 g_string_append_len (s, part_ptr, part_size);
266 nparts ++;
269 res = g_new0 (MonoDebugSourceInfo, 1);
270 res->source_file = g_string_free (s, FALSE);
271 res->guid = NULL;
272 res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
274 mono_debugger_lock ();
275 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
276 if (!cached) {
277 g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res);
278 } else {
279 doc_free (res);
280 res = cached;
282 mono_debugger_unlock ();
283 return res;
286 static char*
287 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
289 MonoDebugSourceInfo *info;
291 info = get_docinfo (ppdb, image, docidx);
292 return g_strdup (info->source_file);
296 * mono_ppdb_lookup_location:
297 * \param minfo A \c MonoDebugMethodInfo which can be retrieved by mono_debug_lookup_method().
298 * \param offset IL offset within the corresponding method's CIL code.
300 * This function is similar to mono_debug_lookup_location(), but we
301 * already looked up the method and also already did the
302 * native address -> IL offset mapping.
304 MonoDebugSourceLocation *
305 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
307 MonoPPDBFile *ppdb = minfo->handle->ppdb;
308 MonoImage *image = ppdb->image;
309 MonoMethod *method = minfo->method;
310 MonoTableInfo *tables = image->tables;
311 guint32 cols [MONO_METHODBODY_SIZE];
312 const char *ptr;
313 const char *end;
314 char *docname;
315 int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
316 gboolean first = TRUE, first_non_hidden = TRUE;
317 MonoDebugSourceLocation *location;
319 if (!method->token)
320 return NULL;
322 idx = mono_metadata_token_index (method->token);
324 mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
326 docidx = cols [MONO_METHODBODY_DOCUMENT];
328 if (!cols [MONO_METHODBODY_SEQ_POINTS])
329 return NULL;
330 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
331 size = mono_metadata_decode_blob_size (ptr, &ptr);
332 end = ptr + size;
334 /* Header */
335 /* LocalSignature */
336 mono_metadata_decode_value (ptr, &ptr);
337 if (docidx == 0)
338 docidx = mono_metadata_decode_value (ptr, &ptr);
339 docname = get_docname (ppdb, image, docidx);
341 iloffset = 0;
342 start_line = 0;
343 start_col = 0;
344 while (ptr < end) {
345 delta_il = mono_metadata_decode_value (ptr, &ptr);
346 if (!first && delta_il == 0) {
347 /* document-record */
348 docidx = mono_metadata_decode_value (ptr, &ptr);
349 docname = get_docname (ppdb, image, docidx);
350 continue;
352 if (!first && iloffset + delta_il > offset)
353 break;
354 iloffset += delta_il;
355 first = FALSE;
357 delta_lines = mono_metadata_decode_value (ptr, &ptr);
358 if (delta_lines == 0)
359 delta_cols = mono_metadata_decode_value (ptr, &ptr);
360 else
361 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
362 if (delta_lines == 0 && delta_cols == 0)
363 /* hidden-sequence-point-record */
364 continue;
365 if (first_non_hidden) {
366 start_line = mono_metadata_decode_value (ptr, &ptr);
367 start_col = mono_metadata_decode_value (ptr, &ptr);
368 } else {
369 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
370 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
371 start_line += adv_line;
372 start_col += adv_col;
374 first_non_hidden = FALSE;
377 location = g_new0 (MonoDebugSourceLocation, 1);
378 location->source_file = docname;
379 location->row = start_line;
380 location->column = start_col;
381 location->il_offset = iloffset;
383 return location;
386 MonoImage *
387 mono_ppdb_get_image (MonoPPDBFile *ppdb)
389 return ppdb->image;
392 void
393 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
395 MonoPPDBFile *ppdb = minfo->handle->ppdb;
396 MonoImage *image = ppdb->image;
397 MonoMethod *method = minfo->method;
398 MonoTableInfo *tables = image->tables;
399 guint32 cols [MONO_METHODBODY_SIZE];
400 const char *ptr;
401 const char *end;
402 MonoDebugSourceInfo *docinfo;
403 int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
404 gboolean first = TRUE, first_non_hidden = TRUE;
405 GArray *sps;
406 MonoSymSeqPoint sp;
407 GPtrArray *sfiles = NULL;
408 GPtrArray *sindexes = NULL;
410 if (source_file)
411 *source_file = NULL;
412 if (source_file_list)
413 *source_file_list = NULL;
414 if (source_files)
415 *source_files = NULL;
416 if (seq_points)
417 *seq_points = NULL;
418 if (n_seq_points)
419 *n_seq_points = 0;
421 if (source_file_list)
422 *source_file_list = sfiles = g_ptr_array_new ();
423 if (source_files)
424 sindexes = g_ptr_array_new ();
426 if (!method->token)
427 return;
429 method_idx = mono_metadata_token_index (method->token);
431 MonoTableInfo *methodbody_table = &tables [MONO_TABLE_METHODBODY];
432 if (G_UNLIKELY (method_idx - 1 >= methodbody_table->rows)) {
433 char *method_name = mono_method_full_name (method, FALSE);
434 g_error ("Method idx %d is greater than number of rows (%d) in PPDB MethodDebugInformation table, for method %s in '%s'. Likely a malformed PDB file.",
435 method_idx - 1, methodbody_table->rows, method_name, image->name);
436 g_free (method_name);
438 mono_metadata_decode_row (methodbody_table, method_idx - 1, cols, MONO_METHODBODY_SIZE);
440 docidx = cols [MONO_METHODBODY_DOCUMENT];
442 if (!cols [MONO_METHODBODY_SEQ_POINTS])
443 return;
445 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
446 size = mono_metadata_decode_blob_size (ptr, &ptr);
447 end = ptr + size;
449 sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
451 /* Header */
452 /* LocalSignature */
453 mono_metadata_decode_value (ptr, &ptr);
454 if (docidx == 0)
455 docidx = mono_metadata_decode_value (ptr, &ptr);
456 docinfo = get_docinfo (ppdb, image, docidx);
458 if (sfiles)
459 g_ptr_array_add (sfiles, docinfo);
461 if (source_file)
462 *source_file = g_strdup (docinfo->source_file);
464 iloffset = 0;
465 start_line = 0;
466 start_col = 0;
467 while (ptr < end) {
468 delta_il = mono_metadata_decode_value (ptr, &ptr);
469 if (!first && delta_il == 0) {
470 /* subsequent-document-record */
471 docidx = mono_metadata_decode_value (ptr, &ptr);
472 docinfo = get_docinfo (ppdb, image, docidx);
473 if (sfiles)
474 g_ptr_array_add (sfiles, docinfo);
475 continue;
477 iloffset += delta_il;
478 first = FALSE;
480 delta_lines = mono_metadata_decode_value (ptr, &ptr);
481 if (delta_lines == 0)
482 delta_cols = mono_metadata_decode_value (ptr, &ptr);
483 else
484 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
486 if (delta_lines == 0 && delta_cols == 0) {
487 /* Hidden sequence point */
488 continue;
491 if (first_non_hidden) {
492 start_line = mono_metadata_decode_value (ptr, &ptr);
493 start_col = mono_metadata_decode_value (ptr, &ptr);
494 } else {
495 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
496 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
497 start_line += adv_line;
498 start_col += adv_col;
500 first_non_hidden = FALSE;
502 memset (&sp, 0, sizeof (sp));
503 sp.il_offset = iloffset;
504 sp.line = start_line;
505 sp.column = start_col;
506 sp.end_line = start_line + delta_lines;
507 sp.end_column = start_col + delta_cols;
509 g_array_append_val (sps, sp);
510 if (source_files)
511 g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
514 if (n_seq_points) {
515 *n_seq_points = sps->len;
516 g_assert (seq_points);
517 *seq_points = g_new (MonoSymSeqPoint, sps->len);
518 memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
521 if (source_files) {
522 *source_files = g_new (int, sps->len);
523 for (i = 0; i < sps->len; ++i)
524 (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
525 g_ptr_array_free (sindexes, TRUE);
528 g_array_free (sps, TRUE);
531 MonoDebugLocalsInfo*
532 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
534 MonoPPDBFile *ppdb = minfo->handle->ppdb;
535 MonoImage *image = ppdb->image;
536 MonoTableInfo *tables = image->tables;
537 MonoMethod *method = minfo->method;
538 guint32 cols [MONO_LOCALSCOPE_SIZE];
539 guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
540 int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
541 MonoDebugLocalsInfo *res;
542 MonoMethodSignature *sig;
544 if (!method->token)
545 return NULL;
547 sig = mono_method_signature_internal (method);
548 if (!sig)
549 return NULL;
551 method_idx = mono_metadata_token_index (method->token);
553 start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
555 if (!start_scope_idx)
556 return NULL;
558 /* Compute number of locals and scopes */
559 scope_idx = start_scope_idx;
560 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
561 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
563 // https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
565 // The variableList attribute in the pdb metadata table is a contiguous array that starts at a
566 // given offset (locals_idx) above and
568 // """
569 // continues to the smaller of:
571 // the last row of the LocalVariable table
572 // the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
573 // """
574 // this endpoint becomes locals_end_idx below
576 // March to the last scope that is in this method
577 while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) {
578 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
579 if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
580 break;
581 scope_idx ++;
583 // The number of scopes is the difference in the indices
584 // for the first and last scopes
585 nscopes = scope_idx - start_scope_idx;
587 // Ends with "the last row of the LocalVariable table"
588 // this happens if the above loop marched one past the end
589 // of the rows
590 if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) {
591 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
592 } else {
593 // Ends with "the next run of LocalVariables,
594 // found by inspecting the VariableList of the next row in this LocalScope table."
595 locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
598 res = g_new0 (MonoDebugLocalsInfo, 1);
599 res->num_blocks = nscopes;
600 res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
601 res->num_locals = locals_end_idx - locals_idx;
602 res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
604 lindex = 0;
605 for (sindex = 0; sindex < nscopes; ++sindex) {
606 scope_idx = start_scope_idx + sindex;
607 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
609 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
610 if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
611 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
612 } else {
613 locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
616 res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
617 res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
619 //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);
621 for (i = locals_idx; i < locals_end_idx; ++i) {
622 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
624 res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
625 res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
626 res->locals [lindex].block = &res->code_blocks [sindex];
627 lindex ++;
629 //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
633 return res;
637 * We use this to pass context information to the row locator
639 typedef struct {
640 int idx; /* The index that we are trying to locate */
641 int col_idx; /* The index in the row where idx may be stored */
642 MonoTableInfo *t; /* pointer to the table */
643 guint32 result;
644 } locator_t;
646 static int
647 table_locator (const void *a, const void *b)
649 locator_t *loc = (locator_t *)a;
650 const char *bb = (const char *)b;
651 guint32 table_index = (bb - loc->t->base) / loc->t->row_size;
652 guint32 col;
654 col = mono_metadata_decode_row_col(loc->t, table_index, loc->col_idx);
656 if (loc->idx == col) {
657 loc->result = table_index;
658 return 0;
660 if (loc->idx < col)
661 return -1;
662 else
663 return 1;
666 static gboolean
667 compare_guid (guint8* guid1, guint8* guid2)
669 for (int i = 0; i < 16; i++) {
670 if (guid1 [i] != guid2 [i])
671 return FALSE;
673 return TRUE;
676 // for parent_type see HasCustomDebugInformation table at
677 // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md
678 static const char*
679 lookup_custom_debug_information (MonoImage* image, guint32 token, uint8_t parent_type, guint8* guid)
681 MonoTableInfo *tables = image->tables;
682 MonoTableInfo *table = &tables[MONO_TABLE_CUSTOMDEBUGINFORMATION];
683 locator_t loc;
685 if (!table->base)
686 return 0;
688 loc.idx = (mono_metadata_token_index (token) << MONO_HAS_CUSTOM_DEBUG_BITS) | parent_type;
689 loc.col_idx = MONO_CUSTOMDEBUGINFORMATION_PARENT;
690 loc.t = table;
692 if (!mono_binary_search (&loc, table->base, table->rows, table->row_size, table_locator))
693 return NULL;
694 // Great we found one of possibly many CustomDebugInformations of this entity they are distinguished by KIND guid
695 // First try on this index found by binary search...(it's most likeley to be only one and binary search found the one we want)
696 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_KIND))))
697 return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_VALUE));
699 // Move forward from binary found index, until parent token differs
700 for (int i = loc.result + 1; i < table->rows; i++)
702 if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
703 break;
704 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
705 return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
708 // Move backward from binary found index, until parent token differs
709 for (int i = loc.result - 1; i >= 0; i--) {
710 if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
711 break;
712 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
713 return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
715 return NULL;
718 MonoDebugMethodAsyncInfo*
719 mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo)
721 MonoMethod *method = minfo->method;
722 MonoPPDBFile *ppdb = minfo->handle->ppdb;
723 MonoImage *image = ppdb->image;
725 // Guid is taken from Roslyn source code:
726 // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Dependencies/CodeAnalysis.Metadata/PortableCustomDebugInfoKinds.cs#L9
727 guint8 async_method_stepping_information_guid [16] = { 0xC5, 0x2A, 0xFD, 0x54, 0x25, 0xE9, 0x1A, 0x40, 0x9C, 0x2A, 0xF9, 0x4F, 0x17, 0x10, 0x72, 0xF8 };
728 char const *blob = lookup_custom_debug_information (image, method->token, MONO_HAS_CUSTOM_DEBUG_METHODDEF, async_method_stepping_information_guid);
729 if (!blob)
730 return NULL;
731 int blob_len = mono_metadata_decode_blob_size (blob, &blob);
732 MonoDebugMethodAsyncInfo* res = g_new0 (MonoDebugMethodAsyncInfo, 1);
733 char const *pointer = blob;
735 // Format of this blob is taken from Roslyn source code:
736 // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L566
738 pointer += 4;//catch_handler_offset
739 while (pointer - blob < blob_len) {
740 res->num_awaits++;
741 pointer += 8;//yield_offsets+resume_offsets
742 mono_metadata_decode_value (pointer, &pointer);//move_next_method_token
744 g_assert(pointer - blob == blob_len); //Check that we used all blob data
745 pointer = blob; //reset pointer after we figured num_awaits
747 res->yield_offsets = g_new (uint32_t, res->num_awaits);
748 res->resume_offsets = g_new (uint32_t, res->num_awaits);
749 res->move_next_method_token = g_new (uint32_t, res->num_awaits);
751 res->catch_handler_offset = read32 (pointer); pointer += 4;
752 for (int i = 0; i < res->num_awaits; i++) {
753 res->yield_offsets [i] = read32 (pointer); pointer += 4;
754 res->resume_offsets [i] = read32 (pointer); pointer += 4;
755 res->move_next_method_token [i] = mono_metadata_decode_value (pointer, &pointer);
757 return res;
760 char*
761 mono_ppdb_get_sourcelink (MonoDebugHandle *handle)
763 MonoPPDBFile *ppdb = handle->ppdb;
764 MonoImage *image = ppdb->image;
765 char *res;
767 guint8 sourcelink_guid [16] = { 0x56, 0x05, 0x11, 0xCC, 0x91, 0xA0, 0x38, 0x4D, 0x9F, 0xEC, 0x25, 0xAB, 0x9A, 0x35, 0x1A, 0x6A };
768 /* The module table only has 1 row */
769 char const *blob = lookup_custom_debug_information (image, 1, MONO_HAS_CUSTOM_DEBUG_MODULE, sourcelink_guid);
770 if (!blob)
771 return NULL;
772 int blob_len = mono_metadata_decode_blob_size (blob, &blob);
773 res = g_malloc (blob_len + 1);
774 memcpy (res, blob, blob_len);
775 res [blob_len] = '\0';
776 return res;