2
// Mono.CSharp.Debugger/MonoSymbolTable.cs
5
// Martin Baulig (martin@ximian.com)
7
// (C) 2002 Ximian, Inc. http://www.ximian.com
11
// Permission is hereby granted, free of charge, to any person obtaining
12
// a copy of this software and associated documentation files (the
13
// "Software"), to deal in the Software without restriction, including
14
// without limitation the rights to use, copy, modify, merge, publish,
15
// distribute, sublicense, and/or sell copies of the Software, and to
16
// permit persons to whom the Software is furnished to do so, subject to
17
// the following conditions:
19
// The above copyright notice and this permission notice shall be
20
// included in all copies or substantial portions of the Software.
22
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
using System.Collections;
37
// Parts which are actually written into the symbol file are marked with
39
// #region This is actually written to the symbol file
42
// Please do not modify these regions without previously talking to me.
44
// All changes to the file format must be synchronized in several places:
46
// a) The fields in these regions (and their order) must match the actual
47
// contents of the symbol file.
49
// This helps people to understand the symbol file format without reading
50
// too much source code, ie. you look at the appropriate region and then
51
// you know what's actually in the file.
53
// It is also required to help me enforce b).
55
// b) The regions must be kept in sync with the unmanaged code in
56
// mono/metadata/debug-mono-symfile.h
58
// When making changes to the file format, you must also increase two version
61
// i) OffsetTable.Version in this file.
62
// ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h
64
// After doing so, recompile everything, including the debugger. Symbol files
65
// with different versions are incompatible to each other and the debugger and
66
// the runtime enfore this, so you need to recompile all your assemblies after
67
// changing the file format.
70
namespace Mono.CompilerServices.SymbolWriterOld
72
public struct OffsetTable
74
public const int Version = 39;
75
public const long Magic = 0x45e82623fd7fa614;
77
#region This is actually written to the symbol file
78
public int TotalFileSize;
79
public int DataSectionOffset;
80
public int DataSectionSize;
81
public int SourceCount;
82
public int SourceTableOffset;
83
public int SourceTableSize;
84
public int MethodCount;
85
public int MethodTableOffset;
86
public int MethodTableSize;
90
internal OffsetTable (BinaryReader reader)
92
TotalFileSize = reader.ReadInt32 ();
93
DataSectionOffset = reader.ReadInt32 ();
94
DataSectionSize = reader.ReadInt32 ();
95
SourceCount = reader.ReadInt32 ();
96
SourceTableOffset = reader.ReadInt32 ();
97
SourceTableSize = reader.ReadInt32 ();
98
MethodCount = reader.ReadInt32 ();
99
MethodTableOffset = reader.ReadInt32 ();
100
MethodTableSize = reader.ReadInt32 ();
101
TypeCount = reader.ReadInt32 ();
104
internal void Write (BinaryWriter bw)
106
bw.Write (TotalFileSize);
107
bw.Write (DataSectionOffset);
108
bw.Write (DataSectionSize);
109
bw.Write (SourceCount);
110
bw.Write (SourceTableOffset);
111
bw.Write (SourceTableSize);
112
bw.Write (MethodCount);
113
bw.Write (MethodTableOffset);
114
bw.Write (MethodTableSize);
115
bw.Write (TypeCount);
118
public override string ToString ()
120
return String.Format (
121
"OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
122
TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount,
123
SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset,
124
MethodTableSize, TypeCount);
128
public struct LineNumberEntry
130
#region This is actually written to the symbol file
131
public readonly int Row;
132
public readonly int Offset;
135
public LineNumberEntry (int row, int offset)
138
this.Offset = offset;
141
public static LineNumberEntry Null = new LineNumberEntry (0, 0);
143
internal LineNumberEntry (BinaryReader reader)
145
Row = reader.ReadInt32 ();
146
Offset = reader.ReadInt32 ();
149
internal void Write (BinaryWriter bw)
155
private class OffsetComparerClass : IComparer
157
public int Compare (object a, object b)
159
LineNumberEntry l1 = (LineNumberEntry) a;
160
LineNumberEntry l2 = (LineNumberEntry) b;
162
if (l1.Offset < l2.Offset)
164
else if (l1.Offset > l2.Offset)
171
private class RowComparerClass : IComparer
173
public int Compare (object a, object b)
175
LineNumberEntry l1 = (LineNumberEntry) a;
176
LineNumberEntry l2 = (LineNumberEntry) b;
180
else if (l1.Row > l2.Row)
187
public static readonly IComparer OffsetComparer = new OffsetComparerClass ();
188
public static readonly IComparer RowComparer = new RowComparerClass ();
190
public override string ToString ()
192
return String.Format ("[Line {0}:{1}]", Row, Offset);
196
public class LexicalBlockEntry
199
#region This is actually written to the symbol file
200
public int StartOffset;
201
public int EndOffset;
204
public LexicalBlockEntry (int index, int start_offset)
207
this.StartOffset = start_offset;
210
internal LexicalBlockEntry (int index, MyBinaryReader reader)
213
this.StartOffset = reader.ReadInt32 ();
214
this.EndOffset = reader.ReadInt32 ();
217
public void Close (int end_offset)
219
this.EndOffset = end_offset;
222
internal void Write (MyBinaryWriter bw)
224
bw.Write (StartOffset);
225
bw.Write (EndOffset);
228
public override string ToString ()
230
return String.Format ("[LexicalBlock {0}:{1}]", StartOffset, EndOffset);
234
public struct LocalVariableEntry
236
#region This is actually written to the symbol file
237
public readonly int Index;
238
public readonly string Name;
239
public readonly byte[] Signature;
240
public readonly int BlockIndex;
243
public LocalVariableEntry (int Index, string Name, byte[] Signature, int BlockIndex)
247
this.Signature = Signature;
248
this.BlockIndex = BlockIndex;
251
internal LocalVariableEntry (MyBinaryReader reader)
253
Index = reader.ReadLeb128 ();
254
Name = reader.ReadString ();
255
int sig_length = reader.ReadLeb128 ();
256
Signature = reader.ReadBytes (sig_length);
257
BlockIndex = reader.ReadLeb128 ();
260
internal void Write (MonoSymbolFile file, MyBinaryWriter bw)
262
bw.WriteLeb128 (Index);
264
bw.WriteLeb128 ((int) Signature.Length);
265
bw.Write (Signature);
266
bw.WriteLeb128 (BlockIndex);
269
public override string ToString ()
271
return String.Format ("[LocalVariable {0}]", Name);
275
public class SourceFileEntry
277
#region This is actually written to the symbol file
278
public readonly int Index;
283
int NamespaceTableOffset;
289
ArrayList namespaces;
292
public static int Size {
296
public SourceFileEntry (MonoSymbolFile file, string file_name)
299
this.file_name = file_name;
300
this.Index = file.AddSource (this);
303
methods = new ArrayList ();
304
namespaces = new ArrayList ();
307
public void DefineMethod (string name, int token, LocalVariableEntry[] locals,
308
LineNumberEntry[] lines, LexicalBlockEntry[] blocks,
309
int start, int end, int namespace_id)
312
throw new InvalidOperationException ();
314
MethodEntry entry = new MethodEntry (
315
file, this, name, (int) token, locals, lines, blocks,
316
start, end, namespace_id);
319
file.AddMethod (entry);
322
public int DefineNamespace (string name, string[] using_clauses, int parent)
325
throw new InvalidOperationException ();
327
int index = file.GetNextNamespaceIndex ();
328
NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent);
333
internal void WriteData (MyBinaryWriter bw)
335
NameOffset = (int) bw.BaseStream.Position;
336
bw.Write (file_name);
338
ArrayList list = new ArrayList ();
339
foreach (MethodEntry entry in methods)
340
list.Add (entry.Write (file, bw));
344
MethodOffset = (int) bw.BaseStream.Position;
345
foreach (MethodSourceEntry method in list)
348
NamespaceCount = namespaces.Count;
349
NamespaceTableOffset = (int) bw.BaseStream.Position;
350
foreach (NamespaceEntry ns in namespaces)
354
internal void Write (BinaryWriter bw)
358
bw.Write (NamespaceCount);
359
bw.Write (NameOffset);
360
bw.Write (MethodOffset);
361
bw.Write (NamespaceTableOffset);
364
internal SourceFileEntry (MonoSymbolFile file, BinaryReader reader)
368
Index = reader.ReadInt32 ();
369
Count = reader.ReadInt32 ();
370
NamespaceCount = reader.ReadInt32 ();
371
NameOffset = reader.ReadInt32 ();
372
MethodOffset = reader.ReadInt32 ();
373
NamespaceTableOffset = reader.ReadInt32 ();
375
file_name = file.ReadString (NameOffset);
378
public string FileName {
379
get { return file_name; }
382
public MethodSourceEntry[] Methods {
385
throw new InvalidOperationException ();
387
BinaryReader reader = file.BinaryReader;
388
int old_pos = (int) reader.BaseStream.Position;
390
reader.BaseStream.Position = MethodOffset;
391
ArrayList list = new ArrayList ();
392
for (int i = 0; i < Count; i ++)
393
list.Add (new MethodSourceEntry (reader));
394
reader.BaseStream.Position = old_pos;
396
MethodSourceEntry[] retval = new MethodSourceEntry [Count];
397
list.CopyTo (retval, 0);
402
public NamespaceEntry[] Namespaces {
405
throw new InvalidOperationException ();
407
MyBinaryReader reader = file.BinaryReader;
408
int old_pos = (int) reader.BaseStream.Position;
410
reader.BaseStream.Position = NamespaceTableOffset;
411
ArrayList list = new ArrayList ();
412
for (int i = 0; i < NamespaceCount; i ++)
413
list.Add (new NamespaceEntry (file, reader));
414
reader.BaseStream.Position = old_pos;
416
NamespaceEntry[] retval = new NamespaceEntry [list.Count];
417
list.CopyTo (retval, 0);
422
public override string ToString ()
424
return String.Format ("SourceFileEntry ({0}:{1}:{2})",
425
Index, file_name, Count);
429
public struct MethodSourceEntry : IComparable
431
#region This is actually written to the symbol file
432
public readonly int Index;
433
public readonly int FileOffset;
434
public readonly int StartRow;
435
public readonly int EndRow;
438
public MethodSourceEntry (int index, int file_offset, int start, int end)
441
this.FileOffset = file_offset;
442
this.StartRow = start;
446
internal MethodSourceEntry (BinaryReader reader)
448
Index = reader.ReadInt32 ();
449
FileOffset = reader.ReadInt32 ();
450
StartRow = reader.ReadInt32 ();
451
EndRow = reader.ReadInt32 ();
454
public static int Size {
458
internal void Write (BinaryWriter bw)
461
bw.Write (FileOffset);
466
public int CompareTo (object obj)
468
MethodSourceEntry method = (MethodSourceEntry) obj;
470
if (method.StartRow < StartRow)
472
else if (method.StartRow > StartRow)
478
public override string ToString ()
480
return String.Format ("MethodSourceEntry ({0}:{1}:{2}:{3})",
481
Index, FileOffset, StartRow, EndRow);
485
public struct MethodIndexEntry
487
#region This is actually written to the symbol file
488
public readonly int FileOffset;
489
public readonly int Token;
492
public static int Size {
496
public MethodIndexEntry (int offset, int token)
498
this.FileOffset = offset;
502
internal MethodIndexEntry (BinaryReader reader)
504
FileOffset = reader.ReadInt32 ();
505
Token = reader.ReadInt32 ();
508
internal void Write (BinaryWriter bw)
510
bw.Write (FileOffset);
514
public override string ToString ()
516
return String.Format ("MethodIndexEntry ({0}:{1:x})",
521
public class MethodEntry : IComparable
523
#region This is actually written to the symbol file
524
public readonly int SourceFileIndex;
525
public readonly int Token;
526
public readonly int StartRow;
527
public readonly int EndRow;
528
public readonly int NumLocals;
529
public readonly int NumLineNumbers;
530
public readonly int NamespaceID;
531
public readonly bool LocalNamesAmbiguous;
534
int TypeIndexTableOffset;
535
int LocalVariableTableOffset;
536
int LineNumberTableOffset;
537
int NumLexicalBlocks;
538
int LexicalBlockTableOffset;
544
public readonly SourceFileEntry SourceFile;
545
public readonly LineNumberEntry[] LineNumbers;
546
public readonly int[] LocalTypeIndices;
547
public readonly LocalVariableEntry[] Locals;
548
public readonly LexicalBlockEntry[] LexicalBlocks;
550
public readonly MonoSymbolFile SymbolFile;
553
get { return index; }
554
set { index = value; }
557
public static int Size {
561
internal MethodEntry (MonoSymbolFile file, MyBinaryReader reader, int index)
563
this.SymbolFile = file;
565
SourceFileIndex = reader.ReadInt32 ();
566
Token = reader.ReadInt32 ();
567
StartRow = reader.ReadInt32 ();
568
EndRow = reader.ReadInt32 ();
569
NumLocals = reader.ReadInt32 ();
570
NumLineNumbers = reader.ReadInt32 ();
571
NameOffset = reader.ReadInt32 ();
572
TypeIndexTableOffset = reader.ReadInt32 ();
573
LocalVariableTableOffset = reader.ReadInt32 ();
574
LineNumberTableOffset = reader.ReadInt32 ();
575
NumLexicalBlocks = reader.ReadInt32 ();
576
LexicalBlockTableOffset = reader.ReadInt32 ();
577
NamespaceID = reader.ReadInt32 ();
578
LocalNamesAmbiguous = reader.ReadInt32 () != 0;
580
SourceFile = file.GetSourceFile (SourceFileIndex);
582
if (LineNumberTableOffset != 0) {
583
long old_pos = reader.BaseStream.Position;
584
reader.BaseStream.Position = LineNumberTableOffset;
586
LineNumbers = new LineNumberEntry [NumLineNumbers];
588
for (int i = 0; i < NumLineNumbers; i++)
589
LineNumbers [i] = new LineNumberEntry (reader);
591
reader.BaseStream.Position = old_pos;
594
if (LocalVariableTableOffset != 0) {
595
long old_pos = reader.BaseStream.Position;
596
reader.BaseStream.Position = LocalVariableTableOffset;
598
Locals = new LocalVariableEntry [NumLocals];
600
for (int i = 0; i < NumLocals; i++)
601
Locals [i] = new LocalVariableEntry (reader);
603
reader.BaseStream.Position = old_pos;
606
if (TypeIndexTableOffset != 0) {
607
long old_pos = reader.BaseStream.Position;
608
reader.BaseStream.Position = TypeIndexTableOffset;
610
LocalTypeIndices = new int [NumLocals];
612
for (int i = 0; i < NumLocals; i++)
613
LocalTypeIndices [i] = reader.ReadInt32 ();
615
reader.BaseStream.Position = old_pos;
618
if (LexicalBlockTableOffset != 0) {
619
long old_pos = reader.BaseStream.Position;
620
reader.BaseStream.Position = LexicalBlockTableOffset;
622
LexicalBlocks = new LexicalBlockEntry [NumLexicalBlocks];
623
for (int i = 0; i < NumLexicalBlocks; i++)
624
LexicalBlocks [i] = new LexicalBlockEntry (i, reader);
626
reader.BaseStream.Position = old_pos;
630
internal MethodEntry (MonoSymbolFile file, SourceFileEntry source,
631
string name, int token, LocalVariableEntry[] locals,
632
LineNumberEntry[] lines, LexicalBlockEntry[] blocks,
633
int start_row, int end_row, int namespace_id)
635
this.SymbolFile = file;
640
SourceFileIndex = source.Index;
642
StartRow = start_row;
644
NamespaceID = namespace_id;
645
LexicalBlocks = blocks;
646
NumLexicalBlocks = LexicalBlocks != null ? LexicalBlocks.Length : 0;
648
LineNumbers = BuildLineNumberTable (lines);
649
NumLineNumbers = LineNumbers.Length;
651
file.NumLineNumbers += NumLineNumbers;
653
NumLocals = locals != null ? locals.Length : 0;
656
if (NumLocals <= 32) {
657
// Most of the time, the O(n^2) factor is actually
658
// less than the cost of allocating the hash table,
659
// 32 is a rough number obtained through some testing.
661
for (int i = 0; i < NumLocals; i ++) {
662
string nm = locals [i].Name;
664
for (int j = i + 1; j < NumLocals; j ++) {
665
if (locals [j].Name == nm) {
666
LocalNamesAmbiguous = true;
667
goto locals_check_done;
674
Hashtable local_names = new Hashtable ();
675
foreach (LocalVariableEntry local in locals) {
676
if (local_names.Contains (local.Name)) {
677
LocalNamesAmbiguous = true;
680
local_names.Add (local.Name, local);
684
LocalTypeIndices = new int [NumLocals];
685
for (int i = 0; i < NumLocals; i++)
686
LocalTypeIndices [i] = file.GetNextTypeIndex ();
689
static LineNumberEntry [] tmp_buff = new LineNumberEntry [20];
691
// BuildLineNumberTable() eliminates duplicate line numbers and ensures
692
// we aren't going "backwards" since this would counfuse the runtime's
693
// debugging code (and the debugger).
695
// In the line number table, the "offset" field most be strictly
696
// monotonic increasing; that is, the next entry must not have an offset
697
// which is equal to or less than the current one.
699
// The most common case is that our input (ie. the line number table as
700
// we get it from mcs) contains several entries with the same offset
701
// (and different line numbers) - but it may also happen that the offset
702
// is decreasing (this can be considered as an exception, such lines will
703
// simply be discarded).
704
LineNumberEntry[] BuildLineNumberTable (LineNumberEntry[] line_numbers)
707
int last_offset = -1;
710
if (line_numbers == null)
711
return new LineNumberEntry [0];
713
if (tmp_buff.Length < (line_numbers.Length + 1))
714
tmp_buff = new LineNumberEntry [(line_numbers.Length + 1) * 2];
716
for (int i = 0; i < line_numbers.Length; i++) {
717
LineNumberEntry line = line_numbers [i];
719
if (line.Offset > last_offset) {
721
tmp_buff [pos ++] = new LineNumberEntry (last_row, last_offset);
723
last_offset = line.Offset;
724
} else if (line.Row > last_row) {
730
tmp_buff [pos ++] = new LineNumberEntry (last_row, last_offset);
732
LineNumberEntry [] retval = new LineNumberEntry [pos];
733
Array.Copy (tmp_buff, retval, pos);
737
internal MethodSourceEntry Write (MonoSymbolFile file, MyBinaryWriter bw)
740
throw new InvalidOperationException ();
742
NameOffset = (int) bw.BaseStream.Position;
744
TypeIndexTableOffset = (int) bw.BaseStream.Position;
746
for (int i = 0; i < NumLocals; i++)
747
bw.Write (LocalTypeIndices [i]);
749
LocalVariableTableOffset = (int) bw.BaseStream.Position;
750
for (int i = 0; i < NumLocals; i++)
751
Locals [i].Write (file, bw);
752
file.LocalCount += NumLocals;
754
LineNumberTableOffset = (int) bw.BaseStream.Position;
755
for (int i = 0; i < NumLineNumbers; i++)
756
LineNumbers [i].Write (bw);
757
file.LineNumberCount += NumLineNumbers;
759
LexicalBlockTableOffset = (int) bw.BaseStream.Position;
760
for (int i = 0; i < NumLexicalBlocks; i++)
761
LexicalBlocks [i].Write (bw);
762
file_offset = (int) bw.BaseStream.Position;
764
bw.Write (SourceFileIndex);
768
bw.Write (NumLocals);
769
bw.Write (NumLineNumbers);
770
bw.Write (NameOffset);
771
bw.Write (TypeIndexTableOffset);
772
bw.Write (LocalVariableTableOffset);
773
bw.Write (LineNumberTableOffset);
774
bw.Write (NumLexicalBlocks);
775
bw.Write (LexicalBlockTableOffset);
776
bw.Write (NamespaceID);
777
bw.Write (LocalNamesAmbiguous ? 1 : 0);
779
return new MethodSourceEntry (index, file_offset, StartRow, EndRow);
782
internal void WriteIndex (BinaryWriter bw)
784
new MethodIndexEntry (file_offset, Token).Write (bw);
787
public int CompareTo (object obj)
789
MethodEntry method = (MethodEntry) obj;
791
if (method.Token < Token)
793
else if (method.Token > Token)
799
public override string ToString ()
801
return String.Format ("[Method {0}:{1}:{2}:{3}:{4} - {6}:{7} - {5}]",
802
index, Token, SourceFileIndex, StartRow, EndRow,
803
SourceFile, NumLocals, NumLineNumbers);
807
public struct NamespaceEntry
809
#region This is actually written to the symbol file
810
public readonly string Name;
811
public readonly int Index;
812
public readonly int Parent;
813
public readonly string[] UsingClauses;
816
public NamespaceEntry (string name, int index, string[] using_clauses, int parent)
820
this.Parent = parent;
821
this.UsingClauses = using_clauses != null ? using_clauses : new string [0];
824
internal NamespaceEntry (MonoSymbolFile file, MyBinaryReader reader)
826
Name = reader.ReadString ();
827
Index = reader.ReadLeb128 ();
828
Parent = reader.ReadLeb128 ();
830
int count = reader.ReadLeb128 ();
831
UsingClauses = new string [count];
832
for (int i = 0; i < count; i++)
833
UsingClauses [i] = reader.ReadString ();
836
internal void Write (MonoSymbolFile file, MyBinaryWriter bw)
839
bw.WriteLeb128 (Index);
840
bw.WriteLeb128 (Parent);
841
bw.WriteLeb128 (UsingClauses.Length);
842
foreach (string uc in UsingClauses)
846
public override string ToString ()
848
return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent);