3 * Support for the portable PDB symbol
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.
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>
33 #include "../zlib/zlib.h"
38 #include "debug-mono-ppdb.h"
40 struct _MonoPPDBFile
{
43 GHashTable
*method_hash
;
46 /* IMAGE_DEBUG_DIRECTORY structure */
49 gint32 characteristics
;
50 gint32 time_date_stamp
;
57 } ImageDebugDirectory
;
63 } CodeviewDebugDirectory
;
68 guint64 referenced_tables
;
72 DEBUG_DIR_ENTRY_CODEVIEW
= 2,
73 DEBUG_DIR_ENTRY_PPDB
= 17
74 } DebugDirectoryEntryType
;
76 #define EMBEDDED_PPDB_MAGIC 0x4244504d
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
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
;
92 gboolean guid_found
= FALSE
;
96 debug_dir_entry
= &image
->image_info
->cli_header
.datadir
.pe_debug
;
97 if (!debug_dir_entry
->size
)
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);
110 *out_timestamp
= debug_dir
->time_date_stamp
;
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;
130 doc_free (gpointer key
)
132 MonoDebugSourceInfo
*info
= (MonoDebugSourceInfo
*)key
;
134 g_free (info
->source_file
);
139 create_ppdb_file (MonoImage
*ppdb_image
)
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
);
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
;
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
) {
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
);
175 #if HOST_WIN32 || HAVE_SYS_ZLIB
177 /* Embedded PPDB data */
178 /* ppdb_size is the uncompressed size */
179 guint8
*data
= g_malloc0 (ppdb_size
);
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);
200 MonoAssemblyLoadContext
*alc
= mono_image_get_alc (image
);
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
);
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
);
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
);
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
,
236 mono_image_close (ppdb_image
);
240 return create_ppdb_file (ppdb_image
);
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
);
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
)))
263 mono_debugger_lock ();
265 minfo
= (MonoDebugMethodInfo
*)g_hash_table_lookup (ppdb
->method_hash
, method
);
267 mono_debugger_unlock ();
271 minfo
= g_new0 (MonoDebugMethodInfo
, 1);
273 minfo
->method
= method
;
274 minfo
->handle
= handle
;
276 g_hash_table_insert (ppdb
->method_hash
, method
, minfo
);
278 mono_debugger_unlock ();
283 static MonoDebugSourceInfo
*
284 get_docinfo (MonoPPDBFile
*ppdb
, MonoImage
*image
, int docidx
)
286 MonoTableInfo
*tables
= image
->tables
;
287 guint32 cols
[MONO_DOCUMENT_SIZE
];
290 const char *part_ptr
;
291 int size
, part_size
, partidx
, nparts
;
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 ();
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
);
312 s
= g_string_new ("");
315 while (ptr
< start
+ size
) {
316 partidx
= mono_metadata_decode_value (ptr
, &ptr
);
318 g_string_append_c (s
, sep
);
320 part_ptr
= mono_metadata_blob_heap (image
, partidx
);
321 part_size
= mono_metadata_decode_blob_size (part_ptr
, &part_ptr
);
324 g_string_append_len (s
, part_ptr
, part_size
);
329 res
= g_new0 (MonoDebugSourceInfo
, 1);
330 res
->source_file
= g_string_free (s
, FALSE
);
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
));
337 g_hash_table_insert (ppdb
->doc_hash
, GUINT_TO_POINTER (docidx
), res
);
342 mono_debugger_unlock ();
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
];
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
;
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
])
390 ptr
= mono_metadata_blob_heap (image
, cols
[MONO_METHODBODY_SEQ_POINTS
]);
391 size
= mono_metadata_decode_blob_size (ptr
, &ptr
);
396 mono_metadata_decode_value (ptr
, &ptr
);
398 docidx
= mono_metadata_decode_value (ptr
, &ptr
);
399 docname
= get_docname (ppdb
, image
, docidx
);
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
);
412 if (!first
&& iloffset
+ delta_il
> offset
)
414 iloffset
+= delta_il
;
417 delta_lines
= mono_metadata_decode_value (ptr
, &ptr
);
418 if (delta_lines
== 0)
419 delta_cols
= mono_metadata_decode_value (ptr
, &ptr
);
421 delta_cols
= mono_metadata_decode_signed_value (ptr
, &ptr
);
422 if (delta_lines
== 0 && delta_cols
== 0)
423 /* hidden-sequence-point-record */
425 if (first_non_hidden
) {
426 start_line
= mono_metadata_decode_value (ptr
, &ptr
);
427 start_col
= mono_metadata_decode_value (ptr
, &ptr
);
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
;
448 mono_ppdb_get_image (MonoPPDBFile
*ppdb
)
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
];
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
;
468 GPtrArray
*sfiles
= NULL
;
469 GPtrArray
*sindexes
= NULL
;
473 if (source_file_list
)
474 *source_file_list
= NULL
;
476 *source_files
= NULL
;
482 if (source_file_list
)
483 *source_file_list
= sfiles
= g_ptr_array_new ();
485 sindexes
= g_ptr_array_new ();
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
])
506 ptr
= mono_metadata_blob_heap (image
, cols
[MONO_METHODBODY_SEQ_POINTS
]);
507 size
= mono_metadata_decode_blob_size (ptr
, &ptr
);
510 sps
= g_array_new (FALSE
, TRUE
, sizeof (MonoSymSeqPoint
));
514 mono_metadata_decode_value (ptr
, &ptr
);
516 docidx
= mono_metadata_decode_value (ptr
, &ptr
);
517 docinfo
= get_docinfo (ppdb
, image
, docidx
);
520 g_ptr_array_add (sfiles
, docinfo
);
523 *source_file
= g_strdup (docinfo
->source_file
);
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
);
535 g_ptr_array_add (sfiles
, docinfo
);
538 iloffset
+= delta_il
;
541 delta_lines
= mono_metadata_decode_value (ptr
, &ptr
);
542 if (delta_lines
== 0)
543 delta_cols
= mono_metadata_decode_value (ptr
, &ptr
);
545 delta_cols
= mono_metadata_decode_signed_value (ptr
, &ptr
);
547 if (delta_lines
== 0 && delta_cols
== 0) {
548 /* Hidden sequence point */
552 if (first_non_hidden
) {
553 start_line
= mono_metadata_decode_value (ptr
, &ptr
);
554 start_col
= mono_metadata_decode_value (ptr
, &ptr
);
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
);
572 g_ptr_array_add (sindexes
, GUINT_TO_POINTER (sfiles
->len
- 1));
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
));
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
);
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
;
608 sig
= mono_method_signature_internal (method
);
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
)
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
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.
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
)
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
651 if (scope_idx
> tables
[MONO_TABLE_LOCALSCOPE
].rows
) {
652 locals_end_idx
= tables
[MONO_TABLE_LOCALVARIABLE
].rows
+ 1;
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
);
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;
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
];
690 //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);
698 * We use this to pass context information to the row locator
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 */
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
;
715 col
= mono_metadata_decode_row_col(loc
->t
, table_index
, loc
->col_idx
);
717 if (loc
->idx
== col
) {
718 loc
->result
= table_index
;
728 compare_guid (guint8
* guid1
, guint8
* guid2
)
730 for (int i
= 0; i
< 16; i
++) {
731 if (guid1
[i
] != guid2
[i
])
737 // for parent_type see HasCustomDebugInformation table at
738 // https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md
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
];
749 loc
.idx
= (mono_metadata_token_index (token
) << MONO_HAS_CUSTOM_DEBUG_BITS
) | parent_type
;
750 loc
.col_idx
= MONO_CUSTOMDEBUGINFORMATION_PARENT
;
753 if (!mono_binary_search (&loc
, table
->base
, table
->rows
, table
->row_size
, table_locator
))
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
)
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
)
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
));
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
);
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
) {
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
);
822 mono_ppdb_get_sourcelink (MonoDebugHandle
*handle
)
824 MonoPPDBFile
*ppdb
= handle
->ppdb
;
825 MonoImage
*image
= ppdb
->image
;
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
);
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';