mirror of
https://github.com/VSadov/Satori.git
synced 2025-06-10 18:11:04 +09:00
Enable basic generation of ngen pdb from composite image (#36311)
* Enable basic generation of ngen pdb from composite image - Port non-line number handling portion of ngen pdb from crossgen to r2rdump - This pdb generation logic may be used for both composite and non-composite R2R images - Only pdb generation is supported. Perfmap generation for unix is not supported - pdb generation is only supported on Windows x86 and amd64 platforms. Diasymreader is not supported on other systems - Pdb generation does not generation symbols with the same names as crossgen. Instead it uses the name generator from r2rdump. For current needs this should be sufficient - Update composite file format so that pdb generation process will work. Major difference is that a CorHeader is always produced for composite images now. This CorHeader is not used by the runtime, but is required for DiaSymReader to generate an ngen pdb.
This commit is contained in:
parent
363ac3692c
commit
b764cae5f8
17 changed files with 751 additions and 100 deletions
|
@ -28,8 +28,9 @@ in the COFF header represent a full copy of the input IL and MSIL metadata it wa
|
|||
|
||||
**Composite R2R files** currently conform to Windows PE executable file format as the
|
||||
native envelope. Moving forward we plan to gradually add support for platform-native
|
||||
executable formats (ELF on Linux, MachO on OSX) as the native envelopes. As a natural corollary
|
||||
there is no global CLI / COR header in the file. The ReadyToRun header structure is pointed to
|
||||
executable formats (ELF on Linux, MachO on OSX) as the native envelopes. There is a
|
||||
global CLI / COR header in the file, but it only exists to facilitate pdb generation, and does
|
||||
not participate in any usages by the CoreCLR runtime. The ReadyToRun header structure is pointed to
|
||||
by the well-known export symbol `RTR_HEADER` and has the `READYTORUN_FLAG_COMPOSITE` flag set.
|
||||
|
||||
Input MSIL metadata and IL streams can be either embedded in the composite R2R file or left
|
||||
|
|
35
eng/DiaSymReaderNative.targets
Normal file
35
eng/DiaSymReaderNative.targets
Normal file
|
@ -0,0 +1,35 @@
|
|||
<!-- Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information. -->
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<!--
|
||||
Ensure the binaries using DiaSymReader.Native are marked as AnyCPU unless they specify
|
||||
a different target. This should be the default behavior but recent SDK changes to more
|
||||
correctly consider RIDs caused our behavior to change here. Once the SDK logic is settled
|
||||
here we can remove this.
|
||||
|
||||
https://github.com/dotnet/sdk/issues/3495
|
||||
-->
|
||||
<PlatformTarget Condition="'$(PlatformTarget)' == ''">AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
This is adding the diasymreader native assets to the output directory of our binaries. The
|
||||
package can't be referenced directly but rather has to have it's assets manually copied
|
||||
out. This logic is responsible for doing that.
|
||||
-->
|
||||
<ItemGroup Condition="'$(DotNetBuildFromSource)' != 'true'">
|
||||
<Content Include="$(NuGetPackageRoot)\microsoft.diasymreader.native\$(MicrosoftDiaSymReaderNativeVersion)\runtimes\win\native\Microsoft.DiaSymReader.Native.x86.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Visible>false</Visible>
|
||||
<Pack>false</Pack>
|
||||
</Content>
|
||||
<Content Include="$(NuGetPackageRoot)\microsoft.diasymreader.native\$(MicrosoftDiaSymReaderNativeVersion)\runtimes\win\native\Microsoft.DiaSymReader.Native.amd64.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Visible>false</Visible>
|
||||
<Pack>false</Pack>
|
||||
</Content>
|
||||
|
||||
<PackageReference Include="Microsoft.DiaSymReader.Native" Version="$(MicrosoftDiaSymReaderNativeVersion)" ExcludeAssets="all"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -150,8 +150,6 @@ namespace ILCompiler.DependencyAnalysis
|
|||
if (node is NativeDebugDirectoryEntryNode nddeNode)
|
||||
{
|
||||
// There should be only one NativeDebugDirectoryEntry.
|
||||
// This assert will need to be revisited when we implement the composite R2R format, where we'll need to figure
|
||||
// out how native symbols will be emitted, and verify that the DiaSymReader library is able to consume them.
|
||||
Debug.Assert(nativeDebugDirectoryEntryNode == null);
|
||||
nativeDebugDirectoryEntryNode = nddeNode;
|
||||
}
|
||||
|
@ -176,11 +174,8 @@ namespace ILCompiler.DependencyAnalysis
|
|||
EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, _mapFileBuilder);
|
||||
}
|
||||
|
||||
if (!_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode || _componentModule != null)
|
||||
{
|
||||
r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size);
|
||||
r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size);
|
||||
}
|
||||
r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size);
|
||||
r2rPeBuilder.SetDebugDirectory(_nodeFactory.DebugDirectoryNode, _nodeFactory.DebugDirectoryNode.Size);
|
||||
|
||||
if (_nodeFactory.Win32ResourcesNode != null)
|
||||
{
|
||||
|
|
|
@ -51,7 +51,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
|
||||
{
|
||||
sb.Append(nameMangler.CompilationUnitPrefix);
|
||||
sb.Append($"__CorHeader_{_module.Assembly.GetName().Name}");
|
||||
if (_module != null)
|
||||
sb.Append($"__CorHeader_{_module.Assembly.GetName().Name}");
|
||||
else
|
||||
sb.Append("__CompositeCorHeader_");
|
||||
}
|
||||
|
||||
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
|
||||
|
@ -62,75 +65,116 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
builder.RequireInitialPointerAlignment();
|
||||
builder.AddSymbol(this);
|
||||
|
||||
BlobReader reader = _module.PEReader.GetEntireImage().GetReader();
|
||||
reader.Offset = _module.PEReader.PEHeaders.CorHeaderStartOffset;
|
||||
|
||||
// Header Size
|
||||
int headerSize = reader.ReadInt32();
|
||||
builder.EmitInt(headerSize);
|
||||
|
||||
// Runtime major, minor version
|
||||
builder.EmitUShort(reader.ReadUInt16());
|
||||
builder.EmitUShort(reader.ReadUInt16());
|
||||
|
||||
// Metadata Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
var metadataBlob = factory.CopiedMetadataBlob(_module);
|
||||
builder.EmitReloc(metadataBlob, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitInt(metadataBlob.Size);
|
||||
|
||||
// Flags
|
||||
builder.EmitUInt((uint)(((CorFlags)reader.ReadUInt32() & ~CorFlags.ILOnly) | CorFlags.ILLibrary));
|
||||
|
||||
// Entrypoint
|
||||
builder.EmitInt(reader.ReadInt32());
|
||||
|
||||
// Resources Directory
|
||||
if (ReadDirectoryEntry(ref reader).Size > 0)
|
||||
if (_module != null)
|
||||
{
|
||||
var managedResources = factory.CopiedManagedResources(_module);
|
||||
builder.EmitReloc(managedResources, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitInt(managedResources.Size);
|
||||
BlobReader reader = _module.PEReader.GetEntireImage().GetReader();
|
||||
reader.Offset = _module.PEReader.PEHeaders.CorHeaderStartOffset;
|
||||
|
||||
// Header Size
|
||||
int headerSize = reader.ReadInt32();
|
||||
builder.EmitInt(headerSize);
|
||||
|
||||
// Runtime major, minor version
|
||||
builder.EmitUShort(reader.ReadUInt16());
|
||||
builder.EmitUShort(reader.ReadUInt16());
|
||||
|
||||
// Metadata Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
var metadataBlob = factory.CopiedMetadataBlob(_module);
|
||||
builder.EmitReloc(metadataBlob, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitInt(metadataBlob.Size);
|
||||
|
||||
// Flags
|
||||
builder.EmitUInt((uint)(((CorFlags)reader.ReadUInt32() & ~CorFlags.ILOnly) | CorFlags.ILLibrary));
|
||||
|
||||
// Entrypoint
|
||||
builder.EmitInt(reader.ReadInt32());
|
||||
|
||||
// Resources Directory
|
||||
if (ReadDirectoryEntry(ref reader).Size > 0)
|
||||
{
|
||||
var managedResources = factory.CopiedManagedResources(_module);
|
||||
builder.EmitReloc(managedResources, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitInt(managedResources.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
}
|
||||
|
||||
// Strong Name Signature Directory
|
||||
if (ReadDirectoryEntry(ref reader).Size > 0)
|
||||
{
|
||||
var strongNameSignature = factory.CopiedStrongNameSignature(_module);
|
||||
builder.EmitReloc(strongNameSignature, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitInt(strongNameSignature.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
}
|
||||
|
||||
// Code Manager Table Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// VTable Fixups Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// Export Address Table Jumps Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// Managed Native (ReadyToRun) Header Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
builder.EmitReloc(factory.Header, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitReloc(factory.Header, RelocType.IMAGE_REL_SYMBOL_SIZE);
|
||||
|
||||
// Did we fully read the header?
|
||||
Debug.Assert(reader.Offset - headerSize == _module.PEReader.PEHeaders.CorHeaderStartOffset);
|
||||
Debug.Assert(builder.CountBytes == headerSize);
|
||||
Debug.Assert(headerSize == Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generating CORHeader for composite image
|
||||
// Header Size
|
||||
builder.EmitInt(Size);
|
||||
|
||||
// Runtime major, minor version
|
||||
builder.EmitUShort(0);
|
||||
builder.EmitUShort(0);
|
||||
|
||||
// Metadata Directory
|
||||
builder.EmitReloc(factory.ManifestMetadataTable, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitReloc(factory.ManifestMetadataTable, RelocType.IMAGE_REL_SYMBOL_SIZE);
|
||||
|
||||
// Flags
|
||||
builder.EmitUInt(0);
|
||||
|
||||
// Entrypoint
|
||||
builder.EmitInt(0);
|
||||
|
||||
// Resources Directory
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
}
|
||||
|
||||
// Strong Name Signature Directory
|
||||
if (ReadDirectoryEntry(ref reader).Size > 0)
|
||||
{
|
||||
var strongNameSignature = factory.CopiedStrongNameSignature(_module);
|
||||
builder.EmitReloc(strongNameSignature, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitInt(strongNameSignature.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Strong Name Signature Directory
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// Code Manager Table Directory
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// VTable Fixups Directory
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// Export Address Table Jumps Directory
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// Managed Native (ReadyToRun) Header Directory
|
||||
builder.EmitReloc(factory.Header, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitReloc(factory.Header, RelocType.IMAGE_REL_SYMBOL_SIZE);
|
||||
}
|
||||
|
||||
|
||||
// Code Manager Table Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// VTable Fixups Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// Export Address Table Jumps Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
WriteEmptyDirectoryEntry(ref builder);
|
||||
|
||||
// Managed Native (ReadyToRun) Header Directory
|
||||
ReadDirectoryEntry(ref reader);
|
||||
builder.EmitReloc(factory.Header, RelocType.IMAGE_REL_BASED_ADDR32NB);
|
||||
builder.EmitReloc(factory.Header, RelocType.IMAGE_REL_SYMBOL_SIZE);
|
||||
|
||||
// Did we fully read the header?
|
||||
Debug.Assert(reader.Offset - headerSize == _module.PEReader.PEHeaders.CorHeaderStartOffset);
|
||||
Debug.Assert(builder.CountBytes == headerSize);
|
||||
Debug.Assert(headerSize == Size);
|
||||
|
||||
return builder.ToObjectData();
|
||||
}
|
||||
|
@ -143,6 +187,17 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
|
||||
{
|
||||
if (_module == null)
|
||||
{
|
||||
if (((CopiedCorHeaderNode)other)._module == null)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
else if (((CopiedCorHeaderNode)other)._module == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return _module.CompareTo(((CopiedCorHeaderNode)other)._module);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,14 +56,18 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
public unsafe int Size => RSDSSize;
|
||||
|
||||
public NativeDebugDirectoryEntryNode(EcmaModule sourceModule)
|
||||
: base(sourceModule)
|
||||
{ }
|
||||
public NativeDebugDirectoryEntryNode(string pdbName)
|
||||
: base(null)
|
||||
{
|
||||
_pdbName = pdbName;
|
||||
}
|
||||
|
||||
private string _pdbName;
|
||||
|
||||
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
|
||||
{
|
||||
sb.Append(nameMangler.CompilationUnitPrefix);
|
||||
sb.Append($"__NativeRvaBlob_{_module.Assembly.GetName().Name}");
|
||||
sb.Append($"__NativeDebugDirectory_{_pdbName.Replace('.','_')}");
|
||||
}
|
||||
|
||||
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
|
||||
|
@ -99,7 +103,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
// Age
|
||||
writer.Write(1);
|
||||
|
||||
string pdbFileName = _module.Assembly.GetName().Name + ".ni.pdb";
|
||||
string pdbFileName = _pdbName;
|
||||
byte[] pdbFileNameBytes = Encoding.UTF8.GetBytes(pdbFileName);
|
||||
writer.Write(pdbFileNameBytes);
|
||||
|
||||
|
@ -107,6 +111,11 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
return rsdsEntry.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
|
||||
{
|
||||
return _pdbName.CompareTo(((NativeDebugDirectoryEntryNode)other)._pdbName);
|
||||
}
|
||||
}
|
||||
|
||||
public class CopiedDebugDirectoryEntryNode : DebugDirectoryEntryNode
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using Internal.Text;
|
||||
using Internal.TypeSystem.Ecma;
|
||||
|
@ -25,10 +26,17 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
sizeof(int); // PointerToRawData
|
||||
|
||||
private EcmaModule _module;
|
||||
private NativeDebugDirectoryEntryNode _nativeEntry;
|
||||
|
||||
public DebugDirectoryNode(EcmaModule sourceModule)
|
||||
public DebugDirectoryNode(EcmaModule sourceModule, string outputFileName)
|
||||
{
|
||||
_module = sourceModule;
|
||||
string pdbNameRoot = Path.GetFileNameWithoutExtension(outputFileName);
|
||||
if (sourceModule != null)
|
||||
{
|
||||
pdbNameRoot = sourceModule.Assembly.GetName().Name;
|
||||
}
|
||||
_nativeEntry = new NativeDebugDirectoryEntryNode(pdbNameRoot + ".ni.pdb");
|
||||
}
|
||||
|
||||
public override ObjectNodeSection Section => ObjectNodeSection.TextSection;
|
||||
|
@ -48,13 +56,22 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
|
||||
{
|
||||
sb.Append(nameMangler.CompilationUnitPrefix);
|
||||
sb.Append($"__DebugDirectory_{_module.Assembly.GetName().Name}");
|
||||
string directoryName;
|
||||
if (_module != null)
|
||||
directoryName = _module.Assembly.GetName().Name;
|
||||
else
|
||||
directoryName = "Composite";
|
||||
|
||||
sb.Append($"__DebugDirectory_{directoryName}");
|
||||
}
|
||||
|
||||
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler);
|
||||
|
||||
int GetNumDebugDirectoryEntriesInModule()
|
||||
{
|
||||
if (_module == null)
|
||||
return 0;
|
||||
|
||||
ImmutableArray<DebugDirectoryEntry> entries = _module.PEReader.ReadDebugDirectory();
|
||||
return entries == null ? 0 : entries.Length;
|
||||
}
|
||||
|
@ -65,12 +82,15 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
builder.RequireInitialPointerAlignment();
|
||||
builder.AddSymbol(this);
|
||||
|
||||
ImmutableArray<DebugDirectoryEntry> entries = _module.PEReader.ReadDebugDirectory();
|
||||
ImmutableArray<DebugDirectoryEntry> entries = default(ImmutableArray<DebugDirectoryEntry>);
|
||||
if (_module != null)
|
||||
entries = _module.PEReader.ReadDebugDirectory();
|
||||
|
||||
int numEntries = GetNumDebugDirectoryEntriesInModule();
|
||||
|
||||
// First, write the native debug directory entry
|
||||
{
|
||||
var entry = (NativeDebugDirectoryEntryNode)factory.DebugDirectoryEntry(_module, -1);
|
||||
var entry = _nativeEntry;
|
||||
|
||||
builder.EmitUInt(0 /* Characteristics */);
|
||||
if (numEntries > 0)
|
||||
|
@ -121,6 +141,17 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
|
||||
{
|
||||
if (_module == null)
|
||||
{
|
||||
if (((DebugDirectoryNode)other)._module == null)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
else if (((DebugDirectoryNode)other)._module == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return _module.CompareTo(((DebugDirectoryNode)other)._module);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,11 +133,13 @@ namespace ILCompiler.DependencyAnalysis
|
|||
Module = module;
|
||||
}
|
||||
|
||||
public bool Equals(ModuleAndIntValueKey other) => IntValue == other.IntValue && Module.Equals(other.Module);
|
||||
public bool Equals(ModuleAndIntValueKey other) => IntValue == other.IntValue && ((Module == null && other.Module == null) || Module.Equals(other.Module));
|
||||
public override bool Equals(object obj) => obj is ModuleAndIntValueKey && Equals((ModuleAndIntValueKey)obj);
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hashCode = IntValue * 0x5498341 + 0x832424;
|
||||
if (Module == null)
|
||||
return hashCode;
|
||||
return hashCode * 23 + Module.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
@ -255,9 +257,6 @@ namespace ILCompiler.DependencyAnalysis
|
|||
|
||||
_debugDirectoryEntries = new NodeCache<ModuleAndIntValueKey, DebugDirectoryEntryNode>(key =>
|
||||
{
|
||||
if (key.IntValue < 0)
|
||||
return new NativeDebugDirectoryEntryNode(key.Module);
|
||||
else
|
||||
return new CopiedDebugDirectoryEntryNode(key.Module, key.IntValue);
|
||||
});
|
||||
|
||||
|
@ -657,10 +656,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
graph.AddRoot(PrecodeImports, "Precode helper imports are always generated");
|
||||
graph.AddRoot(StringImports, "String imports are always generated");
|
||||
graph.AddRoot(Header, "ReadyToRunHeader is always generated");
|
||||
if (!CompilationModuleGroup.IsCompositeBuildMode)
|
||||
{
|
||||
graph.AddRoot(CopiedCorHeaderNode, "MSIL COR header is always generated for single-file R2R files");
|
||||
}
|
||||
graph.AddRoot(CopiedCorHeaderNode, "MSIL COR header is always generated for R2R files");
|
||||
graph.AddRoot(DebugDirectoryNode, "Debug Directory will always contain at least one entry");
|
||||
|
||||
if (Win32ResourcesNode != null)
|
||||
|
|
|
@ -302,7 +302,7 @@ namespace ILCompiler
|
|||
EcmaModule inputModule = NodeFactory.TypeSystemContext.GetModuleFromPath(inputFile);
|
||||
|
||||
CopiedCorHeaderNode copiedCorHeader = new CopiedCorHeaderNode(inputModule);
|
||||
DebugDirectoryNode debugDirectory = new DebugDirectoryNode(inputModule);
|
||||
DebugDirectoryNode debugDirectory = new DebugDirectoryNode(inputModule, outputFile);
|
||||
NodeFactory componentFactory = new NodeFactory(
|
||||
_nodeFactory.TypeSystemContext,
|
||||
_nodeFactory.CompilationModuleGroup,
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace ILCompiler
|
|||
private InstructionSetSupport _instructionSetSupport;
|
||||
|
||||
private string _jitPath;
|
||||
private string _outputFile;
|
||||
|
||||
// These need to provide reasonable defaults so that the user can optionally skip
|
||||
// calling the Use/Configure methods and still get something reasonable back.
|
||||
|
@ -115,13 +116,20 @@ namespace ILCompiler
|
|||
return this;
|
||||
}
|
||||
|
||||
public ReadyToRunCodegenCompilationBuilder GenerateOutputFile(string outputFile)
|
||||
{
|
||||
_outputFile = outputFile;
|
||||
return this;
|
||||
}
|
||||
|
||||
public override ICompilation ToCompilation()
|
||||
{
|
||||
// TODO: only copy COR headers for single-assembly build and for composite build with embedded MSIL
|
||||
IEnumerable<EcmaModule> inputModules = _compilationGroup.CompilationModuleSet;
|
||||
CopiedCorHeaderNode corHeaderNode = (_compilationGroup.IsCompositeBuildMode ? null : new CopiedCorHeaderNode(inputModules.First()));
|
||||
EcmaModule singleModule = _compilationGroup.IsCompositeBuildMode ? null : inputModules.First();
|
||||
CopiedCorHeaderNode corHeaderNode = new CopiedCorHeaderNode(singleModule);
|
||||
// TODO: proper support for multiple input files
|
||||
DebugDirectoryNode debugDirectoryNode = new DebugDirectoryNode(inputModules.First());
|
||||
DebugDirectoryNode debugDirectoryNode = new DebugDirectoryNode(singleModule, _outputFile);
|
||||
|
||||
// Produce a ResourceData where the IBC PROFILE_DATA entry has been filtered out
|
||||
// TODO: proper support for multiple input files
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<AssemblyName>ILCompiler.ReadyToRun</AssemblyName>
|
||||
|
|
|
@ -411,14 +411,20 @@ namespace ILCompiler.Reflection.ReadyToRun
|
|||
{
|
||||
if ((PEReader.PEHeaders.CorHeader.Flags & CorFlags.ILLibrary) == 0)
|
||||
{
|
||||
throw new BadImageFormatException("The file is not a ReadyToRun image");
|
||||
if (!TryLocateNativeReadyToRunHeader())
|
||||
throw new BadImageFormatException("The file is not a ReadyToRun image");
|
||||
|
||||
Debug.Assert(Composite);
|
||||
}
|
||||
else
|
||||
{
|
||||
_assemblyCache.Add(metadata);
|
||||
|
||||
DirectoryEntry r2rHeaderDirectory = PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory;
|
||||
_readyToRunHeaderRVA = r2rHeaderDirectory.RelativeVirtualAddress;
|
||||
Debug.Assert(!Composite);
|
||||
}
|
||||
|
||||
_assemblyCache.Add(metadata);
|
||||
|
||||
DirectoryEntry r2rHeaderDirectory = PEReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory;
|
||||
_readyToRunHeaderRVA = r2rHeaderDirectory.RelativeVirtualAddress;
|
||||
Debug.Assert(!Composite);
|
||||
}
|
||||
else if (!TryLocateNativeReadyToRunHeader())
|
||||
{
|
||||
|
|
|
@ -476,6 +476,7 @@ namespace ILCompiler
|
|||
.UseParallelism(_commandLineOptions.Parallelism)
|
||||
.UseJitPath(_commandLineOptions.JitPath)
|
||||
.UseInstructionSetSupport(instructionSetSupport)
|
||||
.GenerateOutputFile(_commandLineOptions.OutputFilePath.FullName)
|
||||
.UseILProvider(ilProvider)
|
||||
.UseBackendOptions(_commandLineOptions.CodegenOptions)
|
||||
.UseLogger(logger)
|
||||
|
|
|
@ -36,6 +36,8 @@ namespace R2RDump
|
|||
command.AddOption(new Option(new[] { "--referencePath", "--rp" }, "Search paths for reference assemblies", new Argument<DirectoryInfo[]>()));
|
||||
command.AddOption(new Option(new[] { "--inlineSignatureBinary", "--isb" }, "Embed binary signature into its textual representation", new Argument<bool>()));
|
||||
command.AddOption(new Option(new[] { "--signatureBinary", "--sb" }, "Append signature binary to its textual representation", new Argument<bool>()));
|
||||
command.AddOption(new Option(new[] { "--create-pdb" }, "Create PDB", new Argument<bool>()));
|
||||
command.AddOption(new Option(new[] { "--pdb-path" }, "PDB output path for --createpdb", new Argument<string>()));
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
|
85
src/coreclr/src/tools/r2rdump/ISymNGenWriter.cs
Normal file
85
src/coreclr/src/tools/r2rdump/ISymNGenWriter.cs
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#pragma warning disable 436 // SuppressUnmanagedCodeSecurityAttribute defined in source and mscorlib
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.DiaSymReader
|
||||
{
|
||||
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("D682FD12-43dE-411C-811B-BE8404CEA126"), SuppressUnmanagedCodeSecurity]
|
||||
internal interface ISymNGenWriter
|
||||
{
|
||||
// Add a new public symbol to the NGEN PDB.
|
||||
void AddSymbol([MarshalAs(UnmanagedType.BStr)] string pSymbol,
|
||||
ushort iSection,
|
||||
ulong rva);
|
||||
|
||||
// Adds a new section to the NGEN PDB.
|
||||
void AddSection(ushort iSection,
|
||||
OMF flags,
|
||||
int offset,
|
||||
int cb);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
internal enum OMF : ushort
|
||||
{
|
||||
Const_Read = 0x0001,
|
||||
Const_Write = 0x0002,
|
||||
Const_Exec = 0x0004,
|
||||
Const_F32Bit = 0x0008,
|
||||
Const_ReservedBits1 = 0x00f0,
|
||||
Const_FSel = 0x0100,
|
||||
Const_FAbs = 0x0200,
|
||||
Const_ReservedBits2 = 0x0C00,
|
||||
Const_FGroup = 0x1000,
|
||||
Const_ReservedBits3 = 0xE000,
|
||||
|
||||
|
||||
StandardText = (Const_FSel|Const_F32Bit|Const_Exec|Const_Read), // 0x10D
|
||||
SentinelType = (Const_FAbs|Const_F32Bit) // 0x208
|
||||
}
|
||||
|
||||
|
||||
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("B029E51B-4C55-4fe2-B993-9F7BC1F10DB4"), SuppressUnmanagedCodeSecurity]
|
||||
internal interface ISymNGenWriter2 : ISymNGenWriter
|
||||
{
|
||||
// Add a new public symbol to the NGEN PDB.
|
||||
new void AddSymbol([MarshalAs(UnmanagedType.BStr)] string pSymbol,
|
||||
ushort iSection,
|
||||
ulong rva);
|
||||
|
||||
// Adds a new section to the NGEN PDB.
|
||||
new void AddSection(ushort iSection,
|
||||
OMF flags,
|
||||
int offset,
|
||||
int cb);
|
||||
|
||||
void OpenModW([MarshalAs(UnmanagedType.LPWStr)] string wszModule,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string wszObjFile,
|
||||
out UIntPtr ppmod);
|
||||
|
||||
void CloseMod(UIntPtr pmod);
|
||||
|
||||
void ModAddSymbols(UIntPtr pmod, [MarshalAs(UnmanagedType.LPArray)] byte[] pbSym, int cb);
|
||||
|
||||
void ModAddSecContribEx(
|
||||
UIntPtr pmod,
|
||||
ushort isect,
|
||||
int off,
|
||||
int cb,
|
||||
uint dwCharacteristics,
|
||||
uint dwDataCrc,
|
||||
uint dwRelocCrc);
|
||||
|
||||
void QueryPDBNameExW(
|
||||
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder pdb,
|
||||
IntPtr cchMax);
|
||||
}
|
||||
}
|
394
src/coreclr/src/tools/r2rdump/PdbWriter.cs
Normal file
394
src/coreclr/src/tools/r2rdump/PdbWriter.cs
Normal file
|
@ -0,0 +1,394 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.DiaSymReader;
|
||||
|
||||
namespace ILCompiler.PdbWriter
|
||||
{
|
||||
// NGEN always generates PDBs with public symbols lists (so tools can map IP ranges to
|
||||
// methods). This bitmask indicates what extra info should be added to the PDB
|
||||
[Flags]
|
||||
public enum PDBExtraData
|
||||
{
|
||||
None = 0,
|
||||
// Add string table subsection, files checksum subsection, and lines subsection to
|
||||
// allow tools to map IP ranges to source lines.
|
||||
kPDBLines = 0x00000001,
|
||||
};
|
||||
|
||||
struct MethodInfo
|
||||
{
|
||||
public string AssemblyName;
|
||||
public uint MethodToken;
|
||||
public uint HotRVA;
|
||||
public string Name;
|
||||
public uint ColdRVA;
|
||||
}
|
||||
|
||||
interface IModuleData
|
||||
{
|
||||
IEnumerable<MethodInfo> Methods { get; }
|
||||
}
|
||||
|
||||
public enum SymChecksumType : byte
|
||||
{
|
||||
None = 0, // indicates no checksum is available
|
||||
MD5,
|
||||
SHA1,
|
||||
SHA_256,
|
||||
};
|
||||
|
||||
class SymDocument : IEquatable<SymDocument>
|
||||
{
|
||||
public string Name;
|
||||
public SymChecksumType ChecksumType;
|
||||
public byte[] Checksum;
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (other is SymDocument documentOther)
|
||||
{
|
||||
return Equals(documentOther);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Equals(SymDocument other)
|
||||
{
|
||||
if (Name != other.Name)
|
||||
return false;
|
||||
if (ChecksumType != other.ChecksumType)
|
||||
return false;
|
||||
if (Checksum.Length != other.Checksum.Length)
|
||||
return false;
|
||||
for (int i = 0; i < Checksum.Length; i++)
|
||||
{
|
||||
if (Checksum[i] != other.Checksum[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class PdbWriter
|
||||
{
|
||||
string _pdbPath;
|
||||
PDBExtraData _pdbExtraData;
|
||||
|
||||
string _pdbFilePath;
|
||||
string _tempSourceDllName;
|
||||
|
||||
List<SymDocument> _symDocuments = new List<SymDocument>();
|
||||
Dictionary<string,int> _stringTableToOffsetMapping;
|
||||
Dictionary<SymDocument,int> _documentToChecksumOffsetMapping;
|
||||
|
||||
UIntPtr _pdbMod;
|
||||
ISymNGenWriter2 _ngenWriter;
|
||||
|
||||
private const string DiaSymReaderModuleName32 = "Microsoft.DiaSymReader.Native.x86.dll";
|
||||
private const string DiaSymReaderModuleName64 = "Microsoft.DiaSymReader.Native.amd64.dll";
|
||||
|
||||
private const string CreateNGenPdbWriterFactoryName = "CreateNGenPdbWriter";
|
||||
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)]
|
||||
[DllImport(DiaSymReaderModuleName32, EntryPoint = CreateNGenPdbWriterFactoryName, PreserveSig = false)]
|
||||
private extern static void CreateNGenPdbWriter32([MarshalAs(UnmanagedType.LPWStr)] string ngenImagePath, [MarshalAs(UnmanagedType.LPWStr)] string pdbPath, [MarshalAs(UnmanagedType.IUnknown)] out object ngenPdbWriter);
|
||||
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.SafeDirectories)]
|
||||
[DllImport(DiaSymReaderModuleName64, EntryPoint = CreateNGenPdbWriterFactoryName, PreserveSig = false)]
|
||||
private extern static void CreateNGenPdbWriter64([MarshalAs(UnmanagedType.LPWStr)] string ngenImagePath, [MarshalAs(UnmanagedType.LPWStr)] string pdbPath, [MarshalAs(UnmanagedType.IUnknown)] out object ngenPdbWriter);
|
||||
|
||||
private static ISymNGenWriter2 CreateNGenWriter(string ngenImagePath, string pdbPath)
|
||||
{
|
||||
object instance;
|
||||
|
||||
if (IntPtr.Size == 4)
|
||||
{
|
||||
CreateNGenPdbWriter32(ngenImagePath, pdbPath, out instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateNGenPdbWriter64(ngenImagePath, pdbPath, out instance);
|
||||
}
|
||||
return (ISymNGenWriter2)instance;
|
||||
}
|
||||
|
||||
public PdbWriter(string pdbPath, PDBExtraData pdbExtraData)
|
||||
{
|
||||
SymDocument unknownDocument = new SymDocument();
|
||||
unknownDocument.Name = "unknown";
|
||||
unknownDocument.ChecksumType = SymChecksumType.None;
|
||||
unknownDocument.Checksum = Array.Empty<byte>();
|
||||
|
||||
_symDocuments.Add(unknownDocument);
|
||||
_pdbPath = pdbPath;
|
||||
_pdbExtraData = pdbExtraData;
|
||||
}
|
||||
|
||||
public void WritePDBData(string dllPath, IEnumerable<MethodInfo> methods)
|
||||
{
|
||||
bool failed = true;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
WritePDBDataHelper(dllPath, methods);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ((_ngenWriter != null) && (_pdbMod != UIntPtr.Zero))
|
||||
{
|
||||
_ngenWriter.CloseMod(_pdbMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_ngenWriter != null)
|
||||
{
|
||||
Marshal.FinalReleaseComObject(_ngenWriter);
|
||||
}
|
||||
}
|
||||
|
||||
failed = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_tempSourceDllName != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(_tempSourceDllName);
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
|
||||
if (failed && (_pdbFilePath != null))
|
||||
{
|
||||
try
|
||||
{
|
||||
// If anything fails, do not create a partial pdb file
|
||||
File.Delete(_pdbFilePath);
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePDBDataHelper(string dllPath, IEnumerable<MethodInfo> methods)
|
||||
{
|
||||
// This will try to open the managed PDB if lines info was requested. This is a
|
||||
// likely failure point, so intentionally do this before creating the NGEN PDB file
|
||||
// on disk.
|
||||
bool isILPDBProvided = false;
|
||||
if (_pdbExtraData.HasFlag(PDBExtraData.kPDBLines))
|
||||
{
|
||||
// line mapping not ported from crossgen yet.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
string originalDllPath = dllPath;
|
||||
|
||||
// Currently DiaSymReader does not work properly generating NGEN PDBS unless
|
||||
// the DLL whose PDB is being generated ends in .ni.*. Unfortunately, readyToRun
|
||||
// images do not follow this convention and end up producing bad PDBS. To fix
|
||||
// this (without changing diasymreader.dll which ships indepdendently of .NET Core)
|
||||
// we copy the file to somethign with this convention before generating the PDB
|
||||
// and delete it when we are done.
|
||||
if (!dllPath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) && !dllPath.EndsWith(".ni.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_tempSourceDllName = Path.Combine(Path.GetDirectoryName(dllPath), Path.GetFileNameWithoutExtension(dllPath) + ".ni" + Path.GetExtension(dllPath));
|
||||
File.Copy(dllPath, _tempSourceDllName);
|
||||
dllPath = _tempSourceDllName;
|
||||
}
|
||||
|
||||
_ngenWriter = CreateNGenWriter(dllPath, _pdbPath + "\\");
|
||||
|
||||
{
|
||||
// PDB file is now created. Get its path and initialize _pdbFilePath so the PDB file
|
||||
// can be deleted if we don't make it successfully to the end
|
||||
StringBuilder pdbFilePathBuilder = new StringBuilder();
|
||||
pdbFilePathBuilder.Capacity = 1024;
|
||||
_ngenWriter.QueryPDBNameExW(pdbFilePathBuilder, new IntPtr(pdbFilePathBuilder.Capacity));
|
||||
_pdbFilePath = pdbFilePathBuilder.ToString();
|
||||
}
|
||||
|
||||
_ngenWriter.OpenModW(originalDllPath, Path.GetFileName(originalDllPath), out _pdbMod);
|
||||
|
||||
WriteStringTable();
|
||||
WriteFileChecksums();
|
||||
|
||||
ushort? iCodeSection = null;
|
||||
uint rvaOfTextSection = 0;
|
||||
using (var peReader = new PEReader(new FileStream(dllPath, FileMode.Open), PEStreamOptions.Default))
|
||||
{
|
||||
var sections = peReader.PEHeaders.SectionHeaders;
|
||||
|
||||
for (int i = 0; i < sections.Length; i++)
|
||||
{
|
||||
ushort pdbSectionNumber = checked((ushort)(i+1));
|
||||
|
||||
_ngenWriter.AddSection(pdbSectionNumber, OMF.StandardText, 0, sections[i].SizeOfRawData);
|
||||
if (sections[i].Name == ".text")
|
||||
{
|
||||
iCodeSection = pdbSectionNumber;
|
||||
rvaOfTextSection = (uint)sections[i].VirtualAddress;
|
||||
}
|
||||
_ngenWriter.ModAddSecContribEx(_pdbMod, pdbSectionNumber, 0, sections[i].SizeOfRawData, (uint)sections[i].SectionCharacteristics, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// To support lines info, we need a "dummy" section, indexed as 0, for use as a
|
||||
// sentinel when MSPDB sets up its section contribution table
|
||||
_ngenWriter.AddSection(0, // Dummy section 0
|
||||
OMF.SentinelType,
|
||||
0,
|
||||
unchecked((int)0xFFFFFFFF));
|
||||
|
||||
foreach (var method in methods)
|
||||
{
|
||||
WriteMethodPDBData(iCodeSection.Value, method, Path.GetFileNameWithoutExtension(originalDllPath), rvaOfTextSection, isILPDBProvided);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteMethodPDBData(ushort iCodeSection, MethodInfo method, string assemblyName, uint textSectionOffset, bool isILPDBProvided)
|
||||
{
|
||||
string nameSuffix = $"{method.Name}$#{(assemblyName != method.AssemblyName ? method.AssemblyName : String.Empty)}#{method.MethodToken.ToString("X")}";
|
||||
|
||||
_ngenWriter.AddSymbol(nameSuffix, iCodeSection, method.HotRVA - textSectionOffset);
|
||||
if (method.ColdRVA != 0)
|
||||
{
|
||||
_ngenWriter.AddSymbol($"[COLD] {nameSuffix}", iCodeSection, method.ColdRVA);
|
||||
}
|
||||
|
||||
if (isILPDBProvided)
|
||||
{
|
||||
// line mapping not ported from crossgen yet.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private const int CV_SIGNATURE_C13 = 4;
|
||||
private enum DEBUG_S_SUBSECTION_TYPE {
|
||||
DEBUG_S_IGNORE = unchecked((int)0x80000000), // if this bit is set in a subsection type then ignore the subsection contents
|
||||
|
||||
DEBUG_S_SYMBOLS = 0xf1,
|
||||
DEBUG_S_LINES,
|
||||
DEBUG_S_STRINGTABLE,
|
||||
DEBUG_S_FILECHKSMS,
|
||||
DEBUG_S_FRAMEDATA,
|
||||
DEBUG_S_INLINEELINES,
|
||||
DEBUG_S_CROSSSCOPEIMPORTS,
|
||||
DEBUG_S_CROSSSCOPEEXPORTS,
|
||||
|
||||
DEBUG_S_IL_LINES,
|
||||
DEBUG_S_FUNC_MDTOKEN_MAP,
|
||||
DEBUG_S_TYPE_MDTOKEN_MAP,
|
||||
DEBUG_S_MERGED_ASSEMBLYINPUT,
|
||||
|
||||
DEBUG_S_COFF_SYMBOL_RVA,
|
||||
}
|
||||
|
||||
private void WriteStringTable()
|
||||
{
|
||||
_stringTableToOffsetMapping = new Dictionary<string,int>();
|
||||
|
||||
MemoryStream stringTableStream = new MemoryStream();
|
||||
BinaryWriter writer = new BinaryWriter(stringTableStream, Encoding.UTF8);
|
||||
writer.Write(CV_SIGNATURE_C13);
|
||||
writer.Write((uint)DEBUG_S_SUBSECTION_TYPE.DEBUG_S_STRINGTABLE);
|
||||
long sizeOfStringTablePosition = writer.BaseStream.Position;
|
||||
writer.Write((uint)0); // Size of actual string table. To be filled in later
|
||||
long startOfStringTableOffset = writer.BaseStream.Position;
|
||||
foreach (var document in _symDocuments)
|
||||
{
|
||||
string str = document.Name;
|
||||
if (_stringTableToOffsetMapping.ContainsKey(str))
|
||||
continue;
|
||||
|
||||
long offset = writer.BaseStream.Position;
|
||||
_stringTableToOffsetMapping.Add(str, checked((int)(offset - startOfStringTableOffset)));
|
||||
writer.Write(str.AsSpan());
|
||||
writer.Write((byte)0); // Null terminate all strings
|
||||
}
|
||||
|
||||
// Update string table size
|
||||
long stringTableSize = writer.BaseStream.Position - startOfStringTableOffset;
|
||||
writer.BaseStream.Position = sizeOfStringTablePosition;
|
||||
writer.Write(checked((uint)stringTableSize));
|
||||
writer.Flush();
|
||||
|
||||
// Write string table into pdb file
|
||||
byte[] stringTableArray = stringTableStream.ToArray();
|
||||
_ngenWriter.ModAddSymbols(_pdbMod, stringTableArray, stringTableArray.Length);
|
||||
}
|
||||
|
||||
private void WriteFileChecksums()
|
||||
{
|
||||
_documentToChecksumOffsetMapping = new Dictionary<SymDocument,int>();
|
||||
|
||||
MemoryStream checksumStream = new MemoryStream();
|
||||
BinaryWriter writer = new BinaryWriter(checksumStream, Encoding.UTF8);
|
||||
writer.Write(CV_SIGNATURE_C13);
|
||||
writer.Write((uint)DEBUG_S_SUBSECTION_TYPE.DEBUG_S_FILECHKSMS);
|
||||
|
||||
long sizeOfChecksumTablePosition = writer.BaseStream.Position;
|
||||
writer.Write((uint)0); // Size of actual checksum table. To be filled in later
|
||||
long startOfChecksumTableOffset = writer.BaseStream.Position;
|
||||
foreach (var document in _symDocuments)
|
||||
{
|
||||
long offset = writer.BaseStream.Position;
|
||||
_documentToChecksumOffsetMapping.Add(document, checked((int)(offset - startOfChecksumTableOffset)));
|
||||
|
||||
SymChecksumType checksumType = document.ChecksumType;
|
||||
byte[] checksum = document.Checksum;
|
||||
|
||||
if (document.Checksum.Length > 255)
|
||||
{
|
||||
// Should never happen, but just in case checksum data is invalid, just put
|
||||
// no checksum into the NGEN PDB
|
||||
checksumType = SymChecksumType.None;
|
||||
checksum = Array.Empty<byte>();
|
||||
}
|
||||
writer.Write(_stringTableToOffsetMapping[document.Name]);
|
||||
writer.Write((byte)checksum.Length);
|
||||
writer.Write((byte)checksumType);
|
||||
writer.Write(checksum);
|
||||
|
||||
// Must align to the next 4-byte boundary
|
||||
while ((writer.BaseStream.Position % 4) != 0)
|
||||
{
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
// Update checksum table size
|
||||
long checksumTableSize = writer.BaseStream.Position - startOfChecksumTableOffset;
|
||||
writer.BaseStream.Position = sizeOfChecksumTablePosition;
|
||||
writer.Write(checked((uint)checksumTableSize));
|
||||
writer.Flush();
|
||||
|
||||
// Write string table into pdb file
|
||||
byte[] checksumTableArray = checksumStream.ToArray();
|
||||
_ngenWriter.ModAddSymbols(_pdbMod, checksumTableArray, checksumTableArray.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ using System.Runtime.CompilerServices;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ILCompiler.Reflection.ReadyToRun;
|
||||
using ILCompiler.PdbWriter;
|
||||
|
||||
using Internal.Runtime;
|
||||
|
||||
|
@ -46,6 +47,9 @@ namespace R2RDump
|
|||
public bool DiffHideSameDisasm { get; set; }
|
||||
public bool IgnoreSensitive { get; set; }
|
||||
|
||||
public bool CreatePDB { get; set; }
|
||||
public string PdbPath { get; set; }
|
||||
|
||||
public FileInfo[] Reference { get; set; }
|
||||
public DirectoryInfo[] ReferencePath { get; set; }
|
||||
|
||||
|
@ -337,8 +341,9 @@ namespace R2RDump
|
|||
public void Dump(ReadyToRunReader r2r)
|
||||
{
|
||||
_dumper.Begin();
|
||||
bool standardDump = !(_options.EntryPoints || _options.CreatePDB);
|
||||
|
||||
if (_options.Header || !_options.EntryPoints)
|
||||
if (_options.Header && standardDump)
|
||||
{
|
||||
_dumper.WriteDivider("R2R Header");
|
||||
_dumper.DumpHeader(true);
|
||||
|
@ -377,7 +382,18 @@ namespace R2RDump
|
|||
_dumper.DumpEntryPoints();
|
||||
}
|
||||
|
||||
if (!_options.Header && !_options.EntryPoints)
|
||||
if (_options.CreatePDB)
|
||||
{
|
||||
string pdbPath = _options.PdbPath;
|
||||
if (String.IsNullOrEmpty(pdbPath))
|
||||
{
|
||||
pdbPath = Path.GetDirectoryName(r2r.Filename);
|
||||
}
|
||||
var pdbWriter = new PdbWriter(pdbPath, PDBExtraData.None);
|
||||
pdbWriter.WritePDBData(r2r.Filename, ProducePdbWriterMethods(r2r));
|
||||
}
|
||||
|
||||
if (!_options.Header && standardDump)
|
||||
{
|
||||
_dumper.DumpAllMethods();
|
||||
}
|
||||
|
@ -386,6 +402,21 @@ namespace R2RDump
|
|||
_dumper.End();
|
||||
}
|
||||
|
||||
IEnumerable<MethodInfo> ProducePdbWriterMethods(ReadyToRunReader r2r)
|
||||
{
|
||||
foreach (var method in _dumper.NormalizedMethods())
|
||||
{
|
||||
MethodInfo mi = new MethodInfo();
|
||||
mi.Name = method.SignatureString;
|
||||
mi.HotRVA = (uint)method.RuntimeFunctions[0].StartAddress;
|
||||
mi.MethodToken = (uint)MetadataTokens.GetToken(method.MetadataReader, method.MethodHandle);
|
||||
mi.AssemblyName = method.MetadataReader.GetString(method.MetadataReader.GetAssemblyDefinition().Name);
|
||||
mi.ColdRVA = 0;
|
||||
|
||||
yield return mi;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the name, signature or id of <param>method</param> matches <param>query</param>
|
||||
/// </summary>
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(RepositoryEngineeringDir)DiaSymReaderNative.targets" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.CoreDisTools">
|
||||
<Version>1.0.1-prerelease-00005</Version>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue