forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Emit DebugDirectory section and all DebugDirectory entries (dotnet#2398)
* Emit DebugDirectory section and all DebugDirectory entries - Add new RELOC type for raw data points - Emit native DebugDirectory entry with proper MD5 hash of the output image - Fix determinism issue: timestamp has to be copied from input IL image
- Loading branch information
Fadi Hanna
authored
Feb 1, 2020
1 parent
483d042
commit 6c8a629
Showing
13 changed files
with
434 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
159 changes: 159 additions & 0 deletions
159
...2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryEntryNode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
// 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.Text; | ||
using System.Diagnostics; | ||
using System.Reflection.PortableExecutable; | ||
|
||
using Internal.Text; | ||
using Internal.TypeSystem.Ecma; | ||
using System.IO; | ||
using System.Collections.Immutable; | ||
|
||
namespace ILCompiler.DependencyAnalysis.ReadyToRun | ||
{ | ||
public abstract class DebugDirectoryEntryNode : ObjectNode, ISymbolDefinitionNode | ||
{ | ||
protected readonly EcmaModule _module; | ||
|
||
public DebugDirectoryEntryNode(EcmaModule module) | ||
{ | ||
_module = module; | ||
} | ||
|
||
public override ObjectNodeSection Section => ObjectNodeSection.TextSection; | ||
|
||
public override bool IsShareable => false; | ||
|
||
protected internal override int Phase => (int)ObjectNodePhase.Ordered; | ||
|
||
public override bool StaticDependenciesAreComputed => true; | ||
|
||
public int Offset => 0; | ||
|
||
public abstract void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb); | ||
|
||
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); | ||
|
||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) | ||
{ | ||
return _module.CompareTo(((DebugDirectoryEntryNode)other)._module); | ||
} | ||
} | ||
|
||
public class NativeDebugDirectoryEntryNode : DebugDirectoryEntryNode | ||
{ | ||
const int RSDSSize = | ||
sizeof(int) + // Magic | ||
16 + // Signature (guid) | ||
sizeof(int) + // Age | ||
260; // FileName | ||
|
||
public override int ClassCode => 119958401; | ||
|
||
public unsafe int Size => RSDSSize; | ||
|
||
public NativeDebugDirectoryEntryNode(EcmaModule sourceModule) | ||
: base(sourceModule) | ||
{ } | ||
|
||
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) | ||
{ | ||
sb.Append(nameMangler.CompilationUnitPrefix); | ||
sb.Append($"__NativeRvaBlob_{_module.Assembly.GetName().Name}"); | ||
} | ||
|
||
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) | ||
{ | ||
ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); | ||
builder.RequireInitialPointerAlignment(); | ||
builder.AddSymbol(this); | ||
|
||
// Emit empty entry. This will be filled with data after the output image is emitted | ||
builder.EmitZeros(RSDSSize); | ||
|
||
return builder.ToObjectData(); | ||
} | ||
|
||
public byte[] GenerateRSDSEntryData(byte[] md5Hash) | ||
{ | ||
MemoryStream rsdsEntry = new MemoryStream(RSDSSize); | ||
|
||
using (BinaryWriter writer = new BinaryWriter(rsdsEntry)) | ||
{ | ||
// Magic "RSDS" | ||
writer.Write((uint)0x53445352); | ||
|
||
// The PDB signature will be the same as our NGEN signature. | ||
// However we want the printed version of the GUID to be the same as the | ||
// byte dump of the signature so we swap bytes to make this work. | ||
Debug.Assert(md5Hash.Length == 16); | ||
writer.Write((uint)((md5Hash[0] * 256 + md5Hash[1]) * 256 + md5Hash[2]) * 256 + md5Hash[3]); | ||
writer.Write((ushort)(md5Hash[4] * 256 + md5Hash[5])); | ||
writer.Write((ushort)(md5Hash[6] * 256 + md5Hash[7])); | ||
writer.Write(md5Hash, 8, 8); | ||
|
||
// Age | ||
writer.Write(1); | ||
|
||
string pdbFileName = _module.Assembly.GetName().Name + ".ni.pdb"; | ||
byte[] pdbFileNameBytes = Encoding.UTF8.GetBytes(pdbFileName); | ||
writer.Write(pdbFileNameBytes); | ||
|
||
Debug.Assert(rsdsEntry.Length <= RSDSSize); | ||
return rsdsEntry.ToArray(); | ||
} | ||
} | ||
} | ||
|
||
public class CopiedDebugDirectoryEntryNode : DebugDirectoryEntryNode | ||
{ | ||
private readonly int _debugEntryIndex; | ||
|
||
public override int ClassCode => 1558397; | ||
|
||
public CopiedDebugDirectoryEntryNode(EcmaModule sourceModule, int debugEntryIndex) | ||
: base(sourceModule) | ||
{ | ||
Debug.Assert(debugEntryIndex >= 0); | ||
_debugEntryIndex = debugEntryIndex; | ||
} | ||
|
||
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) | ||
{ | ||
sb.Append(nameMangler.CompilationUnitPrefix); | ||
sb.Append($"__CopiedDebugEntryNode_{_debugEntryIndex}_{_module.Assembly.GetName().Name}"); | ||
} | ||
|
||
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) | ||
{ | ||
if (relocsOnly) | ||
{ | ||
return new ObjectData(Array.Empty<byte>(), Array.Empty<Relocation>(), 1, new ISymbolDefinitionNode[] { this }); | ||
} | ||
|
||
ImmutableArray<DebugDirectoryEntry> entries = _module.PEReader.ReadDebugDirectory(); | ||
Debug.Assert(entries != null && _debugEntryIndex < entries.Length); | ||
|
||
DebugDirectoryEntry sourceDebugEntry = entries[_debugEntryIndex]; | ||
|
||
PEMemoryBlock block = _module.PEReader.GetSectionData(sourceDebugEntry.DataRelativeVirtualAddress); | ||
byte[] result = new byte[sourceDebugEntry.DataSize]; | ||
block.GetContent(0, sourceDebugEntry.DataSize).CopyTo(result); | ||
|
||
return new ObjectData(result, Array.Empty<Relocation>(), _module.Context.Target.PointerSize, new ISymbolDefinitionNode[] { this }); | ||
} | ||
|
||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) | ||
{ | ||
int moduleComp = base.CompareToImpl(other, comparer); | ||
if (moduleComp != 0) | ||
return moduleComp; | ||
|
||
return _debugEntryIndex - ((CopiedDebugDirectoryEntryNode)other)._debugEntryIndex; | ||
} | ||
} | ||
} |
127 changes: 127 additions & 0 deletions
127
...ssgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugDirectoryNode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// 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.Immutable; | ||
using System.Diagnostics; | ||
using System.Reflection.PortableExecutable; | ||
using Internal.Text; | ||
using Internal.TypeSystem.Ecma; | ||
|
||
namespace ILCompiler.DependencyAnalysis.ReadyToRun | ||
{ | ||
public class DebugDirectoryNode : ObjectNode, ISymbolDefinitionNode | ||
{ | ||
const int ImageDebugDirectorySize = | ||
sizeof(int) + // Characteristics | ||
sizeof(int) + // TimeDateStamp | ||
sizeof(short) + // MajorVersion: | ||
sizeof(short) + // MinorVersion | ||
sizeof(int) + // Type | ||
sizeof(int) + // SizeOfData: | ||
sizeof(int) + // AddressOfRawData: | ||
sizeof(int); // PointerToRawData | ||
|
||
private EcmaModule _module; | ||
|
||
public DebugDirectoryNode(EcmaModule sourceModule) | ||
{ | ||
_module = sourceModule; | ||
} | ||
|
||
public override ObjectNodeSection Section => ObjectNodeSection.TextSection; | ||
|
||
public override bool IsShareable => false; | ||
|
||
protected internal override int Phase => (int)ObjectNodePhase.Ordered; | ||
|
||
public override int ClassCode => 315358387; | ||
|
||
public override bool StaticDependenciesAreComputed => true; | ||
|
||
public int Offset => 0; | ||
|
||
public int Size => (GetNumDebugDirectoryEntriesInModule() + 1) * ImageDebugDirectorySize; | ||
|
||
public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) | ||
{ | ||
sb.Append(nameMangler.CompilationUnitPrefix); | ||
sb.Append($"__DebugDirectory_{_module.Assembly.GetName().Name}"); | ||
} | ||
|
||
protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); | ||
|
||
int GetNumDebugDirectoryEntriesInModule() | ||
{ | ||
ImmutableArray<DebugDirectoryEntry> entries = _module.PEReader.ReadDebugDirectory(); | ||
return entries == null ? 0 : entries.Length; | ||
} | ||
|
||
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) | ||
{ | ||
ObjectDataBuilder builder = new ObjectDataBuilder(factory, relocsOnly); | ||
builder.RequireInitialPointerAlignment(); | ||
builder.AddSymbol(this); | ||
|
||
ImmutableArray<DebugDirectoryEntry> entries = _module.PEReader.ReadDebugDirectory(); | ||
int numEntries = GetNumDebugDirectoryEntriesInModule(); | ||
|
||
// First, write the native debug directory entry | ||
{ | ||
var entry = (NativeDebugDirectoryEntryNode)factory.DebugDirectoryEntry(_module, -1); | ||
|
||
builder.EmitUInt(0 /* Characteristics */); | ||
if (numEntries > 0) | ||
{ | ||
builder.EmitUInt(entries[0].Stamp); | ||
builder.EmitUShort(entries[0].MajorVersion); | ||
} | ||
else | ||
{ | ||
builder.EmitUInt(0); | ||
builder.EmitUShort(0); | ||
} | ||
// Make sure the "is portable pdb" indicator (MinorVersion == 0x504d) is clear | ||
// for the NGen debug directory entry since this debug directory can be copied | ||
// from an existing entry which could be a portable pdb. | ||
builder.EmitUShort(0 /* MinorVersion */); | ||
builder.EmitInt((int)DebugDirectoryEntryType.CodeView); | ||
builder.EmitInt(entry.Size); | ||
builder.EmitReloc(entry, RelocType.IMAGE_REL_BASED_ADDR32NB); | ||
builder.EmitReloc(entry, RelocType.IMAGE_REL_FILE_ABSOLUTE); | ||
} | ||
|
||
// Second, copy existing entries from input module | ||
for(int i = 0; i < numEntries; i++) | ||
{ | ||
builder.EmitUInt(0 /* Characteristics */); | ||
builder.EmitUInt(entries[i].Stamp); | ||
builder.EmitUShort(entries[i].MajorVersion); | ||
builder.EmitUShort(entries[i].MinorVersion); | ||
builder.EmitInt((int)entries[i].Type); | ||
builder.EmitInt(entries[i].DataSize); | ||
if (entries[i].DataSize == 0) | ||
{ | ||
builder.EmitUInt(0); | ||
builder.EmitUInt(0); | ||
} | ||
else | ||
{ | ||
builder.EmitReloc(factory.DebugDirectoryEntry(_module, i), RelocType.IMAGE_REL_BASED_ADDR32NB); | ||
builder.EmitReloc(factory.DebugDirectoryEntry(_module, i), RelocType.IMAGE_REL_FILE_ABSOLUTE); | ||
} | ||
} | ||
|
||
Debug.Assert(builder.CountBytes == Size); | ||
|
||
return builder.ToObjectData(); | ||
} | ||
|
||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) | ||
{ | ||
return _module.CompareTo(((DebugDirectoryNode)other)._module); | ||
} | ||
} | ||
} |
Oops, something went wrong.