.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
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.
 
 
 
 

227 lines
9.3 KiB

// Copyright (c) 2018 Siegfried Pammer
//
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Security.Cryptography;
using System.Text;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.DebugInfo
{
public class PortablePdbWriter
{
public static readonly Guid CSharpLanguageGuid = new Guid("3f5162f8-07c6-11d3-9053-00c04fa302a1");
public static readonly Guid DebugInfoEmbeddedSource = new Guid("0e8a571b-6926-466e-b4ad-8ab04611f5fe");
public static readonly Guid HashAlgorithmSHA1 = new Guid("ff1816ec-aa5e-4d10-87f7-6f4963833460");
public static readonly Guid HashAlgorithmSHA256 = new Guid("8829d00f-11b8-4213-878b-770e8597ac16");
static readonly FileVersionInfo decompilerVersion = FileVersionInfo.GetVersionInfo(typeof(CSharpDecompiler).Assembly.Location);
public static bool HasCodeViewDebugDirectoryEntry(PEFile file)
{
return file.Reader.ReadDebugDirectory().Any(entry => entry.Type == DebugDirectoryEntryType.CodeView);
}
public static void WritePdb(PEFile file, CSharpDecompiler decompiler, DecompilerSettings settings, Stream targetStream)
{
MetadataBuilder metadata = new MetadataBuilder();
MetadataReader reader = file.Metadata;
var entrypointHandle = MetadataTokens.MethodDefinitionHandle(file.Reader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress);
var hasher = SHA256.Create();
var sequencePointBlobs = new Dictionary<MethodDefinitionHandle, (DocumentHandle Document, BlobHandle SequencePoints)>();
var emptyList = new List<SequencePoint>();
var globalImportScope = metadata.AddImportScope(default, default);
foreach (var handle in reader.GetTopLevelTypeDefinitions()) {
var type = reader.GetTypeDefinition(handle);
// Generate syntax tree, source and checksum
var name = metadata.GetOrAddDocumentName(type.GetFullTypeName(reader).ReflectionName.Replace('.', Path.DirectorySeparatorChar) + ".cs");
var syntaxTree = decompiler.DecompileTypes(new[] { handle });
syntaxTree.InsertChildAfter(null, new Comment(" PDB and source generated by ICSharpCode.Decompiler " + decompilerVersion.FileVersion), Roles.Comment);
var sourceText = SyntaxTreeToString(syntaxTree, settings);
var sourceCheckSum = hasher.ComputeHash(Encoding.UTF8.GetBytes(sourceText));
var sourceBlob = WriteSourceToBlob(metadata, sourceText);
// Create Document(Handle)
var document = metadata.AddDocument(name,
hashAlgorithm: metadata.GetOrAddGuid(HashAlgorithmSHA256),
hash: metadata.GetOrAddBlob(sourceCheckSum),
language: metadata.GetOrAddGuid(CSharpLanguageGuid));
// Add embedded source to the PDB
metadata.AddCustomDebugInformation(document, metadata.GetOrAddGuid(DebugInfoEmbeddedSource), sourceBlob);
ScopesGenerator.Generate(decompiler.TypeSystem, metadata, globalImportScope, syntaxTree);
// Generate sequence points for the syntax tree
var sequencePoints = decompiler.CreateSequencePoints(syntaxTree).ToDictionary(sp => (MethodDefinitionHandle)sp.Key.Method.MetadataToken, sp => sp.Value);
foreach (var method in type.GetMethods()) {
ProcessMethod(method, document, sequencePoints, syntaxTree);
}
foreach (var nestedTypeHandle in type.GetNestedTypes()) {
var nestedType = reader.GetTypeDefinition(nestedTypeHandle);
foreach (var method in nestedType.GetMethods()) {
ProcessMethod(method, document, sequencePoints, syntaxTree);
}
}
}
foreach (var method in reader.MethodDefinitions) {
var md = reader.GetMethodDefinition(method);
if (sequencePointBlobs.TryGetValue(method, out var info)) {
metadata.AddMethodDebugInformation(info.Document, info.SequencePoints);
} else {
metadata.AddMethodDebugInformation(default, default);
}
}
var debugDir = file.Reader.ReadDebugDirectory().FirstOrDefault(dir => dir.Type == DebugDirectoryEntryType.CodeView);
var portable = file.Reader.ReadCodeViewDebugDirectoryData(debugDir);
var contentId = new BlobContentId(portable.Guid, debugDir.Stamp);
PortablePdbBuilder serializer = new PortablePdbBuilder(metadata, GetRowCounts(reader), entrypointHandle, blobs => contentId);
BlobBuilder blobBuilder = new BlobBuilder();
serializer.Serialize(blobBuilder);
blobBuilder.WriteContentTo(targetStream);
void ProcessMethod(MethodDefinitionHandle method, DocumentHandle document,
Dictionary<MethodDefinitionHandle, List<SequencePoint>> sequencePoints, SyntaxTree syntaxTree)
{
var methodDef = reader.GetMethodDefinition(method);
int localSignatureRowId;
MethodBodyBlock methodBody;
if (methodDef.RelativeVirtualAddress != 0) {
methodBody = file.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
localSignatureRowId = methodBody.LocalSignature.IsNil ? 0 : MetadataTokens.GetRowNumber(methodBody.LocalSignature);
} else {
methodBody = null;
localSignatureRowId = 0;
}
if (!sequencePoints.TryGetValue(method, out var points))
points = emptyList;
if (points.Count == 0)
sequencePointBlobs.Add(method, (default, default));
else
sequencePointBlobs.Add(method, (document, EncodeSequencePoints(metadata, localSignatureRowId, points)));
}
}
static BlobHandle WriteSourceToBlob(MetadataBuilder metadata, string sourceText)
{
var builder = new BlobBuilder();
builder.WriteInt32(0); // uncompressed
builder.WriteUTF8(sourceText);
return metadata.GetOrAddBlob(builder);
}
static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List<SequencePoint> sequencePoints)
{
if (sequencePoints.Count == 0)
return default;
var writer = new BlobBuilder();
// header:
writer.WriteCompressedInteger(localSignatureRowId);
int previousOffset = -1;
int previousStartLine = -1;
int previousStartColumn = -1;
for (int i = 0; i < sequencePoints.Count; i++) {
var sequencePoint = sequencePoints[i];
// delta IL offset:
if (i > 0)
writer.WriteCompressedInteger(sequencePoint.Offset - previousOffset);
else
writer.WriteCompressedInteger(sequencePoint.Offset);
previousOffset = sequencePoint.Offset;
if (sequencePoint.IsHidden) {
writer.WriteInt16(0);
continue;
}
int lineDelta = sequencePoint.EndLine - sequencePoint.StartLine;
int columnDelta = sequencePoint.EndColumn - sequencePoint.StartColumn;
writer.WriteCompressedInteger(lineDelta);
if (lineDelta == 0) {
writer.WriteCompressedInteger(columnDelta);
} else {
writer.WriteCompressedSignedInteger(columnDelta);
}
if (previousStartLine < 0) {
writer.WriteCompressedInteger(sequencePoint.StartLine);
writer.WriteCompressedInteger(sequencePoint.StartColumn);
} else {
writer.WriteCompressedSignedInteger(sequencePoint.StartLine - previousStartLine);
writer.WriteCompressedSignedInteger(sequencePoint.StartColumn - previousStartColumn);
}
previousStartLine = sequencePoint.StartLine;
previousStartColumn = sequencePoint.StartColumn;
}
return metadata.GetOrAddBlob(writer);
}
static ImmutableArray<int> GetRowCounts(MetadataReader reader)
{
var builder = ImmutableArray.CreateBuilder<int>(MetadataTokens.TableCount);
for (int i = 0; i < MetadataTokens.TableCount; i++) {
builder.Add(reader.GetTableRowCount((TableIndex)i));
}
return builder.MoveToImmutable();
}
static string SyntaxTreeToString(SyntaxTree syntaxTree, DecompilerSettings settings)
{
StringWriter w = new StringWriter();
TokenWriter tokenWriter = new TextWriterTokenWriter(w);
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
tokenWriter = TokenWriter.WrapInWriterThatSetsLocationsInAST(tokenWriter);
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions));
return w.ToString();
}
}
}