Backport 2020-02 enable embedded ppdb. (#19050)
[mono-project.git] / mono / metadata / debug-mono-ppdb.c
blobf89aaf4e380e1b1055e8e063c7b3842a9e458228
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 #if HOST_WIN32
33 #include "../zlib/zlib.h"
34 #elif HAVE_SYS_ZLIB
35 #include <zlib.h>
36 #endif
38 #include "debug-mono-ppdb.h"
40 struct _MonoPPDBFile {
41 MonoImage *image;
42 GHashTable *doc_hash;
43 GHashTable *method_hash;
46 /* IMAGE_DEBUG_DIRECTORY structure */
47 typedef struct
49 gint32 characteristics;
50 gint32 time_date_stamp;
51 gint16 major_version;
52 gint16 minor_version;
53 gint32 type;
54 gint32 size_of_data;
55 gint32 address;
56 gint32 pointer;
57 } ImageDebugDirectory;
59 typedef struct {
60 gint32 signature;
61 guint8 guid [16];
62 gint32 age;
63 } CodeviewDebugDirectory;
65 typedef struct {
66 guint8 guid [20];
67 guint32 entry_point;
68 guint64 referenced_tables;
69 } PdbStreamHeader;
71 typedef enum {
72 DEBUG_DIR_ENTRY_CODEVIEW = 2,
73 DEBUG_DIR_ENTRY_PPDB = 17
74 } DebugDirectoryEntryType;
76 #define EMBEDDED_PPDB_MAGIC 0x4244504d
78 enum {
79 MONO_HAS_CUSTOM_DEBUG_METHODDEF = 0,
80 MONO_HAS_CUSTOM_DEBUG_MODULE = 7,
81 MONO_HAS_CUSTOM_DEBUG_BITS = 5,
82 MONO_HAS_CUSTOM_DEBUG_MASK = 0x1f
85 static gboolean
86 get_pe_debug_info (MonoImage *image, guint8 *out_guid, gint32 *out_age, gint32 *out_timestamp, guint8 **ppdb_data,
87 int *ppdb_uncompressed_size, int *ppdb_compressed_size)
89 MonoPEDirEntry *debug_dir_entry;
90 ImageDebugDirectory *debug_dir;
91 int idx;
92 gboolean guid_found = FALSE;
94 *ppdb_data = NULL;
96 debug_dir_entry = &image->image_info->cli_header.datadir.pe_debug;
97 if (!debug_dir_entry->size)
98 return FALSE;
100 int offset = mono_cli_rva_image_map (image, debug_dir_entry->rva);
101 for (idx = 0; idx < debug_dir_entry->size / sizeof (ImageDebugDirectory); ++idx) {
102 debug_dir = (ImageDebugDirectory*)(image->raw_data + offset) + idx;
103 if (debug_dir->type == DEBUG_DIR_ENTRY_CODEVIEW && debug_dir->major_version == 0x100 && debug_dir->minor_version == 0x504d) {
104 /* This is a 'CODEVIEW' debug directory */
105 CodeviewDebugDirectory *dir = (CodeviewDebugDirectory*)(image->raw_data + debug_dir->pointer);
107 if (dir->signature == 0x53445352) {
108 memcpy (out_guid, dir->guid, 16);
109 *out_age = dir->age;
110 *out_timestamp = debug_dir->time_date_stamp;
111 guid_found = TRUE;
114 if (debug_dir->type == DEBUG_DIR_ENTRY_PPDB && debug_dir->major_version >= 0x100 && debug_dir->minor_version == 0x100) {
115 /* Embedded PPDB blob */
116 /* See src/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEReader.EmbeddedPortablePdb.cs in corefx */
117 guint8 *data = (guint8*)(image->raw_data + debug_dir->pointer);
118 guint32 magic = read32 (data);
119 g_assert (magic == EMBEDDED_PPDB_MAGIC);
120 guint32 size = read32 (data + 4);
121 *ppdb_data = data + 8;
122 *ppdb_uncompressed_size = size;
123 *ppdb_compressed_size = debug_dir->size_of_data - 8;
126 return guid_found;
129 static void
130 doc_free (gpointer key)
132 MonoDebugSourceInfo *info = (MonoDebugSourceInfo *)key;
134 g_free (info->source_file);
135 g_free (info);
138 static MonoPPDBFile*
139 create_ppdb_file (MonoImage *ppdb_image)
141 MonoPPDBFile *ppdb;
143 ppdb = g_new0 (MonoPPDBFile, 1);
144 ppdb->image = ppdb_image;
145 ppdb->doc_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) doc_free);
146 ppdb->method_hash = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free);
147 return ppdb;
150 MonoPPDBFile*
151 mono_ppdb_load_file (MonoImage *image, const guint8 *raw_contents, int size)
153 MonoImage *ppdb_image = NULL;
154 const char *filename;
155 char *s, *ppdb_filename;
156 MonoImageOpenStatus status;
157 guint8 pe_guid [16];
158 gint32 pe_age;
159 gint32 pe_timestamp;
160 guint8 *ppdb_data = NULL;
161 guint8 *to_free = NULL;
162 int ppdb_size = 0, ppdb_compressed_size = 0;
164 if (image->tables [MONO_TABLE_DOCUMENT].rows) {
165 /* Embedded ppdb */
166 mono_image_addref (image);
167 return create_ppdb_file (image);
170 if (!get_pe_debug_info (image, pe_guid, &pe_age, &pe_timestamp, &ppdb_data, &ppdb_size, &ppdb_compressed_size)) {
171 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_ASSEMBLY, "Image '%s' has no debug directory.", image->name);
172 return NULL;
175 #if HOST_WIN32 || HAVE_SYS_ZLIB
176 if (ppdb_data) {
177 /* Embedded PPDB data */
178 /* ppdb_size is the uncompressed size */
179 guint8 *data = g_malloc0 (ppdb_size);
180 z_stream stream;
182 memset (&stream, 0, sizeof (stream));
183 stream.avail_in = ppdb_compressed_size;
184 stream.next_in = ppdb_data;
185 stream.avail_out = ppdb_size;
186 stream.next_out = data;
187 int res = inflateInit2 (&stream, -15);
188 g_assert (res == Z_OK);
189 res = inflate (&stream, Z_NO_FLUSH);
190 g_assert (res == Z_STREAM_END);
192 g_assert (ppdb_size > 4);
193 g_assert (strncmp ((char*)data, "BSJB", 4) == 0);
194 raw_contents = data;
195 size = ppdb_size;
196 to_free = data;
198 #endif
200 MonoAssemblyLoadContext *alc = mono_image_get_alc (image);
201 if (raw_contents) {
202 if (size > 4 && strncmp ((char*)raw_contents, "BSJB", 4) == 0)
203 ppdb_image = mono_image_open_from_data_internal (alc, (char*)raw_contents, size, TRUE, &status, FALSE, TRUE, NULL);
204 } else {
205 /* ppdb files drop the .exe/.dll extension */
206 filename = mono_image_get_filename (image);
207 if (strlen (filename) > 4 && (!strcmp (filename + strlen (filename) - 4, ".exe") || !strcmp (filename + strlen (filename) - 4, ".dll"))) {
208 s = g_strdup (filename);
209 s [strlen (filename) - 4] = '\0';
210 ppdb_filename = g_strdup_printf ("%s.pdb", s);
211 g_free (s);
212 } else {
213 ppdb_filename = g_strdup_printf ("%s.pdb", filename);
216 ppdb_image = mono_image_open_metadata_only (alc, ppdb_filename, &status);
217 g_free (ppdb_filename);
219 g_free (to_free);
220 if (!ppdb_image)
221 return NULL;
224 * Check that the images match.
225 * The same id is stored in the Debug Directory of the PE file, and in the
226 * #Pdb stream in the ppdb file.
228 PdbStreamHeader *pdb_stream = (PdbStreamHeader*)ppdb_image->heap_pdb.data;
230 g_assert (pdb_stream);
232 /* The pdb id is a concentation of the pe guid and the timestamp */
233 if (memcmp (pe_guid, pdb_stream->guid, 16) != 0 || memcmp (&pe_timestamp, pdb_stream->guid + 16, 4) != 0) {
234 g_warning ("Symbol file %s doesn't match image %s", ppdb_image->name,
235 image->name);
236 mono_image_close (ppdb_image);
237 return NULL;
240 return create_ppdb_file (ppdb_image);
243 void
244 mono_ppdb_close (MonoDebugHandle *handle)
246 MonoPPDBFile *ppdb = handle->ppdb;
248 mono_image_close (ppdb->image);
249 g_hash_table_destroy (ppdb->doc_hash);
250 g_hash_table_destroy (ppdb->method_hash);
251 g_free (ppdb);
254 MonoDebugMethodInfo *
255 mono_ppdb_lookup_method (MonoDebugHandle *handle, MonoMethod *method)
257 MonoDebugMethodInfo *minfo;
258 MonoPPDBFile *ppdb = handle->ppdb;
260 if (handle->image != mono_class_get_image (mono_method_get_class (method)))
261 return NULL;
263 mono_debugger_lock ();
265 minfo = (MonoDebugMethodInfo *)g_hash_table_lookup (ppdb->method_hash, method);
266 if (minfo) {
267 mono_debugger_unlock ();
268 return minfo;
271 minfo = g_new0 (MonoDebugMethodInfo, 1);
272 minfo->index = 0;
273 minfo->method = method;
274 minfo->handle = handle;
276 g_hash_table_insert (ppdb->method_hash, method, minfo);
278 mono_debugger_unlock ();
280 return minfo;
283 static MonoDebugSourceInfo*
284 get_docinfo (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
286 MonoTableInfo *tables = image->tables;
287 guint32 cols [MONO_DOCUMENT_SIZE];
288 const char *ptr;
289 const char *start;
290 const char *part_ptr;
291 int size, part_size, partidx, nparts;
292 char sep;
293 GString *s;
294 MonoDebugSourceInfo *res, *cached;
296 mono_debugger_lock ();
297 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
298 mono_debugger_unlock ();
299 if (cached)
300 return cached;
302 mono_metadata_decode_row (&tables [MONO_TABLE_DOCUMENT], docidx-1, cols, MONO_DOCUMENT_SIZE);
304 ptr = mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_NAME]);
305 size = mono_metadata_decode_blob_size (ptr, &ptr);
306 start = ptr;
308 // FIXME: UTF8
309 sep = ptr [0];
310 ptr ++;
312 s = g_string_new ("");
314 nparts = 0;
315 while (ptr < start + size) {
316 partidx = mono_metadata_decode_value (ptr, &ptr);
317 if (nparts)
318 g_string_append_c (s, sep);
319 if (partidx) {
320 part_ptr = mono_metadata_blob_heap (image, partidx);
321 part_size = mono_metadata_decode_blob_size (part_ptr, &part_ptr);
323 // FIXME: UTF8
324 g_string_append_len (s, part_ptr, part_size);
326 nparts ++;
329 res = g_new0 (MonoDebugSourceInfo, 1);
330 res->source_file = g_string_free (s, FALSE);
331 res->guid = NULL;
332 res->hash = (guint8*)mono_metadata_blob_heap (image, cols [MONO_DOCUMENT_HASH]);
334 mono_debugger_lock ();
335 cached = (MonoDebugSourceInfo *)g_hash_table_lookup (ppdb->doc_hash, GUINT_TO_POINTER (docidx));
336 if (!cached) {
337 g_hash_table_insert (ppdb->doc_hash, GUINT_TO_POINTER (docidx), res);
338 } else {
339 doc_free (res);
340 res = cached;
342 mono_debugger_unlock ();
343 return res;
346 static char*
347 get_docname (MonoPPDBFile *ppdb, MonoImage *image, int docidx)
349 MonoDebugSourceInfo *info;
351 info = get_docinfo (ppdb, image, docidx);
352 return g_strdup (info->source_file);
356 * mono_ppdb_lookup_location:
357 * \param minfo A \c MonoDebugMethodInfo which can be retrieved by mono_debug_lookup_method().
358 * \param offset IL offset within the corresponding method's CIL code.
360 * This function is similar to mono_debug_lookup_location(), but we
361 * already looked up the method and also already did the
362 * native address -> IL offset mapping.
364 MonoDebugSourceLocation *
365 mono_ppdb_lookup_location (MonoDebugMethodInfo *minfo, uint32_t offset)
367 MonoPPDBFile *ppdb = minfo->handle->ppdb;
368 MonoImage *image = ppdb->image;
369 MonoMethod *method = minfo->method;
370 MonoTableInfo *tables = image->tables;
371 guint32 cols [MONO_METHODBODY_SIZE];
372 const char *ptr;
373 const char *end;
374 char *docname;
375 int idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
376 gboolean first = TRUE, first_non_hidden = TRUE;
377 MonoDebugSourceLocation *location;
379 if (!method->token)
380 return NULL;
382 idx = mono_metadata_token_index (method->token);
384 mono_metadata_decode_row (&tables [MONO_TABLE_METHODBODY], idx-1, cols, MONO_METHODBODY_SIZE);
386 docidx = cols [MONO_METHODBODY_DOCUMENT];
388 if (!cols [MONO_METHODBODY_SEQ_POINTS])
389 return NULL;
390 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
391 size = mono_metadata_decode_blob_size (ptr, &ptr);
392 end = ptr + size;
394 /* Header */
395 /* LocalSignature */
396 mono_metadata_decode_value (ptr, &ptr);
397 if (docidx == 0)
398 docidx = mono_metadata_decode_value (ptr, &ptr);
399 docname = get_docname (ppdb, image, docidx);
401 iloffset = 0;
402 start_line = 0;
403 start_col = 0;
404 while (ptr < end) {
405 delta_il = mono_metadata_decode_value (ptr, &ptr);
406 if (!first && delta_il == 0) {
407 /* document-record */
408 docidx = mono_metadata_decode_value (ptr, &ptr);
409 docname = get_docname (ppdb, image, docidx);
410 continue;
412 if (!first && iloffset + delta_il > offset)
413 break;
414 iloffset += delta_il;
415 first = FALSE;
417 delta_lines = mono_metadata_decode_value (ptr, &ptr);
418 if (delta_lines == 0)
419 delta_cols = mono_metadata_decode_value (ptr, &ptr);
420 else
421 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
422 if (delta_lines == 0 && delta_cols == 0)
423 /* hidden-sequence-point-record */
424 continue;
425 if (first_non_hidden) {
426 start_line = mono_metadata_decode_value (ptr, &ptr);
427 start_col = mono_metadata_decode_value (ptr, &ptr);
428 } else {
429 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
430 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
431 start_line += adv_line;
432 start_col += adv_col;
434 first_non_hidden = FALSE;
437 location = g_new0 (MonoDebugSourceLocation, 1);
438 if (docname && docname [0])
439 location->source_file = docname;
440 location->row = start_line;
441 location->column = start_col;
442 location->il_offset = iloffset;
444 return location;
447 MonoImage *
448 mono_ppdb_get_image (MonoPPDBFile *ppdb)
450 return ppdb->image;
453 void
454 mono_ppdb_get_seq_points (MonoDebugMethodInfo *minfo, char **source_file, GPtrArray **source_file_list, int **source_files, MonoSymSeqPoint **seq_points, int *n_seq_points)
456 MonoPPDBFile *ppdb = minfo->handle->ppdb;
457 MonoImage *image = ppdb->image;
458 MonoMethod *method = minfo->method;
459 MonoTableInfo *tables = image->tables;
460 guint32 cols [MONO_METHODBODY_SIZE];
461 const char *ptr;
462 const char *end;
463 MonoDebugSourceInfo *docinfo;
464 int i, method_idx, size, docidx, iloffset, delta_il, delta_lines, delta_cols, start_line, start_col, adv_line, adv_col;
465 gboolean first = TRUE, first_non_hidden = TRUE;
466 GArray *sps;
467 MonoSymSeqPoint sp;
468 GPtrArray *sfiles = NULL;
469 GPtrArray *sindexes = NULL;
471 if (source_file)
472 *source_file = NULL;
473 if (source_file_list)
474 *source_file_list = NULL;
475 if (source_files)
476 *source_files = NULL;
477 if (seq_points)
478 *seq_points = NULL;
479 if (n_seq_points)
480 *n_seq_points = 0;
482 if (source_file_list)
483 *source_file_list = sfiles = g_ptr_array_new ();
484 if (source_files)
485 sindexes = g_ptr_array_new ();
487 if (!method->token)
488 return;
490 method_idx = mono_metadata_token_index (method->token);
492 MonoTableInfo *methodbody_table = &tables [MONO_TABLE_METHODBODY];
493 if (G_UNLIKELY (method_idx - 1 >= methodbody_table->rows)) {
494 char *method_name = mono_method_full_name (method, FALSE);
495 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.",
496 method_idx - 1, methodbody_table->rows, method_name, image->name);
497 g_free (method_name);
499 mono_metadata_decode_row (methodbody_table, method_idx - 1, cols, MONO_METHODBODY_SIZE);
501 docidx = cols [MONO_METHODBODY_DOCUMENT];
503 if (!cols [MONO_METHODBODY_SEQ_POINTS])
504 return;
506 ptr = mono_metadata_blob_heap (image, cols [MONO_METHODBODY_SEQ_POINTS]);
507 size = mono_metadata_decode_blob_size (ptr, &ptr);
508 end = ptr + size;
510 sps = g_array_new (FALSE, TRUE, sizeof (MonoSymSeqPoint));
512 /* Header */
513 /* LocalSignature */
514 mono_metadata_decode_value (ptr, &ptr);
515 if (docidx == 0)
516 docidx = mono_metadata_decode_value (ptr, &ptr);
517 docinfo = get_docinfo (ppdb, image, docidx);
519 if (sfiles)
520 g_ptr_array_add (sfiles, docinfo);
522 if (source_file)
523 *source_file = g_strdup (docinfo->source_file);
525 iloffset = 0;
526 start_line = 0;
527 start_col = 0;
528 while (ptr < end) {
529 delta_il = mono_metadata_decode_value (ptr, &ptr);
530 if (!first && delta_il == 0) {
531 /* subsequent-document-record */
532 docidx = mono_metadata_decode_value (ptr, &ptr);
533 docinfo = get_docinfo (ppdb, image, docidx);
534 if (sfiles)
535 g_ptr_array_add (sfiles, docinfo);
536 continue;
538 iloffset += delta_il;
539 first = FALSE;
541 delta_lines = mono_metadata_decode_value (ptr, &ptr);
542 if (delta_lines == 0)
543 delta_cols = mono_metadata_decode_value (ptr, &ptr);
544 else
545 delta_cols = mono_metadata_decode_signed_value (ptr, &ptr);
547 if (delta_lines == 0 && delta_cols == 0) {
548 /* Hidden sequence point */
549 continue;
552 if (first_non_hidden) {
553 start_line = mono_metadata_decode_value (ptr, &ptr);
554 start_col = mono_metadata_decode_value (ptr, &ptr);
555 } else {
556 adv_line = mono_metadata_decode_signed_value (ptr, &ptr);
557 adv_col = mono_metadata_decode_signed_value (ptr, &ptr);
558 start_line += adv_line;
559 start_col += adv_col;
561 first_non_hidden = FALSE;
563 memset (&sp, 0, sizeof (sp));
564 sp.il_offset = iloffset;
565 sp.line = start_line;
566 sp.column = start_col;
567 sp.end_line = start_line + delta_lines;
568 sp.end_column = start_col + delta_cols;
570 g_array_append_val (sps, sp);
571 if (source_files)
572 g_ptr_array_add (sindexes, GUINT_TO_POINTER (sfiles->len - 1));
575 if (n_seq_points) {
576 *n_seq_points = sps->len;
577 g_assert (seq_points);
578 *seq_points = g_new (MonoSymSeqPoint, sps->len);
579 memcpy (*seq_points, sps->data, sps->len * sizeof (MonoSymSeqPoint));
582 if (source_files) {
583 *source_files = g_new (int, sps->len);
584 for (i = 0; i < sps->len; ++i)
585 (*source_files)[i] = GPOINTER_TO_INT (g_ptr_array_index (sindexes, i));
586 g_ptr_array_free (sindexes, TRUE);
589 g_array_free (sps, TRUE);
592 MonoDebugLocalsInfo*
593 mono_ppdb_lookup_locals (MonoDebugMethodInfo *minfo)
595 MonoPPDBFile *ppdb = minfo->handle->ppdb;
596 MonoImage *image = ppdb->image;
597 MonoTableInfo *tables = image->tables;
598 MonoMethod *method = minfo->method;
599 guint32 cols [MONO_LOCALSCOPE_SIZE];
600 guint32 locals_cols [MONO_LOCALVARIABLE_SIZE];
601 int i, lindex, sindex, method_idx, start_scope_idx, scope_idx, locals_idx, locals_end_idx, nscopes;
602 MonoDebugLocalsInfo *res;
603 MonoMethodSignature *sig;
605 if (!method->token)
606 return NULL;
608 sig = mono_method_signature_internal (method);
609 if (!sig)
610 return NULL;
612 method_idx = mono_metadata_token_index (method->token);
614 start_scope_idx = mono_metadata_localscope_from_methoddef (image, method_idx);
616 if (!start_scope_idx)
617 return NULL;
619 /* Compute number of locals and scopes */
620 scope_idx = start_scope_idx;
621 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
622 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
624 // https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
626 // The variableList attribute in the pdb metadata table is a contiguous array that starts at a
627 // given offset (locals_idx) above and
629 // """
630 // continues to the smaller of:
632 // the last row of the LocalVariable table
633 // the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
634 // """
635 // this endpoint becomes locals_end_idx below
637 // March to the last scope that is in this method
638 while (scope_idx <= tables [MONO_TABLE_LOCALSCOPE].rows) {
639 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
640 if (cols [MONO_LOCALSCOPE_METHOD] != method_idx)
641 break;
642 scope_idx ++;
644 // The number of scopes is the difference in the indices
645 // for the first and last scopes
646 nscopes = scope_idx - start_scope_idx;
648 // Ends with "the last row of the LocalVariable table"
649 // this happens if the above loop marched one past the end
650 // of the rows
651 if (scope_idx > tables [MONO_TABLE_LOCALSCOPE].rows) {
652 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
653 } else {
654 // Ends with "the next run of LocalVariables,
655 // found by inspecting the VariableList of the next row in this LocalScope table."
656 locals_end_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
659 res = g_new0 (MonoDebugLocalsInfo, 1);
660 res->num_blocks = nscopes;
661 res->code_blocks = g_new0 (MonoDebugCodeBlock, res->num_blocks);
662 res->num_locals = locals_end_idx - locals_idx;
663 res->locals = g_new0 (MonoDebugLocalVar, res->num_locals);
665 lindex = 0;
666 for (sindex = 0; sindex < nscopes; ++sindex) {
667 scope_idx = start_scope_idx + sindex;
668 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1, cols, MONO_LOCALSCOPE_SIZE);
670 locals_idx = cols [MONO_LOCALSCOPE_VARIABLELIST];
671 if (scope_idx == tables [MONO_TABLE_LOCALSCOPE].rows) {
672 locals_end_idx = tables [MONO_TABLE_LOCALVARIABLE].rows + 1;
673 } else {
674 locals_end_idx = mono_metadata_decode_row_col (&tables [MONO_TABLE_LOCALSCOPE], scope_idx-1 + 1, MONO_LOCALSCOPE_VARIABLELIST);
677 res->code_blocks [sindex].start_offset = cols [MONO_LOCALSCOPE_STARTOFFSET];
678 res->code_blocks [sindex].end_offset = cols [MONO_LOCALSCOPE_STARTOFFSET] + cols [MONO_LOCALSCOPE_LENGTH];
680 //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);
682 for (i = locals_idx; i < locals_end_idx; ++i) {
683 mono_metadata_decode_row (&tables [MONO_TABLE_LOCALVARIABLE], i - 1, locals_cols, MONO_LOCALVARIABLE_SIZE);
685 res->locals [lindex].name = g_strdup (mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]));
686 res->locals [lindex].index = locals_cols [MONO_LOCALVARIABLE_INDEX];
687 res->locals [lindex].block = &res->code_blocks [sindex];
688 lindex ++;
690 //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
694 return res;
698 * We use this to pass context information to the row locator
700 typedef struct {
701 int idx; /* The index that we are trying to locate */
702 int col_idx; /* The index in the row where idx may be stored */
703 MonoTableInfo *t; /* pointer to the table */
704 guint32 result;
705 } locator_t;
707 static int
708 table_locator (const void *a, const void *b)
710 locator_t *loc = (locator_t *)a;
711 const char *bb = (const char *)b;
712 guint32 table_index = (bb - loc->t->base) / loc->t->row_size;
713 guint32 col;
715 col = mono_metadata_decode_row_col(loc->t, table_index, loc->col_idx);
717 if (loc->idx == col) {
718 loc->result = table_index;
719 return 0;
721 if (loc->idx < col)
722 return -1;
723 else
724 return 1;
727 static gboolean
728 compare_guid (guint8* guid1, guint8* guid2)
730 for (int i = 0; i < 16; i++) {
731 if (guid1 [i] != guid2 [i])
732 return FALSE;
734 return TRUE;
737 // for parent_type see HasCustomDebugInformation table at
738 // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md
739 static const char*
740 lookup_custom_debug_information (MonoImage* image, guint32 token, uint8_t parent_type, guint8* guid)
742 MonoTableInfo *tables = image->tables;
743 MonoTableInfo *table = &tables[MONO_TABLE_CUSTOMDEBUGINFORMATION];
744 locator_t loc;
746 if (!table->base)
747 return 0;
749 loc.idx = (mono_metadata_token_index (token) << MONO_HAS_CUSTOM_DEBUG_BITS) | parent_type;
750 loc.col_idx = MONO_CUSTOMDEBUGINFORMATION_PARENT;
751 loc.t = table;
753 if (!mono_binary_search (&loc, table->base, table->rows, table->row_size, table_locator))
754 return NULL;
755 // Great we found one of possibly many CustomDebugInformations of this entity they are distinguished by KIND guid
756 // 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)
757 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_KIND))))
758 return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, loc.result, MONO_CUSTOMDEBUGINFORMATION_VALUE));
760 // Move forward from binary found index, until parent token differs
761 for (int i = loc.result + 1; i < table->rows; i++)
763 if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
764 break;
765 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
766 return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
769 // Move backward from binary found index, until parent token differs
770 for (int i = loc.result - 1; i >= 0; i--) {
771 if (mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_PARENT) != loc.idx)
772 break;
773 if (compare_guid (guid, (guint8*)mono_metadata_guid_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_KIND))))
774 return mono_metadata_blob_heap (image, mono_metadata_decode_row_col (table, i, MONO_CUSTOMDEBUGINFORMATION_VALUE));
776 return NULL;
779 MonoDebugMethodAsyncInfo*
780 mono_ppdb_lookup_method_async_debug_info (MonoDebugMethodInfo *minfo)
782 MonoMethod *method = minfo->method;
783 MonoPPDBFile *ppdb = minfo->handle->ppdb;
784 MonoImage *image = ppdb->image;
786 // Guid is taken from Roslyn source code:
787 // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Dependencies/CodeAnalysis.Metadata/PortableCustomDebugInfoKinds.cs#L9
788 guint8 async_method_stepping_information_guid [16] = { 0xC5, 0x2A, 0xFD, 0x54, 0x25, 0xE9, 0x1A, 0x40, 0x9C, 0x2A, 0xF9, 0x4F, 0x17, 0x10, 0x72, 0xF8 };
789 char const *blob = lookup_custom_debug_information (image, method->token, MONO_HAS_CUSTOM_DEBUG_METHODDEF, async_method_stepping_information_guid);
790 if (!blob)
791 return NULL;
792 int blob_len = mono_metadata_decode_blob_size (blob, &blob);
793 MonoDebugMethodAsyncInfo* res = g_new0 (MonoDebugMethodAsyncInfo, 1);
794 char const *pointer = blob;
796 // Format of this blob is taken from Roslyn source code:
797 // https://github.com/dotnet/roslyn/blob/1ad4b58/src/Compilers/Core/Portable/PEWriter/MetadataWriter.PortablePdb.cs#L566
799 pointer += 4;//catch_handler_offset
800 while (pointer - blob < blob_len) {
801 res->num_awaits++;
802 pointer += 8;//yield_offsets+resume_offsets
803 mono_metadata_decode_value (pointer, &pointer);//move_next_method_token
805 g_assert(pointer - blob == blob_len); //Check that we used all blob data
806 pointer = blob; //reset pointer after we figured num_awaits
808 res->yield_offsets = g_new (uint32_t, res->num_awaits);
809 res->resume_offsets = g_new (uint32_t, res->num_awaits);
810 res->move_next_method_token = g_new (uint32_t, res->num_awaits);
812 res->catch_handler_offset = read32 (pointer); pointer += 4;
813 for (int i = 0; i < res->num_awaits; i++) {
814 res->yield_offsets [i] = read32 (pointer); pointer += 4;
815 res->resume_offsets [i] = read32 (pointer); pointer += 4;
816 res->move_next_method_token [i] = mono_metadata_decode_value (pointer, &pointer);
818 return res;
821 char*
822 mono_ppdb_get_sourcelink (MonoDebugHandle *handle)
824 MonoPPDBFile *ppdb = handle->ppdb;
825 MonoImage *image = ppdb->image;
826 char *res;
828 guint8 sourcelink_guid [16] = { 0x56, 0x05, 0x11, 0xCC, 0x91, 0xA0, 0x38, 0x4D, 0x9F, 0xEC, 0x25, 0xAB, 0x9A, 0x35, 0x1A, 0x6A };
829 /* The module table only has 1 row */
830 char const *blob = lookup_custom_debug_information (image, 1, MONO_HAS_CUSTOM_DEBUG_MODULE, sourcelink_guid);
831 if (!blob)
832 return NULL;
833 int blob_len = mono_metadata_decode_blob_size (blob, &blob);
834 res = g_malloc (blob_len + 1);
835 memcpy (res, blob, blob_len);
836 res [blob_len] = '\0';
837 return res;