2 * debug-mono-ppdb.c: Support for the portable PDB symbol
7 * Mono Project (http://www.mono-project.com)
9 * Copyright 2015 Xamarin Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
18 #include <mono/metadata/metadata.h>
19 #include <mono/metadata/tabledefs.h>
20 #include <mono/metadata/tokentype.h>
21 #include <mono/metadata/debug-helpers.h>
22 #include <mono/metadata/mono-debug.h>
23 #include <mono/metadata/debug-mono-symfile.h>
24 #include <mono/metadata/mono-debug-debugger.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>
30 #include "debug-mono-ppdb.h"
32 struct _MonoPPDBFile
{
35 GHashTable
*method_hash
;
38 /* IMAGE_DEBUG_DIRECTORY structure */
41 gint32 characteristics
;
42 gint32 time_date_stamp
;
49 } ImageDebugDirectory
;
55 } CodeviewDebugDirectory
;
60 guint64 referenced_tables
;
64 get_pe_debug_guid (MonoImage
*image
, guint8
*out_guid
, gint32
*out_age
, gint32
*out_timestamp
)
66 MonoPEDirEntry
*debug_dir_entry
;
67 ImageDebugDirectory
*debug_dir
;
69 debug_dir_entry
= &((MonoCLIImageInfo
*)image
->image_info
)->cli_header
.datadir
.pe_debug
;
70 if (!debug_dir_entry
->size
)
73 int offset
= mono_cli_rva_image_map (image
, debug_dir_entry
->rva
);
74 debug_dir
= (ImageDebugDirectory
*)(image
->raw_data
+ offset
);
75 if (debug_dir
->type
== 2 && debug_dir
->major_version
== 0x100 && debug_dir
->minor_version
== 0x504d) {
76 /* This is a 'CODEVIEW' debug directory */
77 CodeviewDebugDirectory
*dir
= (CodeviewDebugDirectory
*)(image
->raw_data
+ debug_dir
->pointer
);
79 if (dir
->signature
== 0x53445352) {
80 memcpy (out_guid
, dir
->guid
, 16);
82 *out_timestamp
= debug_dir
->time_date_stamp
;
90 doc_free (gpointer key
)
92 MonoDebugSourceInfo
*info
= (MonoDebugSourceInfo
*)key
;
94 g_free (info
->source_file
);
99 mono_ppdb_load_file (MonoImage
*image
, const guint8
*raw_contents
, int size
)
101 MonoImage
*ppdb_image
= NULL
;
102 const char *filename
;
103 char *s
, *ppdb_filename
;
104 MonoImageOpenStatus status
;
110 if (!get_pe_debug_guid (image
, pe_guid
, &pe_age
, &pe_timestamp
))
114 if (size
> 4 && strncmp ((char*)raw_contents
, "BSJB", 4) == 0)
115 ppdb_image
= mono_image_open_from_data_internal ((char*)raw_contents
, size
, TRUE
, &status
, FALSE
, TRUE
, NULL
);
117 /* ppdb files drop the .exe/.dll extension */
118 filename
= mono_image_get_filename (image
);
119 if (strlen (filename
) > 4 && (!strcmp (filename
+ strlen (filename
) - 4, ".exe") || !strcmp (filename
+ strlen (filename
) - 4, ".dll"))) {
120 s
= g_strdup (filename
);
121 s
[strlen (filename
) - 4] = '\0';
122 ppdb_filename
= g_strdup_printf ("%s.pdb", s
);
125 ppdb_filename
= g_strdup_printf ("%s.pdb", filename
);
128 ppdb_image
= mono_image_open_metadata_only (ppdb_filename
, &status
);
130 g_free (ppdb_filename
);
136 * Check that the images match.
137 * The same id is stored in the Debug Directory of the PE file, and in the
138 * #Pdb stream in the ppdb file.
140 PdbStreamHeader
*pdb_stream
= (PdbStreamHeader
*)ppdb_image
->heap_pdb
.data
;
142 g_assert (pdb_stream
);
144 /* The pdb id is a concentation of the pe guid and the timestamp */
145 if (memcmp (pe_guid
, pdb_stream
->guid
, 16) != 0 || memcmp (&pe_timestamp
, pdb_stream
->guid
+ 16, 4) != 0) {
146 g_warning ("Symbol file %s doesn't match image %s", ppdb_image
->name
,
148 mono_image_close (ppdb_image
);
152 ppdb
= g_new0 (MonoPPDBFile
, 1);
153 ppdb
->image
= ppdb_image
;
154 ppdb
->doc_hash
= g_hash_table_new_full (NULL
, NULL
, NULL
, (GDestroyNotify
) doc_free
);
155 ppdb
->method_hash
= g_hash_table_new_full (NULL
, NULL
, NULL
, (GDestroyNotify
) g_free
);
161 mono_ppdb_close (MonoDebugHandle
*handle
)
163 MonoPPDBFile
*ppdb
= handle
->ppdb
;
165 mono_image_close (ppdb
->image
);
166 g_hash_table_destroy (ppdb
->doc_hash
);
167 g_hash_table_destroy (ppdb
->method_hash
);
171 MonoDebugMethodInfo
*
172 mono_ppdb_lookup_method (MonoDebugHandle
*handle
, MonoMethod
*method
)
174 MonoDebugMethodInfo
*minfo
;
175 MonoPPDBFile
*ppdb
= handle
->ppdb
;
177 if (handle
->image
!= mono_class_get_image (mono_method_get_class (method
)))
180 mono_debugger_lock ();
182 minfo
= (MonoDebugMethodInfo
*)g_hash_table_lookup (ppdb
->method_hash
, method
);
184 mono_debugger_unlock ();
188 minfo
= g_new0 (MonoDebugMethodInfo
, 1);
190 minfo
->method
= method
;
191 minfo
->handle
= handle
;
193 g_hash_table_insert (ppdb
->method_hash
, method
, minfo
);
195 mono_debugger_unlock ();
200 static MonoDebugSourceInfo
*
201 get_docinfo (MonoPPDBFile
*ppdb
, MonoImage
*image
, int docidx
)
203 MonoTableInfo
*tables
= image
->tables
;
204 guint32 cols
[MONO_DOCUMENT_SIZE
];
207 const char *part_ptr
;
208 int size
, part_size
, partidx
, nparts
;
211 MonoDebugSourceInfo
*res
, *cached
;
213 mono_debugger_lock ();
214 cached
= (MonoDebugSourceInfo
*)g_hash_table_lookup (ppdb
->doc_hash
, GUINT_TO_POINTER (docidx
));
215 mono_debugger_unlock ();
219 mono_metadata_decode_row (&tables
[MONO_TABLE_DOCUMENT
], docidx
-1, cols
, MONO_DOCUMENT_SIZE
);
221 ptr
= mono_metadata_blob_heap (image
, cols
[MONO_DOCUMENT_NAME
]);
222 size
= mono_metadata_decode_blob_size (ptr
, &ptr
);
229 s
= g_string_new ("");
232 while (ptr
< start
+ size
) {
233 partidx
= mono_metadata_decode_value (ptr
, &ptr
);
235 g_string_append_c (s
, sep
);
237 part_ptr
= mono_metadata_blob_heap (image
, partidx
);
238 part_size
= mono_metadata_decode_blob_size (part_ptr
, &part_ptr
);
241 g_string_append_len (s
, part_ptr
, part_size
);
246 res
= g_new0 (MonoDebugSourceInfo
, 1);
247 res
->source_file
= g_string_free (s
, FALSE
);
249 res
->hash
= (guint8
*)mono_metadata_blob_heap (image
, cols
[MONO_DOCUMENT_HASH
]);
251 mono_debugger_lock ();
252 cached
= (MonoDebugSourceInfo
*)g_hash_table_lookup (ppdb
->doc_hash
, GUINT_TO_POINTER (docidx
));
254 g_hash_table_insert (ppdb
->doc_hash
, GUINT_TO_POINTER (docidx
), res
);
259 mono_debugger_unlock ();
264 get_docname (MonoPPDBFile
*ppdb
, MonoImage
*image
, int docidx
)
266 MonoDebugSourceInfo
*info
;
268 info
= get_docinfo (ppdb
, image
, docidx
);
269 return g_strdup (info
->source_file
);
273 * mono_ppdb_lookup_location:
274 * @minfo: A `MonoDebugMethodInfo' which can be retrieved by
275 * mono_debug_lookup_method().
276 * @offset: IL offset within the corresponding method's CIL code.
278 * This function is similar to mono_debug_lookup_location(), but we
279 * already looked up the method and also already did the
280 * `native address -> IL offset' mapping.
282 MonoDebugSourceLocation
*
283 mono_ppdb_lookup_location (MonoDebugMethodInfo
*minfo
, uint32_t offset
)
285 MonoPPDBFile
*ppdb
= minfo
->handle
->ppdb
;
286 MonoImage
*image
= ppdb
->image
;
287 MonoMethod
*method
= minfo
->method
;
288 MonoTableInfo
*tables
= image
->tables
;
289 guint32 cols
[MONO_METHODBODY_SIZE
];
293 int idx
, size
, docidx
, iloffset
, delta_il
, delta_lines
, delta_cols
, start_line
, start_col
, adv_line
, adv_col
;
294 gboolean first
= TRUE
, first_non_hidden
= TRUE
;
295 MonoDebugSourceLocation
*location
;
300 idx
= mono_metadata_token_index (method
->token
);
302 mono_metadata_decode_row (&tables
[MONO_TABLE_METHODBODY
], idx
-1, cols
, MONO_METHODBODY_SIZE
);
304 docidx
= cols
[MONO_METHODBODY_DOCUMENT
];
306 if (!cols
[MONO_METHODBODY_SEQ_POINTS
])
308 ptr
= mono_metadata_blob_heap (image
, cols
[MONO_METHODBODY_SEQ_POINTS
]);
309 size
= mono_metadata_decode_blob_size (ptr
, &ptr
);
314 mono_metadata_decode_value (ptr
, &ptr
);
316 docidx
= mono_metadata_decode_value (ptr
, &ptr
);
317 docname
= get_docname (ppdb
, image
, docidx
);
323 delta_il
= mono_metadata_decode_value (ptr
, &ptr
);
324 if (!first
&& delta_il
== 0) {
325 /* document-record */
326 docidx
= mono_metadata_decode_value (ptr
, &ptr
);
327 docname
= get_docname (ppdb
, image
, docidx
);
330 if (!first
&& iloffset
+ delta_il
> offset
)
332 iloffset
+= delta_il
;
335 delta_lines
= mono_metadata_decode_value (ptr
, &ptr
);
336 if (delta_lines
== 0)
337 delta_cols
= mono_metadata_decode_value (ptr
, &ptr
);
339 delta_cols
= mono_metadata_decode_signed_value (ptr
, &ptr
);
340 if (delta_lines
== 0 && delta_cols
== 0)
341 /* hidden-sequence-point-record */
343 if (first_non_hidden
) {
344 start_line
= mono_metadata_decode_value (ptr
, &ptr
);
345 start_col
= mono_metadata_decode_value (ptr
, &ptr
);
347 adv_line
= mono_metadata_decode_signed_value (ptr
, &ptr
);
348 adv_col
= mono_metadata_decode_signed_value (ptr
, &ptr
);
349 start_line
+= adv_line
;
350 start_col
+= adv_col
;
352 first_non_hidden
= FALSE
;
355 location
= g_new0 (MonoDebugSourceLocation
, 1);
356 location
->source_file
= docname
;
357 location
->row
= start_line
;
358 location
->il_offset
= iloffset
;
364 mono_ppdb_get_seq_points (MonoDebugMethodInfo
*minfo
, char **source_file
, GPtrArray
**source_file_list
, int **source_files
, MonoSymSeqPoint
**seq_points
, int *n_seq_points
)
366 MonoPPDBFile
*ppdb
= minfo
->handle
->ppdb
;
367 MonoImage
*image
= ppdb
->image
;
368 MonoMethod
*method
= minfo
->method
;
369 MonoTableInfo
*tables
= image
->tables
;
370 guint32 cols
[MONO_METHODBODY_SIZE
];
373 MonoDebugSourceInfo
*docinfo
;
374 int i
, method_idx
, size
, docidx
, iloffset
, delta_il
, delta_lines
, delta_cols
, start_line
, start_col
, adv_line
, adv_col
;
375 gboolean first
= TRUE
, first_non_hidden
= TRUE
;
378 GPtrArray
*sfiles
= NULL
;
379 GPtrArray
*sindexes
= NULL
;
383 if (source_file_list
)
384 *source_file_list
= NULL
;
386 *source_files
= NULL
;
392 if (source_file_list
)
393 *source_file_list
= sfiles
= g_ptr_array_new ();
395 sindexes
= g_ptr_array_new ();
400 method_idx
= mono_metadata_token_index (method
->token
);
402 mono_metadata_decode_row (&tables
[MONO_TABLE_METHODBODY
], method_idx
-1, cols
, MONO_METHODBODY_SIZE
);
404 docidx
= cols
[MONO_METHODBODY_DOCUMENT
];
406 if (!cols
[MONO_METHODBODY_SEQ_POINTS
])
409 ptr
= mono_metadata_blob_heap (image
, cols
[MONO_METHODBODY_SEQ_POINTS
]);
410 size
= mono_metadata_decode_blob_size (ptr
, &ptr
);
413 sps
= g_array_new (FALSE
, TRUE
, sizeof (MonoSymSeqPoint
));
417 mono_metadata_decode_value (ptr
, &ptr
);
419 docidx
= mono_metadata_decode_value (ptr
, &ptr
);
420 docinfo
= get_docinfo (ppdb
, image
, docidx
);
423 g_ptr_array_add (sfiles
, docinfo
);
429 delta_il
= mono_metadata_decode_value (ptr
, &ptr
);
430 if (!first
&& delta_il
== 0) {
431 /* subsequent-document-record */
432 docidx
= mono_metadata_decode_value (ptr
, &ptr
);
433 docinfo
= get_docinfo (ppdb
, image
, docidx
);
435 g_ptr_array_add (sfiles
, docinfo
);
438 iloffset
+= delta_il
;
441 delta_lines
= mono_metadata_decode_value (ptr
, &ptr
);
442 if (delta_lines
== 0)
443 delta_cols
= mono_metadata_decode_value (ptr
, &ptr
);
445 delta_cols
= mono_metadata_decode_signed_value (ptr
, &ptr
);
447 if (delta_lines
== 0 && delta_cols
== 0) {
448 /* Hidden sequence point */
452 if (first_non_hidden
) {
453 start_line
= mono_metadata_decode_value (ptr
, &ptr
);
454 start_col
= mono_metadata_decode_value (ptr
, &ptr
);
456 adv_line
= mono_metadata_decode_signed_value (ptr
, &ptr
);
457 adv_col
= mono_metadata_decode_signed_value (ptr
, &ptr
);
458 start_line
+= adv_line
;
459 start_col
+= adv_col
;
461 first_non_hidden
= FALSE
;
463 memset (&sp
, 0, sizeof (sp
));
464 sp
.il_offset
= iloffset
;
465 sp
.line
= start_line
;
466 sp
.column
= start_col
;
467 sp
.end_line
= start_line
+ delta_lines
;
468 sp
.end_column
= start_col
+ delta_cols
;
470 g_array_append_val (sps
, sp
);
472 g_ptr_array_add (sindexes
, GUINT_TO_POINTER (sfiles
->len
- 1));
476 *n_seq_points
= sps
->len
;
477 g_assert (seq_points
);
478 *seq_points
= g_new (MonoSymSeqPoint
, sps
->len
);
479 memcpy (*seq_points
, sps
->data
, sps
->len
* sizeof (MonoSymSeqPoint
));
483 *source_file
= g_strdup (((MonoDebugSourceInfo
*)g_ptr_array_index (sfiles
, 0))->source_file
);
485 *source_files
= g_new (int, sps
->len
);
486 for (i
= 0; i
< sps
->len
; ++i
)
487 (*source_files
)[i
] = GPOINTER_TO_INT (g_ptr_array_index (sindexes
, i
));
488 g_ptr_array_free (sindexes
, TRUE
);
491 g_array_free (sps
, TRUE
);
495 mono_ppdb_lookup_locals (MonoDebugMethodInfo
*minfo
)
497 MonoPPDBFile
*ppdb
= minfo
->handle
->ppdb
;
498 MonoImage
*image
= ppdb
->image
;
499 MonoTableInfo
*tables
= image
->tables
;
500 MonoMethod
*method
= minfo
->method
;
501 guint32 cols
[MONO_LOCALSCOPE_SIZE
];
502 guint32 locals_cols
[MONO_LOCALVARIABLE_SIZE
];
503 int i
, lindex
, sindex
, method_idx
, start_scope_idx
, scope_idx
, locals_idx
, locals_end_idx
, nscopes
;
504 MonoDebugLocalsInfo
*res
;
505 MonoMethodSignature
*sig
;
510 sig
= mono_method_signature (method
);
514 method_idx
= mono_metadata_token_index (method
->token
);
516 start_scope_idx
= mono_metadata_localscope_from_methoddef (image
, method_idx
);
518 if (!start_scope_idx
)
521 /* Compute number of locals and scopes */
522 scope_idx
= start_scope_idx
;
523 mono_metadata_decode_row (&tables
[MONO_TABLE_LOCALSCOPE
], scope_idx
-1, cols
, MONO_LOCALSCOPE_SIZE
);
524 locals_idx
= cols
[MONO_LOCALSCOPE_VARIABLELIST
];
526 // https://github.com/dotnet/roslyn/blob/2ae8d5fed96ab3f1164031f9b4ac827f53289159/docs/specs/PortablePdb-Metadata.md#LocalScopeTable
528 // The variableList attribute in the pdb metadata table is a contiguous array that starts at a
529 // given offset (locals_idx) above and
532 // continues to the smaller of:
534 // the last row of the LocalVariable table
535 // the next run of LocalVariables, found by inspecting the VariableList of the next row in this LocalScope table.
537 // this endpoint becomes locals_end_idx below
539 // March to the last scope that is in this method
540 while (scope_idx
<= tables
[MONO_TABLE_LOCALSCOPE
].rows
) {
541 mono_metadata_decode_row (&tables
[MONO_TABLE_LOCALSCOPE
], scope_idx
-1, cols
, MONO_LOCALSCOPE_SIZE
);
542 if (cols
[MONO_LOCALSCOPE_METHOD
] != method_idx
)
546 // The number of scopes is the difference in the indices
547 // for the first and last scopes
548 nscopes
= scope_idx
- start_scope_idx
;
550 // Ends with "the last row of the LocalVariable table"
551 // this happens if the above loop marched one past the end
553 if (scope_idx
> tables
[MONO_TABLE_LOCALSCOPE
].rows
) {
554 locals_end_idx
= tables
[MONO_TABLE_LOCALVARIABLE
].rows
+ 1;
556 // Ends with "the next run of LocalVariables,
557 // found by inspecting the VariableList of the next row in this LocalScope table."
558 locals_end_idx
= cols
[MONO_LOCALSCOPE_VARIABLELIST
];
561 res
= g_new0 (MonoDebugLocalsInfo
, 1);
562 res
->num_blocks
= nscopes
;
563 res
->code_blocks
= g_new0 (MonoDebugCodeBlock
, res
->num_blocks
);
564 res
->num_locals
= locals_end_idx
- locals_idx
;
565 res
->locals
= g_new0 (MonoDebugLocalVar
, res
->num_locals
);
568 for (sindex
= 0; sindex
< nscopes
; ++sindex
) {
569 scope_idx
= start_scope_idx
+ sindex
;
570 mono_metadata_decode_row (&tables
[MONO_TABLE_LOCALSCOPE
], scope_idx
-1, cols
, MONO_LOCALSCOPE_SIZE
);
572 locals_idx
= cols
[MONO_LOCALSCOPE_VARIABLELIST
];
573 if (scope_idx
== tables
[MONO_TABLE_LOCALSCOPE
].rows
) {
574 locals_end_idx
= tables
[MONO_TABLE_LOCALVARIABLE
].rows
+ 1;
576 locals_end_idx
= mono_metadata_decode_row_col (&tables
[MONO_TABLE_LOCALSCOPE
], scope_idx
-1 + 1, MONO_LOCALSCOPE_VARIABLELIST
);
579 res
->code_blocks
[sindex
].start_offset
= cols
[MONO_LOCALSCOPE_STARTOFFSET
];
580 res
->code_blocks
[sindex
].end_offset
= cols
[MONO_LOCALSCOPE_STARTOFFSET
] + cols
[MONO_LOCALSCOPE_LENGTH
];
582 //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);
584 for (i
= locals_idx
; i
< locals_end_idx
; ++i
) {
585 mono_metadata_decode_row (&tables
[MONO_TABLE_LOCALVARIABLE
], i
- 1, locals_cols
, MONO_LOCALVARIABLE_SIZE
);
587 res
->locals
[lindex
].name
= g_strdup (mono_metadata_string_heap (image
, locals_cols
[MONO_LOCALVARIABLE_NAME
]));
588 res
->locals
[lindex
].index
= locals_cols
[MONO_LOCALVARIABLE_INDEX
];
589 res
->locals
[lindex
].block
= &res
->code_blocks
[sindex
];
592 //printf ("\t %s %d\n", mono_metadata_string_heap (image, locals_cols [MONO_LOCALVARIABLE_NAME]), locals_cols [MONO_LOCALVARIABLE_INDEX]);