mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1380 lines
34 KiB
1380 lines
34 KiB
// |
|
// Mono.CSharp.Debugger/MonoSymbolTable.cs |
|
// |
|
// Author: |
|
// Martin Baulig (martin@ximian.com) |
|
// |
|
// (C) 2002 Ximian, Inc. http://www.ximian.com |
|
// |
|
|
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining |
|
// a copy of this software and associated documentation files (the |
|
// "Software"), to deal in the Software without restriction, including |
|
// without limitation the rights to use, copy, modify, merge, publish, |
|
// distribute, sublicense, and/or sell copies of the Software, and to |
|
// permit persons to whom the Software is furnished to do so, subject to |
|
// the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be |
|
// included in all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
// |
|
|
|
using System; |
|
using System.Security.Cryptography; |
|
using System.Collections.Generic; |
|
using System.Text; |
|
using System.IO; |
|
|
|
// |
|
// Parts which are actually written into the symbol file are marked with |
|
// |
|
// #region This is actually written to the symbol file |
|
// #endregion |
|
// |
|
// Please do not modify these regions without previously talking to me. |
|
// |
|
// All changes to the file format must be synchronized in several places: |
|
// |
|
// a) The fields in these regions (and their order) must match the actual |
|
// contents of the symbol file. |
|
// |
|
// This helps people to understand the symbol file format without reading |
|
// too much source code, ie. you look at the appropriate region and then |
|
// you know what's actually in the file. |
|
// |
|
// It is also required to help me enforce b). |
|
// |
|
// b) The regions must be kept in sync with the unmanaged code in |
|
// mono/metadata/debug-mono-symfile.h |
|
// |
|
// When making changes to the file format, you must also increase two version |
|
// numbers: |
|
// |
|
// i) OffsetTable.Version in this file. |
|
// ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h |
|
// |
|
// After doing so, recompile everything, including the debugger. Symbol files |
|
// with different versions are incompatible to each other and the debugger and |
|
// the runtime enfore this, so you need to recompile all your assemblies after |
|
// changing the file format. |
|
// |
|
|
|
namespace Mono.CompilerServices.SymbolWriter |
|
{ |
|
public class OffsetTable |
|
{ |
|
public const int MajorVersion = 50; |
|
public const int MinorVersion = 0; |
|
public const long Magic = 0x45e82623fd7fa614; |
|
|
|
#region This is actually written to the symbol file |
|
public int TotalFileSize; |
|
public int DataSectionOffset; |
|
public int DataSectionSize; |
|
public int CompileUnitCount; |
|
public int CompileUnitTableOffset; |
|
public int CompileUnitTableSize; |
|
public int SourceCount; |
|
public int SourceTableOffset; |
|
public int SourceTableSize; |
|
public int MethodCount; |
|
public int MethodTableOffset; |
|
public int MethodTableSize; |
|
public int TypeCount; |
|
public int AnonymousScopeCount; |
|
public int AnonymousScopeTableOffset; |
|
public int AnonymousScopeTableSize; |
|
|
|
[Flags] |
|
public enum Flags |
|
{ |
|
IsAspxSource = 1, |
|
WindowsFileNames = 2 |
|
} |
|
|
|
public Flags FileFlags; |
|
|
|
public int LineNumberTable_LineBase = LineNumberTable.Default_LineBase; |
|
public int LineNumberTable_LineRange = LineNumberTable.Default_LineRange; |
|
public int LineNumberTable_OpcodeBase = LineNumberTable.Default_OpcodeBase; |
|
#endregion |
|
|
|
internal OffsetTable () |
|
{ |
|
int platform = (int) Environment.OSVersion.Platform; |
|
if ((platform != 4) && (platform != 128)) |
|
FileFlags |= Flags.WindowsFileNames; |
|
} |
|
|
|
internal OffsetTable (BinaryReader reader, int major_version, int minor_version) |
|
{ |
|
TotalFileSize = reader.ReadInt32 (); |
|
DataSectionOffset = reader.ReadInt32 (); |
|
DataSectionSize = reader.ReadInt32 (); |
|
CompileUnitCount = reader.ReadInt32 (); |
|
CompileUnitTableOffset = reader.ReadInt32 (); |
|
CompileUnitTableSize = reader.ReadInt32 (); |
|
SourceCount = reader.ReadInt32 (); |
|
SourceTableOffset = reader.ReadInt32 (); |
|
SourceTableSize = reader.ReadInt32 (); |
|
MethodCount = reader.ReadInt32 (); |
|
MethodTableOffset = reader.ReadInt32 (); |
|
MethodTableSize = reader.ReadInt32 (); |
|
TypeCount = reader.ReadInt32 (); |
|
|
|
AnonymousScopeCount = reader.ReadInt32 (); |
|
AnonymousScopeTableOffset = reader.ReadInt32 (); |
|
AnonymousScopeTableSize = reader.ReadInt32 (); |
|
|
|
LineNumberTable_LineBase = reader.ReadInt32 (); |
|
LineNumberTable_LineRange = reader.ReadInt32 (); |
|
LineNumberTable_OpcodeBase = reader.ReadInt32 (); |
|
|
|
FileFlags = (Flags) reader.ReadInt32 (); |
|
} |
|
|
|
internal void Write (BinaryWriter bw, int major_version, int minor_version) |
|
{ |
|
bw.Write (TotalFileSize); |
|
bw.Write (DataSectionOffset); |
|
bw.Write (DataSectionSize); |
|
bw.Write (CompileUnitCount); |
|
bw.Write (CompileUnitTableOffset); |
|
bw.Write (CompileUnitTableSize); |
|
bw.Write (SourceCount); |
|
bw.Write (SourceTableOffset); |
|
bw.Write (SourceTableSize); |
|
bw.Write (MethodCount); |
|
bw.Write (MethodTableOffset); |
|
bw.Write (MethodTableSize); |
|
bw.Write (TypeCount); |
|
|
|
bw.Write (AnonymousScopeCount); |
|
bw.Write (AnonymousScopeTableOffset); |
|
bw.Write (AnonymousScopeTableSize); |
|
|
|
bw.Write (LineNumberTable_LineBase); |
|
bw.Write (LineNumberTable_LineRange); |
|
bw.Write (LineNumberTable_OpcodeBase); |
|
|
|
bw.Write ((int) FileFlags); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ( |
|
"OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]", |
|
TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount, |
|
SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset, |
|
MethodTableSize, TypeCount); |
|
} |
|
} |
|
|
|
public class LineNumberEntry |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int Row; |
|
public int Column; |
|
public readonly int File; |
|
public readonly int Offset; |
|
public readonly bool IsHidden; // Obsolete is never used |
|
#endregion |
|
|
|
public sealed class LocationComparer : IComparer<LineNumberEntry> |
|
{ |
|
public static readonly LocationComparer Default = new LocationComparer (); |
|
|
|
public int Compare (LineNumberEntry l1, LineNumberEntry l2) |
|
{ |
|
return l1.Row == l2.Row ? |
|
l1.Column.CompareTo (l2.Column) : |
|
l1.Row.CompareTo (l2.Row); |
|
} |
|
} |
|
|
|
public static readonly LineNumberEntry Null = new LineNumberEntry (0, 0, 0, 0); |
|
|
|
public LineNumberEntry (int file, int row, int column, int offset) |
|
: this (file, row, offset, column, false) |
|
{ |
|
} |
|
|
|
public LineNumberEntry (int file, int row, int offset) |
|
: this (file, row, -1, offset, false) |
|
{ |
|
} |
|
|
|
public LineNumberEntry (int file, int row, int column, int offset, bool is_hidden) |
|
{ |
|
this.File = file; |
|
this.Row = row; |
|
this.Column = column; |
|
this.Offset = offset; |
|
this.IsHidden = is_hidden; |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[Line {0}:{1,2}:{3}]", File, Row, Column, Offset); |
|
} |
|
} |
|
|
|
public class CodeBlockEntry |
|
{ |
|
public int Index; |
|
#region This is actually written to the symbol file |
|
public int Parent; |
|
public Type BlockType; |
|
public int StartOffset; |
|
public int EndOffset; |
|
#endregion |
|
|
|
public enum Type { |
|
Lexical = 1, |
|
CompilerGenerated = 2, |
|
IteratorBody = 3, |
|
IteratorDispatcher = 4 |
|
} |
|
|
|
public CodeBlockEntry (int index, int parent, Type type, int start_offset) |
|
{ |
|
this.Index = index; |
|
this.Parent = parent; |
|
this.BlockType = type; |
|
this.StartOffset = start_offset; |
|
} |
|
|
|
internal CodeBlockEntry (int index, MyBinaryReader reader) |
|
{ |
|
this.Index = index; |
|
int type_flag = reader.ReadLeb128 (); |
|
BlockType = (Type) (type_flag & 0x3f); |
|
this.Parent = reader.ReadLeb128 (); |
|
this.StartOffset = reader.ReadLeb128 (); |
|
this.EndOffset = reader.ReadLeb128 (); |
|
|
|
/* Reserved for future extensions. */ |
|
if ((type_flag & 0x40) != 0) { |
|
int data_size = reader.ReadInt16 (); |
|
reader.BaseStream.Position += data_size; |
|
} |
|
} |
|
|
|
public void Close (int end_offset) |
|
{ |
|
this.EndOffset = end_offset; |
|
} |
|
|
|
internal void Write (MyBinaryWriter bw) |
|
{ |
|
bw.WriteLeb128 ((int) BlockType); |
|
bw.WriteLeb128 (Parent); |
|
bw.WriteLeb128 (StartOffset); |
|
bw.WriteLeb128 (EndOffset); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[CodeBlock {0}:{1}:{2}:{3}:{4}]", |
|
Index, Parent, BlockType, StartOffset, EndOffset); |
|
} |
|
} |
|
|
|
public struct LocalVariableEntry |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int Index; |
|
public readonly string Name; |
|
public readonly int BlockIndex; |
|
#endregion |
|
|
|
public LocalVariableEntry (int index, string name, int block) |
|
{ |
|
this.Index = index; |
|
this.Name = name; |
|
this.BlockIndex = block; |
|
} |
|
|
|
internal LocalVariableEntry (MonoSymbolFile file, MyBinaryReader reader) |
|
{ |
|
Index = reader.ReadLeb128 (); |
|
Name = reader.ReadString (); |
|
BlockIndex = reader.ReadLeb128 (); |
|
} |
|
|
|
internal void Write (MonoSymbolFile file, MyBinaryWriter bw) |
|
{ |
|
bw.WriteLeb128 (Index); |
|
bw.Write (Name); |
|
bw.WriteLeb128 (BlockIndex); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[LocalVariable {0}:{1}:{2}]", |
|
Name, Index, BlockIndex - 1); |
|
} |
|
} |
|
|
|
public struct CapturedVariable |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly string Name; |
|
public readonly string CapturedName; |
|
public readonly CapturedKind Kind; |
|
#endregion |
|
|
|
public enum CapturedKind : byte |
|
{ |
|
Local, |
|
Parameter, |
|
This |
|
} |
|
|
|
public CapturedVariable (string name, string captured_name, |
|
CapturedKind kind) |
|
{ |
|
this.Name = name; |
|
this.CapturedName = captured_name; |
|
this.Kind = kind; |
|
} |
|
|
|
internal CapturedVariable (MyBinaryReader reader) |
|
{ |
|
Name = reader.ReadString (); |
|
CapturedName = reader.ReadString (); |
|
Kind = (CapturedKind) reader.ReadByte (); |
|
} |
|
|
|
internal void Write (MyBinaryWriter bw) |
|
{ |
|
bw.Write (Name); |
|
bw.Write (CapturedName); |
|
bw.Write ((byte) Kind); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[CapturedVariable {0}:{1}:{2}]", |
|
Name, CapturedName, Kind); |
|
} |
|
} |
|
|
|
public struct CapturedScope |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int Scope; |
|
public readonly string CapturedName; |
|
#endregion |
|
|
|
public CapturedScope (int scope, string captured_name) |
|
{ |
|
this.Scope = scope; |
|
this.CapturedName = captured_name; |
|
} |
|
|
|
internal CapturedScope (MyBinaryReader reader) |
|
{ |
|
Scope = reader.ReadLeb128 (); |
|
CapturedName = reader.ReadString (); |
|
} |
|
|
|
internal void Write (MyBinaryWriter bw) |
|
{ |
|
bw.WriteLeb128 (Scope); |
|
bw.Write (CapturedName); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[CapturedScope {0}:{1}]", |
|
Scope, CapturedName); |
|
} |
|
} |
|
|
|
public struct ScopeVariable |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int Scope; |
|
public readonly int Index; |
|
#endregion |
|
|
|
public ScopeVariable (int scope, int index) |
|
{ |
|
this.Scope = scope; |
|
this.Index = index; |
|
} |
|
|
|
internal ScopeVariable (MyBinaryReader reader) |
|
{ |
|
Scope = reader.ReadLeb128 (); |
|
Index = reader.ReadLeb128 (); |
|
} |
|
|
|
internal void Write (MyBinaryWriter bw) |
|
{ |
|
bw.WriteLeb128 (Scope); |
|
bw.WriteLeb128 (Index); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[ScopeVariable {0}:{1}]", Scope, Index); |
|
} |
|
} |
|
|
|
public class AnonymousScopeEntry |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int ID; |
|
#endregion |
|
|
|
List<CapturedVariable> captured_vars = new List<CapturedVariable> (); |
|
List<CapturedScope> captured_scopes = new List<CapturedScope> (); |
|
|
|
public AnonymousScopeEntry (int id) |
|
{ |
|
this.ID = id; |
|
} |
|
|
|
internal AnonymousScopeEntry (MyBinaryReader reader) |
|
{ |
|
ID = reader.ReadLeb128 (); |
|
|
|
int num_captured_vars = reader.ReadLeb128 (); |
|
for (int i = 0; i < num_captured_vars; i++) |
|
captured_vars.Add (new CapturedVariable (reader)); |
|
|
|
int num_captured_scopes = reader.ReadLeb128 (); |
|
for (int i = 0; i < num_captured_scopes; i++) |
|
captured_scopes.Add (new CapturedScope (reader)); |
|
} |
|
|
|
internal void AddCapturedVariable (string name, string captured_name, |
|
CapturedVariable.CapturedKind kind) |
|
{ |
|
captured_vars.Add (new CapturedVariable (name, captured_name, kind)); |
|
} |
|
|
|
public CapturedVariable[] CapturedVariables { |
|
get { |
|
CapturedVariable[] retval = new CapturedVariable [captured_vars.Count]; |
|
captured_vars.CopyTo (retval, 0); |
|
return retval; |
|
} |
|
} |
|
|
|
internal void AddCapturedScope (int scope, string captured_name) |
|
{ |
|
captured_scopes.Add (new CapturedScope (scope, captured_name)); |
|
} |
|
|
|
public CapturedScope[] CapturedScopes { |
|
get { |
|
CapturedScope[] retval = new CapturedScope [captured_scopes.Count]; |
|
captured_scopes.CopyTo (retval, 0); |
|
return retval; |
|
} |
|
} |
|
|
|
internal void Write (MyBinaryWriter bw) |
|
{ |
|
bw.WriteLeb128 (ID); |
|
|
|
bw.WriteLeb128 (captured_vars.Count); |
|
foreach (CapturedVariable cv in captured_vars) |
|
cv.Write (bw); |
|
|
|
bw.WriteLeb128 (captured_scopes.Count); |
|
foreach (CapturedScope cs in captured_scopes) |
|
cs.Write (bw); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[AnonymousScope {0}]", ID); |
|
} |
|
} |
|
|
|
public class CompileUnitEntry : ICompileUnit |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int Index; |
|
int DataOffset; |
|
#endregion |
|
|
|
MonoSymbolFile file; |
|
SourceFileEntry source; |
|
List<SourceFileEntry> include_files; |
|
List<NamespaceEntry> namespaces; |
|
|
|
bool creating; |
|
|
|
public static int Size { |
|
get { return 8; } |
|
} |
|
|
|
CompileUnitEntry ICompileUnit.Entry { |
|
get { return this; } |
|
} |
|
|
|
public CompileUnitEntry (MonoSymbolFile file, SourceFileEntry source) |
|
{ |
|
this.file = file; |
|
this.source = source; |
|
|
|
this.Index = file.AddCompileUnit (this); |
|
|
|
creating = true; |
|
namespaces = new List<NamespaceEntry> (); |
|
} |
|
|
|
public void AddFile (SourceFileEntry file) |
|
{ |
|
if (!creating) |
|
throw new InvalidOperationException (); |
|
|
|
if (include_files == null) |
|
include_files = new List<SourceFileEntry> (); |
|
|
|
include_files.Add (file); |
|
} |
|
|
|
public SourceFileEntry SourceFile { |
|
get { |
|
if (creating) |
|
return source; |
|
|
|
ReadData (); |
|
return source; |
|
} |
|
} |
|
|
|
public int DefineNamespace (string name, string[] using_clauses, int parent) |
|
{ |
|
if (!creating) |
|
throw new InvalidOperationException (); |
|
|
|
int index = file.GetNextNamespaceIndex (); |
|
NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent); |
|
namespaces.Add (ns); |
|
return index; |
|
} |
|
|
|
internal void WriteData (MyBinaryWriter bw) |
|
{ |
|
DataOffset = (int) bw.BaseStream.Position; |
|
bw.WriteLeb128 (source.Index); |
|
|
|
int count_includes = include_files != null ? include_files.Count : 0; |
|
bw.WriteLeb128 (count_includes); |
|
if (include_files != null) { |
|
foreach (SourceFileEntry entry in include_files) |
|
bw.WriteLeb128 (entry.Index); |
|
} |
|
|
|
bw.WriteLeb128 (namespaces.Count); |
|
foreach (NamespaceEntry ns in namespaces) |
|
ns.Write (file, bw); |
|
} |
|
|
|
internal void Write (BinaryWriter bw) |
|
{ |
|
bw.Write (Index); |
|
bw.Write (DataOffset); |
|
} |
|
|
|
internal CompileUnitEntry (MonoSymbolFile file, MyBinaryReader reader) |
|
{ |
|
this.file = file; |
|
|
|
Index = reader.ReadInt32 (); |
|
DataOffset = reader.ReadInt32 (); |
|
} |
|
|
|
void ReadData () |
|
{ |
|
if (creating) |
|
throw new InvalidOperationException (); |
|
|
|
lock (file) { |
|
if (namespaces != null) |
|
return; |
|
|
|
MyBinaryReader reader = file.BinaryReader; |
|
int old_pos = (int) reader.BaseStream.Position; |
|
|
|
reader.BaseStream.Position = DataOffset; |
|
|
|
int source_idx = reader.ReadLeb128 (); |
|
source = file.GetSourceFile (source_idx); |
|
|
|
int count_includes = reader.ReadLeb128 (); |
|
if (count_includes > 0) { |
|
include_files = new List<SourceFileEntry> (); |
|
for (int i = 0; i < count_includes; i++) |
|
include_files.Add (file.GetSourceFile (reader.ReadLeb128 ())); |
|
} |
|
|
|
int count_ns = reader.ReadLeb128 (); |
|
namespaces = new List<NamespaceEntry> (); |
|
for (int i = 0; i < count_ns; i ++) |
|
namespaces.Add (new NamespaceEntry (file, reader)); |
|
|
|
reader.BaseStream.Position = old_pos; |
|
} |
|
} |
|
|
|
public NamespaceEntry[] Namespaces { |
|
get { |
|
ReadData (); |
|
NamespaceEntry[] retval = new NamespaceEntry [namespaces.Count]; |
|
namespaces.CopyTo (retval, 0); |
|
return retval; |
|
} |
|
} |
|
|
|
public SourceFileEntry[] IncludeFiles { |
|
get { |
|
ReadData (); |
|
if (include_files == null) |
|
return new SourceFileEntry [0]; |
|
|
|
SourceFileEntry[] retval = new SourceFileEntry [include_files.Count]; |
|
include_files.CopyTo (retval, 0); |
|
return retval; |
|
} |
|
} |
|
} |
|
|
|
public class SourceFileEntry |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int Index; |
|
int DataOffset; |
|
#endregion |
|
|
|
MonoSymbolFile file; |
|
string file_name; |
|
byte[] guid; |
|
byte[] hash; |
|
bool creating; |
|
bool auto_generated; |
|
|
|
public static int Size { |
|
get { return 8; } |
|
} |
|
|
|
public SourceFileEntry (MonoSymbolFile file, string file_name) |
|
{ |
|
this.file = file; |
|
this.file_name = file_name; |
|
this.Index = file.AddSource (this); |
|
|
|
creating = true; |
|
} |
|
|
|
public SourceFileEntry (MonoSymbolFile file, string file_name, byte[] guid, byte[] checksum) |
|
: this (file, file_name) |
|
{ |
|
this.guid = guid; |
|
this.hash = checksum; |
|
} |
|
|
|
public byte[] Checksum { |
|
get { |
|
return hash; |
|
} |
|
} |
|
|
|
internal void WriteData (MyBinaryWriter bw) |
|
{ |
|
DataOffset = (int) bw.BaseStream.Position; |
|
bw.Write (file_name); |
|
|
|
if (guid == null) |
|
guid = new byte[16]; |
|
|
|
if (hash == null) { |
|
try { |
|
using (FileStream fs = new FileStream (file_name, FileMode.Open, FileAccess.Read)) { |
|
MD5 md5 = MD5.Create (); |
|
hash = md5.ComputeHash (fs); |
|
} |
|
} catch { |
|
hash = new byte [16]; |
|
} |
|
} |
|
|
|
bw.Write (guid); |
|
bw.Write (hash); |
|
bw.Write ((byte) (auto_generated ? 1 : 0)); |
|
} |
|
|
|
internal void Write (BinaryWriter bw) |
|
{ |
|
bw.Write (Index); |
|
bw.Write (DataOffset); |
|
} |
|
|
|
internal SourceFileEntry (MonoSymbolFile file, MyBinaryReader reader) |
|
{ |
|
this.file = file; |
|
|
|
Index = reader.ReadInt32 (); |
|
DataOffset = reader.ReadInt32 (); |
|
|
|
int old_pos = (int) reader.BaseStream.Position; |
|
reader.BaseStream.Position = DataOffset; |
|
|
|
file_name = reader.ReadString (); |
|
guid = reader.ReadBytes (16); |
|
hash = reader.ReadBytes (16); |
|
auto_generated = reader.ReadByte () == 1; |
|
|
|
reader.BaseStream.Position = old_pos; |
|
} |
|
|
|
public string FileName { |
|
get { return file_name; } |
|
} |
|
|
|
public bool AutoGenerated { |
|
get { return auto_generated; } |
|
} |
|
|
|
public void SetAutoGenerated () |
|
{ |
|
if (!creating) |
|
throw new InvalidOperationException (); |
|
|
|
auto_generated = true; |
|
file.OffsetTable.FileFlags |= OffsetTable.Flags.IsAspxSource; |
|
} |
|
|
|
public bool CheckChecksum () |
|
{ |
|
try { |
|
using (FileStream fs = new FileStream (file_name, FileMode.Open)) { |
|
MD5 md5 = MD5.Create (); |
|
byte[] data = md5.ComputeHash (fs); |
|
for (int i = 0; i < 16; i++) |
|
if (data [i] != hash [i]) |
|
return false; |
|
return true; |
|
} |
|
} catch { |
|
return false; |
|
} |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("SourceFileEntry ({0}:{1})", Index, DataOffset); |
|
} |
|
} |
|
|
|
public class LineNumberTable |
|
{ |
|
protected LineNumberEntry[] _line_numbers; |
|
public LineNumberEntry[] LineNumbers { |
|
get { return _line_numbers; } |
|
} |
|
|
|
public readonly int LineBase; |
|
public readonly int LineRange; |
|
public readonly byte OpcodeBase; |
|
public readonly int MaxAddressIncrement; |
|
|
|
#region Configurable constants |
|
public const int Default_LineBase = -1; |
|
public const int Default_LineRange = 8; |
|
public const byte Default_OpcodeBase = 9; |
|
|
|
#endregion |
|
|
|
public const byte DW_LNS_copy = 1; |
|
public const byte DW_LNS_advance_pc = 2; |
|
public const byte DW_LNS_advance_line = 3; |
|
public const byte DW_LNS_set_file = 4; |
|
public const byte DW_LNS_const_add_pc = 8; |
|
|
|
public const byte DW_LNE_end_sequence = 1; |
|
|
|
// MONO extensions. |
|
public const byte DW_LNE_MONO_negate_is_hidden = 0x40; |
|
|
|
internal const byte DW_LNE_MONO__extensions_start = 0x40; |
|
internal const byte DW_LNE_MONO__extensions_end = 0x7f; |
|
|
|
protected LineNumberTable (MonoSymbolFile file) |
|
{ |
|
this.LineBase = file.OffsetTable.LineNumberTable_LineBase; |
|
this.LineRange = file.OffsetTable.LineNumberTable_LineRange; |
|
this.OpcodeBase = (byte) file.OffsetTable.LineNumberTable_OpcodeBase; |
|
this.MaxAddressIncrement = (255 - OpcodeBase) / LineRange; |
|
} |
|
|
|
internal LineNumberTable (MonoSymbolFile file, LineNumberEntry[] lines) |
|
: this (file) |
|
{ |
|
this._line_numbers = lines; |
|
} |
|
|
|
internal void Write (MonoSymbolFile file, MyBinaryWriter bw, bool readColumnsInfo) |
|
{ |
|
int start = (int) bw.BaseStream.Position; |
|
|
|
bool last_is_hidden = false; |
|
int last_line = 1, last_offset = 0, last_file = 1; |
|
for (int i = 0; i < LineNumbers.Length; i++) { |
|
int line_inc = LineNumbers [i].Row - last_line; |
|
int offset_inc = LineNumbers [i].Offset - last_offset; |
|
|
|
if (LineNumbers [i].File != last_file) { |
|
bw.Write (DW_LNS_set_file); |
|
bw.WriteLeb128 (LineNumbers [i].File); |
|
last_file = LineNumbers [i].File; |
|
} |
|
|
|
if (LineNumbers [i].IsHidden != last_is_hidden) { |
|
bw.Write ((byte) 0); |
|
bw.Write ((byte) 1); |
|
bw.Write (DW_LNE_MONO_negate_is_hidden); |
|
last_is_hidden = LineNumbers [i].IsHidden; |
|
} |
|
|
|
if (offset_inc >= MaxAddressIncrement) { |
|
if (offset_inc < 2 * MaxAddressIncrement) { |
|
bw.Write (DW_LNS_const_add_pc); |
|
offset_inc -= MaxAddressIncrement; |
|
} else { |
|
bw.Write (DW_LNS_advance_pc); |
|
bw.WriteLeb128 (offset_inc); |
|
offset_inc = 0; |
|
} |
|
} |
|
|
|
if ((line_inc < LineBase) || (line_inc >= LineBase + LineRange)) { |
|
bw.Write (DW_LNS_advance_line); |
|
bw.WriteLeb128 (line_inc); |
|
if (offset_inc != 0) { |
|
bw.Write (DW_LNS_advance_pc); |
|
bw.WriteLeb128 (offset_inc); |
|
} |
|
bw.Write (DW_LNS_copy); |
|
} else { |
|
byte opcode; |
|
opcode = (byte) (line_inc - LineBase + (LineRange * offset_inc) + |
|
OpcodeBase); |
|
bw.Write (opcode); |
|
} |
|
|
|
last_line = LineNumbers [i].Row; |
|
last_offset = LineNumbers [i].Offset; |
|
} |
|
|
|
bw.Write ((byte) 0); |
|
bw.Write ((byte) 1); |
|
bw.Write (DW_LNE_end_sequence); |
|
|
|
for (int i = 0; i < LineNumbers.Length; i++) { |
|
var ln = LineNumbers [i]; |
|
if (ln.Row >= 0) |
|
bw.WriteLeb128 (ln.Column); |
|
} |
|
|
|
file.ExtendedLineNumberSize += (int) bw.BaseStream.Position - start; |
|
} |
|
|
|
internal static LineNumberTable Read (MonoSymbolFile file, MyBinaryReader br, bool readColumnsInfo) |
|
{ |
|
LineNumberTable lnt = new LineNumberTable (file); |
|
lnt.DoRead (file, br, readColumnsInfo); |
|
return lnt; |
|
} |
|
|
|
void DoRead (MonoSymbolFile file, MyBinaryReader br, bool includesColumns) |
|
{ |
|
var lines = new List<LineNumberEntry> (); |
|
|
|
bool is_hidden = false, modified = false; |
|
int stm_line = 1, stm_offset = 0, stm_file = 1; |
|
while (true) { |
|
byte opcode = br.ReadByte (); |
|
|
|
if (opcode == 0) { |
|
byte size = br.ReadByte (); |
|
long end_pos = br.BaseStream.Position + size; |
|
opcode = br.ReadByte (); |
|
|
|
if (opcode == DW_LNE_end_sequence) { |
|
if (modified) |
|
lines.Add (new LineNumberEntry ( |
|
stm_file, stm_line, -1, stm_offset, is_hidden)); |
|
break; |
|
} else if (opcode == DW_LNE_MONO_negate_is_hidden) { |
|
is_hidden = !is_hidden; |
|
modified = true; |
|
} else if ((opcode >= DW_LNE_MONO__extensions_start) && |
|
(opcode <= DW_LNE_MONO__extensions_end)) { |
|
; // reserved for future extensions |
|
} else { |
|
throw new MonoSymbolFileException ("Unknown extended opcode {0:x}", opcode); |
|
} |
|
|
|
br.BaseStream.Position = end_pos; |
|
continue; |
|
} else if (opcode < OpcodeBase) { |
|
switch (opcode) { |
|
case DW_LNS_copy: |
|
lines.Add (new LineNumberEntry ( |
|
stm_file, stm_line, -1, stm_offset, is_hidden)); |
|
modified = false; |
|
break; |
|
case DW_LNS_advance_pc: |
|
stm_offset += br.ReadLeb128 (); |
|
modified = true; |
|
break; |
|
case DW_LNS_advance_line: |
|
stm_line += br.ReadLeb128 (); |
|
modified = true; |
|
break; |
|
case DW_LNS_set_file: |
|
stm_file = br.ReadLeb128 (); |
|
modified = true; |
|
break; |
|
case DW_LNS_const_add_pc: |
|
stm_offset += MaxAddressIncrement; |
|
modified = true; |
|
break; |
|
default: |
|
throw new MonoSymbolFileException ( |
|
"Unknown standard opcode {0:x} in LNT", |
|
opcode); |
|
} |
|
} else { |
|
opcode -= OpcodeBase; |
|
|
|
stm_offset += opcode / LineRange; |
|
stm_line += LineBase + (opcode % LineRange); |
|
lines.Add (new LineNumberEntry ( |
|
stm_file, stm_line, -1, stm_offset, is_hidden)); |
|
modified = false; |
|
} |
|
} |
|
|
|
_line_numbers = lines.ToArray (); |
|
|
|
if (includesColumns) { |
|
for (int i = 0; i < _line_numbers.Length; ++i) { |
|
var ln = _line_numbers[i]; |
|
if (ln.Row >= 0) |
|
ln.Column = br.ReadLeb128 (); |
|
} |
|
} |
|
} |
|
|
|
public bool GetMethodBounds (out LineNumberEntry start, out LineNumberEntry end) |
|
{ |
|
if (_line_numbers.Length > 1) { |
|
start = _line_numbers [0]; |
|
end = _line_numbers [_line_numbers.Length - 1]; |
|
return true; |
|
} |
|
|
|
start = LineNumberEntry.Null; |
|
end = LineNumberEntry.Null; |
|
return false; |
|
} |
|
} |
|
|
|
public class MethodEntry : IComparable |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly int CompileUnitIndex; |
|
public readonly int Token; |
|
public readonly int NamespaceID; |
|
|
|
int DataOffset; |
|
int LocalVariableTableOffset; |
|
int LineNumberTableOffset; |
|
int CodeBlockTableOffset; |
|
int ScopeVariableTableOffset; |
|
int RealNameOffset; |
|
Flags flags; |
|
#endregion |
|
|
|
int index; |
|
|
|
public Flags MethodFlags { |
|
get { return flags; } |
|
} |
|
|
|
public readonly CompileUnitEntry CompileUnit; |
|
|
|
LocalVariableEntry[] locals; |
|
CodeBlockEntry[] code_blocks; |
|
ScopeVariable[] scope_vars; |
|
LineNumberTable lnt; |
|
string real_name; |
|
|
|
public readonly MonoSymbolFile SymbolFile; |
|
|
|
public int Index { |
|
get { return index; } |
|
set { index = value; } |
|
} |
|
|
|
[Flags] |
|
public enum Flags |
|
{ |
|
LocalNamesAmbiguous = 1, |
|
ColumnsInfoIncluded = 1 << 1 |
|
} |
|
|
|
public const int Size = 12; |
|
|
|
internal MethodEntry (MonoSymbolFile file, MyBinaryReader reader, int index) |
|
{ |
|
this.SymbolFile = file; |
|
this.index = index; |
|
|
|
Token = reader.ReadInt32 (); |
|
DataOffset = reader.ReadInt32 (); |
|
LineNumberTableOffset = reader.ReadInt32 (); |
|
|
|
long old_pos = reader.BaseStream.Position; |
|
reader.BaseStream.Position = DataOffset; |
|
|
|
CompileUnitIndex = reader.ReadLeb128 (); |
|
LocalVariableTableOffset = reader.ReadLeb128 (); |
|
NamespaceID = reader.ReadLeb128 (); |
|
|
|
CodeBlockTableOffset = reader.ReadLeb128 (); |
|
ScopeVariableTableOffset = reader.ReadLeb128 (); |
|
|
|
RealNameOffset = reader.ReadLeb128 (); |
|
|
|
flags = (Flags) reader.ReadLeb128 (); |
|
|
|
reader.BaseStream.Position = old_pos; |
|
|
|
CompileUnit = file.GetCompileUnit (CompileUnitIndex); |
|
} |
|
|
|
internal MethodEntry (MonoSymbolFile file, CompileUnitEntry comp_unit, |
|
int token, ScopeVariable[] scope_vars, |
|
LocalVariableEntry[] locals, LineNumberEntry[] lines, |
|
CodeBlockEntry[] code_blocks, string real_name, |
|
Flags flags, int namespace_id) |
|
{ |
|
this.SymbolFile = file; |
|
this.real_name = real_name; |
|
this.locals = locals; |
|
this.code_blocks = code_blocks; |
|
this.scope_vars = scope_vars; |
|
this.flags = flags; |
|
|
|
index = -1; |
|
|
|
Token = token; |
|
CompileUnitIndex = comp_unit.Index; |
|
CompileUnit = comp_unit; |
|
NamespaceID = namespace_id; |
|
|
|
CheckLineNumberTable (lines); |
|
lnt = new LineNumberTable (file, lines); |
|
file.NumLineNumbers += lines.Length; |
|
|
|
int num_locals = locals != null ? locals.Length : 0; |
|
|
|
if (num_locals <= 32) { |
|
// Most of the time, the O(n^2) factor is actually |
|
// less than the cost of allocating the hash table, |
|
// 32 is a rough number obtained through some testing. |
|
|
|
for (int i = 0; i < num_locals; i ++) { |
|
string nm = locals [i].Name; |
|
|
|
for (int j = i + 1; j < num_locals; j ++) { |
|
if (locals [j].Name == nm) { |
|
flags |= Flags.LocalNamesAmbiguous; |
|
goto locals_check_done; |
|
} |
|
} |
|
} |
|
locals_check_done : |
|
; |
|
} else { |
|
var local_names = new Dictionary<string, LocalVariableEntry> (); |
|
foreach (LocalVariableEntry local in locals) { |
|
if (local_names.ContainsKey (local.Name)) { |
|
flags |= Flags.LocalNamesAmbiguous; |
|
break; |
|
} |
|
local_names.Add (local.Name, local); |
|
} |
|
} |
|
} |
|
|
|
static void CheckLineNumberTable (LineNumberEntry[] line_numbers) |
|
{ |
|
int last_offset = -1; |
|
int last_row = -1; |
|
|
|
if (line_numbers == null) |
|
return; |
|
|
|
for (int i = 0; i < line_numbers.Length; i++) { |
|
LineNumberEntry line = line_numbers [i]; |
|
|
|
if (line.Equals (LineNumberEntry.Null)) |
|
throw new MonoSymbolFileException (); |
|
|
|
if (line.Offset < last_offset) |
|
throw new MonoSymbolFileException (); |
|
|
|
if (line.Offset > last_offset) { |
|
last_row = line.Row; |
|
last_offset = line.Offset; |
|
} else if (line.Row > last_row) { |
|
last_row = line.Row; |
|
} |
|
} |
|
} |
|
|
|
internal void Write (MyBinaryWriter bw) |
|
{ |
|
if ((index <= 0) || (DataOffset == 0)) |
|
throw new InvalidOperationException (); |
|
|
|
bw.Write (Token); |
|
bw.Write (DataOffset); |
|
bw.Write (LineNumberTableOffset); |
|
} |
|
|
|
internal void WriteData (MonoSymbolFile file, MyBinaryWriter bw) |
|
{ |
|
if (index <= 0) |
|
throw new InvalidOperationException (); |
|
|
|
LocalVariableTableOffset = (int) bw.BaseStream.Position; |
|
int num_locals = locals != null ? locals.Length : 0; |
|
bw.WriteLeb128 (num_locals); |
|
for (int i = 0; i < num_locals; i++) |
|
locals [i].Write (file, bw); |
|
file.LocalCount += num_locals; |
|
|
|
CodeBlockTableOffset = (int) bw.BaseStream.Position; |
|
int num_code_blocks = code_blocks != null ? code_blocks.Length : 0; |
|
bw.WriteLeb128 (num_code_blocks); |
|
for (int i = 0; i < num_code_blocks; i++) |
|
code_blocks [i].Write (bw); |
|
|
|
ScopeVariableTableOffset = (int) bw.BaseStream.Position; |
|
int num_scope_vars = scope_vars != null ? scope_vars.Length : 0; |
|
bw.WriteLeb128 (num_scope_vars); |
|
for (int i = 0; i < num_scope_vars; i++) |
|
scope_vars [i].Write (bw); |
|
|
|
if (real_name != null) { |
|
RealNameOffset = (int) bw.BaseStream.Position; |
|
bw.Write (real_name); |
|
} |
|
|
|
LineNumberTableOffset = (int) bw.BaseStream.Position; |
|
lnt.Write (file, bw, (flags & Flags.ColumnsInfoIncluded) != 0); |
|
|
|
DataOffset = (int) bw.BaseStream.Position; |
|
|
|
bw.WriteLeb128 (CompileUnitIndex); |
|
bw.WriteLeb128 (LocalVariableTableOffset); |
|
bw.WriteLeb128 (NamespaceID); |
|
|
|
bw.WriteLeb128 (CodeBlockTableOffset); |
|
bw.WriteLeb128 (ScopeVariableTableOffset); |
|
|
|
bw.WriteLeb128 (RealNameOffset); |
|
bw.WriteLeb128 ((int) flags); |
|
} |
|
|
|
public LineNumberTable GetLineNumberTable () |
|
{ |
|
lock (SymbolFile) { |
|
if (lnt != null) |
|
return lnt; |
|
|
|
if (LineNumberTableOffset == 0) |
|
return null; |
|
|
|
MyBinaryReader reader = SymbolFile.BinaryReader; |
|
long old_pos = reader.BaseStream.Position; |
|
reader.BaseStream.Position = LineNumberTableOffset; |
|
|
|
lnt = LineNumberTable.Read (SymbolFile, reader, (flags & Flags.ColumnsInfoIncluded) != 0); |
|
|
|
reader.BaseStream.Position = old_pos; |
|
return lnt; |
|
} |
|
} |
|
|
|
public LocalVariableEntry[] GetLocals () |
|
{ |
|
lock (SymbolFile) { |
|
if (locals != null) |
|
return locals; |
|
|
|
if (LocalVariableTableOffset == 0) |
|
return null; |
|
|
|
MyBinaryReader reader = SymbolFile.BinaryReader; |
|
long old_pos = reader.BaseStream.Position; |
|
reader.BaseStream.Position = LocalVariableTableOffset; |
|
|
|
int num_locals = reader.ReadLeb128 (); |
|
locals = new LocalVariableEntry [num_locals]; |
|
|
|
for (int i = 0; i < num_locals; i++) |
|
locals [i] = new LocalVariableEntry (SymbolFile, reader); |
|
|
|
reader.BaseStream.Position = old_pos; |
|
return locals; |
|
} |
|
} |
|
|
|
public CodeBlockEntry[] GetCodeBlocks () |
|
{ |
|
lock (SymbolFile) { |
|
if (code_blocks != null) |
|
return code_blocks; |
|
|
|
if (CodeBlockTableOffset == 0) |
|
return null; |
|
|
|
MyBinaryReader reader = SymbolFile.BinaryReader; |
|
long old_pos = reader.BaseStream.Position; |
|
reader.BaseStream.Position = CodeBlockTableOffset; |
|
|
|
int num_code_blocks = reader.ReadLeb128 (); |
|
code_blocks = new CodeBlockEntry [num_code_blocks]; |
|
|
|
for (int i = 0; i < num_code_blocks; i++) |
|
code_blocks [i] = new CodeBlockEntry (i, reader); |
|
|
|
reader.BaseStream.Position = old_pos; |
|
return code_blocks; |
|
} |
|
} |
|
|
|
public ScopeVariable[] GetScopeVariables () |
|
{ |
|
lock (SymbolFile) { |
|
if (scope_vars != null) |
|
return scope_vars; |
|
|
|
if (ScopeVariableTableOffset == 0) |
|
return null; |
|
|
|
MyBinaryReader reader = SymbolFile.BinaryReader; |
|
long old_pos = reader.BaseStream.Position; |
|
reader.BaseStream.Position = ScopeVariableTableOffset; |
|
|
|
int num_scope_vars = reader.ReadLeb128 (); |
|
scope_vars = new ScopeVariable [num_scope_vars]; |
|
|
|
for (int i = 0; i < num_scope_vars; i++) |
|
scope_vars [i] = new ScopeVariable (reader); |
|
|
|
reader.BaseStream.Position = old_pos; |
|
return scope_vars; |
|
} |
|
} |
|
|
|
public string GetRealName () |
|
{ |
|
lock (SymbolFile) { |
|
if (real_name != null) |
|
return real_name; |
|
|
|
if (RealNameOffset == 0) |
|
return null; |
|
|
|
real_name = SymbolFile.BinaryReader.ReadString (RealNameOffset); |
|
return real_name; |
|
} |
|
} |
|
|
|
public int CompareTo (object obj) |
|
{ |
|
MethodEntry method = (MethodEntry) obj; |
|
|
|
if (method.Token < Token) |
|
return 1; |
|
else if (method.Token > Token) |
|
return -1; |
|
else |
|
return 0; |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[Method {0}:{1:x}:{2}:{3}]", |
|
index, Token, CompileUnitIndex, CompileUnit); |
|
} |
|
} |
|
|
|
public struct NamespaceEntry |
|
{ |
|
#region This is actually written to the symbol file |
|
public readonly string Name; |
|
public readonly int Index; |
|
public readonly int Parent; |
|
public readonly string[] UsingClauses; |
|
#endregion |
|
|
|
public NamespaceEntry (string name, int index, string[] using_clauses, int parent) |
|
{ |
|
this.Name = name; |
|
this.Index = index; |
|
this.Parent = parent; |
|
this.UsingClauses = using_clauses != null ? using_clauses : new string [0]; |
|
} |
|
|
|
internal NamespaceEntry (MonoSymbolFile file, MyBinaryReader reader) |
|
{ |
|
Name = reader.ReadString (); |
|
Index = reader.ReadLeb128 (); |
|
Parent = reader.ReadLeb128 (); |
|
|
|
int count = reader.ReadLeb128 (); |
|
UsingClauses = new string [count]; |
|
for (int i = 0; i < count; i++) |
|
UsingClauses [i] = reader.ReadString (); |
|
} |
|
|
|
internal void Write (MonoSymbolFile file, MyBinaryWriter bw) |
|
{ |
|
bw.Write (Name); |
|
bw.WriteLeb128 (Index); |
|
bw.WriteLeb128 (Parent); |
|
bw.WriteLeb128 (UsingClauses.Length); |
|
foreach (string uc in UsingClauses) |
|
bw.Write (uc); |
|
} |
|
|
|
public override string ToString () |
|
{ |
|
return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent); |
|
} |
|
} |
|
}
|
|
|