diff options
author | Zoltan Varga <vargaz@gmail.com> | 2016-04-06 00:34:28 +0300 |
---|---|---|
committer | Zoltan Varga <vargaz@gmail.com> | 2016-04-06 00:34:28 +0300 |
commit | 1f93971046fb8d0739c04cc502ec7cd226b6308e (patch) | |
tree | 285c7becd5d27f8a82b8639932bea2401d200360 | |
parent | 00252c18fc0a4a206e45461736a890acb785a9d8 (diff) |
Add preliminary support for reading portable pdb files.
-rw-r--r-- | reflect/Emit/ModuleBuilder.cs | 5 | ||||
-rw-r--r-- | reflect/Metadata/Tables.cs | 283 | ||||
-rw-r--r-- | reflect/Module.cs | 28 | ||||
-rw-r--r-- | reflect/Reader/ModuleReader.cs | 78 |
4 files changed, 388 insertions, 6 deletions
diff --git a/reflect/Emit/ModuleBuilder.cs b/reflect/Emit/ModuleBuilder.cs index 571f8e24..b3940bc1 100644 --- a/reflect/Emit/ModuleBuilder.cs +++ b/reflect/Emit/ModuleBuilder.cs @@ -1532,6 +1532,11 @@ namespace IKVM.Reflection.Emit return Blobs.GetBlob(blobIndex); } + internal sealed override Guid GetGuid(int guidIndex) + { + throw new NotImplementedException(); + } + internal int GetSignatureBlobIndex(Signature sig) { ByteBuffer bb = new ByteBuffer(16); diff --git a/reflect/Metadata/Tables.cs b/reflect/Metadata/Tables.cs index b8734c2e..75ceef7c 100644 --- a/reflect/Metadata/Tables.cs +++ b/reflect/Metadata/Tables.cs @@ -2719,4 +2719,287 @@ namespace IKVM.Reflection.Metadata Sort(); } } + + // Portable PDB + sealed class DocumentTable : Table<DocumentTable.Record> + { + internal const int Index = 0x30; + + internal static Guid SHA1Guid = new Guid("ff1816ec-aa5e-4d10-87f7-6f4963833460"); + internal static Guid CSharpGuid = new Guid("3f5162f8-07c6-11d3-9053-00c04fa302a1"); + + internal struct Record + { + internal int Name; // -> StringHeap + internal int HashAlgorithm; // -> GuidHeap + internal int Hash; // -> BlobHeap + internal int Language; // -> GuidHeap + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + records[i].Name = mr.ReadBlobIndex(); + records[i].HashAlgorithm = mr.ReadGuidIndex(); + records[i].Hash = mr.ReadBlobIndex(); + records[i].Language = mr.ReadGuidIndex(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + return rsc + .WriteStringIndex() + .WriteGuidIndex() + .WriteBlobIndex() + .WriteGuidIndex() + .Value; + } + } + + // Portable PDB + sealed class MethodDebugInformationTable : Table<MethodDebugInformationTable.Record> + { + internal const int Index = 0x31; + + internal struct Record + { + internal int Document; // -> Document table + internal int SequencePoints; // -> BlobHeap + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + // FIXME: Token size + records[i].Document = mr.ReadInt16 (); + records[i].SequencePoints = mr.ReadBlobIndex(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + return rsc + .WriteBlobIndex() + .Value; + } + } + + // Portable PDB + // FIXME: Sorted + sealed class LocalScopeTable : Table<LocalScopeTable.Record> + { + internal const int Index = 0x32; + + internal struct Record + { + internal int Method; + internal int ImportScope; + internal int VariableList; + internal int ConstantList; + internal uint StartOffset; + internal uint Length; + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + // FIXME: Token sizes ? + records[i].Method = mr.ReadInt16(); + records[i].ImportScope = mr.ReadInt16(); + records[i].VariableList = mr.ReadInt16(); + records[i].ConstantList = mr.ReadInt16(); + records[i].StartOffset = (uint)mr.ReadInt32(); + records[i].Length = (uint)mr.ReadInt32(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + throw new NotImplementedException(); + } + } + + // Portable PDB + sealed class LocalVariableTable : Table<LocalVariableTable.Record> + { + internal const int Index = 0x33; + + internal enum LocalVariableAttributes { + DebuggerHidden = 0x1 + } + + internal struct Record + { + internal int Attributes; + internal int Index; + internal int Name; // -> StringHeap + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + records[i].Attributes = mr.ReadInt16(); + records[i].Index = mr.ReadInt16(); + records[i].Name = mr.ReadStringIndex(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + throw new NotImplementedException(); + } + } + + // Portable PDB + sealed class LocalConstantTable : Table<LocalConstantTable.Record> + { + internal const int Index = 0x34; + + internal struct Record + { + internal int Name; // -> StringHeap + internal int Signature; // -> BlobHeap + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + records[i].Name = mr.ReadStringIndex(); + records[i].Signature = mr.ReadBlobIndex(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + throw new NotImplementedException(); + } + } + + // Portable PDB + sealed class ImportScopeTable : Table<ImportScopeTable.Record> + { + internal const int Index = 0x35; + + internal struct Record + { + internal int Parent; + internal int Imports; // -> BlobHeap + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + // FIXME: Token size + records[i].Parent = mr.ReadUInt16(); + records[i].Imports = mr.ReadBlobIndex(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + throw new NotImplementedException(); + } + } + + // Portable PDB + sealed class StateMachineTable : Table<StateMachineTable.Record> + { + internal const int Index = 0x36; + + internal struct Record + { + internal int MoveNextMethod; + internal int KickoffMethod; + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + records[i].MoveNextMethod = mr.ReadUInt16(); + records[i].KickoffMethod = mr.ReadUInt16(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + throw new NotImplementedException(); + } + } + + // Portable PDB + sealed class CustomDebugInformationTable : Table<CustomDebugInformationTable.Record> + { + internal const int Index = 0x37; + + internal struct Record + { + internal int Parent; + internal int Kind; // -> GuidHeap + internal int Value; // -> BlobHeap + } + + internal override void Read(MetadataReader mr) + { + for (int i = 0; i < records.Length; i++) + { + // FIXME: Token size + records[i].Parent = mr.ReadUInt16(); + records[i].Kind = mr.ReadBlobIndex(); + records[i].Value = mr.ReadBlobIndex(); + } + } + + internal override void Write(MetadataWriter mw) + { + throw new NotImplementedException (); + } + + protected override int GetRowSize(RowSizeCalc rsc) + { + throw new NotImplementedException(); + } + } } diff --git a/reflect/Module.cs b/reflect/Module.cs index d93dba5f..23c14057 100644 --- a/reflect/Module.cs +++ b/reflect/Module.cs @@ -156,6 +156,14 @@ namespace IKVM.Reflection internal readonly GenericParamTable GenericParam = new GenericParamTable(); internal readonly MethodSpecTable MethodSpec = new MethodSpecTable(); internal readonly GenericParamConstraintTable GenericParamConstraint = new GenericParamConstraintTable(); + internal readonly DocumentTable Document = new DocumentTable(); + internal readonly MethodDebugInformationTable MethodDebugInformation = new MethodDebugInformationTable(); + internal readonly LocalScopeTable LocalScope = new LocalScopeTable(); + internal readonly LocalVariableTable LocalVariable = new LocalVariableTable(); + internal readonly LocalConstantTable LocalConstant = new LocalConstantTable(); + internal readonly ImportScopeTable ImportScope = new ImportScopeTable(); + internal readonly StateMachineTable StateMachine = new StateMachineTable(); + internal readonly CustomDebugInformationTable CustomDebugInformation = new CustomDebugInformationTable(); protected Module(Universe universe) { @@ -204,6 +212,14 @@ namespace IKVM.Reflection tables[GenericParamTable.Index] = GenericParam; tables[MethodSpecTable.Index] = MethodSpec; tables[GenericParamConstraintTable.Index] = GenericParamConstraint; + tables[DocumentTable.Index] = Document; + tables[MethodDebugInformationTable.Index] = MethodDebugInformation; + tables[LocalScopeTable.Index] = LocalScope; + tables[LocalVariableTable.Index] = LocalVariable; + tables[LocalConstantTable.Index] = LocalConstant; + tables[ImportScopeTable.Index] = ImportScope; + tables[StateMachineTable.Index] = StateMachine; + tables[CustomDebugInformationTable.Index] = CustomDebugInformation; return tables; } @@ -552,6 +568,11 @@ namespace IKVM.Reflection get { throw new NotSupportedException(); } } + public virtual bool __IsMetadataOnly + { + get { throw new NotSupportedException(); } + } + public IEnumerable<CustomAttributeData> __EnumerateCustomAttributeTable() { List<CustomAttributeData> list = new List<CustomAttributeData>(CustomAttribute.RowCount); @@ -594,6 +615,8 @@ namespace IKVM.Reflection internal abstract ByteReader GetBlob(int blobIndex); + internal abstract Guid GetGuid(int guidIndex); + internal IList<CustomAttributeData> GetDeclarativeSecurity(int metadataToken) { List<CustomAttributeData> list = new List<CustomAttributeData>(); @@ -650,6 +673,11 @@ namespace IKVM.Reflection throw InvalidOperationException(); } + internal sealed override Guid GetGuid(int guidIndex) + { + throw InvalidOperationException(); + } + public sealed override AssemblyName[] __GetReferencedAssemblies() { throw NotSupportedException(); diff --git a/reflect/Reader/ModuleReader.cs b/reflect/Reader/ModuleReader.cs index 4daa5355..75e703a8 100644 --- a/reflect/Reader/ModuleReader.cs +++ b/reflect/Reader/ModuleReader.cs @@ -52,6 +52,13 @@ namespace IKVM.Reflection.Reader } } + // FIXME: Put this somewhere else + class PdbStream { + public int EntryPoint { get; set; } + public ulong ReferencedTables { get; set; } + public int[] TableSizes { get; set; } + } + sealed class ModuleReader : Module { private readonly Stream stream; @@ -78,6 +85,8 @@ namespace IKVM.Reflection.Reader private Dictionary<int, string> strings = new Dictionary<int, string>(); private Dictionary<TypeName, Type> types = new Dictionary<TypeName, Type>(); private Dictionary<TypeName, LazyForwardedType> forwardedTypes = new Dictionary<TypeName, LazyForwardedType>(); + private PdbStream pdbStream; + private bool isMetadataOnly; private sealed class LazyForwardedType { @@ -126,10 +135,23 @@ namespace IKVM.Reflection.Reader private void Read(Stream stream, bool mapped) { BinaryReader br = new BinaryReader(stream); - peFile.Read(br, mapped); - stream.Seek(peFile.RvaToFileOffset(peFile.GetComDescriptorVirtualAddress()), SeekOrigin.Begin); - cliHeader.Read(br); - stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress), SeekOrigin.Begin); + + long pos = stream.Position; + uint header = br.ReadUInt32(); + stream.Seek(pos, SeekOrigin.Begin); + + if (header == 0x424a5342) + { + // Naked metadata file (enc/portable pdb) + this.isMetadataOnly = true; + } + else + { + peFile.Read(br, mapped); + stream.Seek(peFile.RvaToFileOffset(peFile.GetComDescriptorVirtualAddress()), SeekOrigin.Begin); + cliHeader.Read(br); + stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress), SeekOrigin.Begin); + } foreach (StreamHeader sh in ReadStreamHeaders(br, out imageRuntimeVersion)) { switch (sh.Name) @@ -149,9 +171,26 @@ namespace IKVM.Reflection.Reader break; case "#~": case "#-": - stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + sh.Offset), SeekOrigin.Begin); + if (isMetadataOnly) + { + stream.Seek(sh.Offset, SeekOrigin.Begin); + } + else + { + stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + sh.Offset), SeekOrigin.Begin); + } ReadTables(br); break; + case "#Pdb": + var entryPoint = br.ReadInt32 (); + var referencedTables = br.ReadUInt64 (); + var tableSizes = new int [64]; + for (int i = 0; i < 64; ++i) { + if ((referencedTables & ((ulong)1 << i)) != 0) + tableSizes [i] = (int)br.ReadUInt32 (); + } + pdbStream = new PdbStream () { EntryPoint = entryPoint, ReferencedTables = referencedTables, TableSizes = tableSizes }; + break; default: // we ignore unknown streams, because the CLR does so too // (and some obfuscators add bogus streams) @@ -204,6 +243,10 @@ namespace IKVM.Reflection.Reader { if ((Valid & (1UL << i)) != 0) { + if (tables[i] == null) + { + throw new NotImplementedException ("Unknown table " + i); + } tables[i].Sorted = (Sorted & (1UL << i)) != 0; tables[i].RowCount = br.ReadInt32(); } @@ -225,7 +268,14 @@ namespace IKVM.Reflection.Reader private byte[] ReadHeap(Stream stream, uint offset, uint size) { byte[] buf = new byte[size]; - stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + offset), SeekOrigin.Begin); + if (isMetadataOnly) + { + stream.Seek(offset, SeekOrigin.Begin); + } + else + { + stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + offset), SeekOrigin.Begin); + } for (int pos = 0; pos < buf.Length; ) { int read = stream.Read(buf, pos, buf.Length - pos); @@ -349,6 +399,13 @@ namespace IKVM.Reflection.Reader return ByteReader.FromBlob(blobHeap, blobIndex); } + internal override Guid GetGuid(int guidIndex) + { + byte[] buf = new byte[16]; + Buffer.BlockCopy(guidHeap, 16 * (guidIndex - 1), buf, 0, 16); + return new Guid(buf); + } + public override string ResolveString(int metadataToken) { string str; @@ -1149,8 +1206,17 @@ namespace IKVM.Reflection.Reader get { return metadataStreamVersion; } } + public override bool __IsMetadataOnly + { + get { return isMetadataOnly; } + } + public override void __GetDataDirectoryEntry(int index, out int rva, out int length) { + if (isMetadataOnly) + { + throw new NotSupportedException(); + } peFile.GetDataDirectoryEntry(index, out rva, out length); } |