diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj
index 97c97bb7de720d..0d1b23ad73b3c8 100644
--- a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj
@@ -15,6 +15,7 @@
binaries are up to date and which are stale. -->
false
Debug;Release;Checked
+ true
diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompilerComWrappers.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompilerComWrappers.cs
new file mode 100644
index 00000000000000..d9972da41f76c6
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompilerComWrappers.cs
@@ -0,0 +1,32 @@
+
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.DiaSymReader
+{
+ internal unsafe sealed class ILCompilerComWrappers : ComWrappers
+ {
+ protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count)
+ {
+ // passing the managed object to COM is not currently supported
+ throw new NotImplementedException();
+ }
+
+ protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags)
+ {
+ // Assert use of the UniqueInstance flag since the returned Native Object Wrapper always
+ // supports IDisposable and its Dispose will always release and suppress finalization.
+ // If the wrapper doesn't always support IDisposable the assert can be relaxed.
+ Debug.Assert(flags.HasFlag(CreateObjectFlags.UniqueInstance));
+
+ // Throw an exception if the type is not supported by the implementation.
+ // Null can be returned as well, but an ArgumentNullException will be thrown for
+ // the consumer of this ComWrappers instance.
+ return SymNgenWriterWrapper.CreateIfSupported(externalComObject) ?? throw new NotSupportedException();
+ }
+
+ protected override void ReleaseObjects(IEnumerable objects) => throw new NotImplementedException();
+ }
+}
\ No newline at end of file
diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ISymNGenWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ISymNGenWriter.cs
index 4c4b6b97d60a91..40702fd187a75a 100644
--- a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ISymNGenWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ISymNGenWriter.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-#pragma warning disable 436 // SuppressUnmanagedCodeSecurityAttribute defined in source and mscorlib
+#pragma warning disable 436 // SuppressUnmanagedCodeSecurityAttribute defined in source and mscorlib
using System;
using System.Collections.Generic;
@@ -11,9 +11,39 @@
namespace Microsoft.DiaSymReader
{
- [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("D682FD12-43dE-411C-811B-BE8404CEA126"), SuppressUnmanagedCodeSecurity]
+ ///
+ /// IUnknown COM type for writing NGen PDBs
+ ///
+ ///
+ ///
+ /// [
+ /// object,
+ /// uuid(d682fd12-43de-411c-811b-be8404cea126),
+ /// pointer_default(unique)
+ /// ]
+ /// interface ISymNGenWriter : IUnknown
+ /// {
+ /// /*
+ /// * Add a new public symbol to the NGEN PDB.
+ /// */
+ /// HRESULT AddSymbol([in] BSTR pSymbol,
+ /// [in] USHORT iSection,
+ /// [in] ULONGLONG rva);
+ ///
+ /// /*
+ /// * Adds a new section to the NGEN PDB.
+ /// */
+ /// HRESULT AddSection([in] USHORT iSection,
+ /// [in] USHORT flags,
+ /// [in] long offset,
+ /// [in] long cb);
+ /// };
+ ///
+ ///
internal interface ISymNGenWriter
{
+ public static readonly Guid IID = new Guid("D682FD12-43dE-411C-811B-BE8404CEA126");
+
// Add a new public symbol to the NGEN PDB.
void AddSymbol([MarshalAs(UnmanagedType.BStr)] string pSymbol,
ushort iSection,
@@ -46,9 +76,46 @@ internal enum OMF : ushort
}
- [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("B029E51B-4C55-4fe2-B993-9F7BC1F10DB4"), SuppressUnmanagedCodeSecurity]
+ ///
+ /// IUnknown COM type for writing NGen PDBs
+ ///
+ ///
+ ///
+ /// [
+ /// object,
+ /// local,
+ /// uuid(B029E51B-4C55-4fe2-B993-9F7BC1F10DB4),
+ /// pointer_default(unique)
+ /// ]
+ /// interface ISymNGenWriter2 : ISymNGenWriter
+ /// {
+ /// HRESULT OpenModW([in] const wchar_t* wszModule,
+ /// [in] const wchar_t* wszObjFile,
+ /// [out] BYTE** ppmod);
+ ///
+ /// HRESULT CloseMod([in] BYTE* pmod);
+ ///
+ /// HRESULT ModAddSymbols([in] BYTE* pmod, [in] BYTE* pbSym, [in] long cb);
+ ///
+ /// HRESULT ModAddSecContribEx(
+ /// [in] BYTE* pmod,
+ /// [in] USHORT isect,
+ /// [in] long off,
+ /// [in] long cb,
+ /// [in] ULONG dwCharacteristics,
+ /// [in] DWORD dwDataCrc,
+ /// [in] DWORD dwRelocCrc);
+ ///
+ /// HRESULT QueryPDBNameExW(
+ /// [out, size_is(cchMax)] wchar_t wszPDB[],
+ /// [in] SIZE_T cchMax);
+ /// };
+ ///
+ ///
internal interface ISymNGenWriter2 : ISymNGenWriter
{
+ public readonly static new Guid IID = new Guid("B029E51B-4C55-4fe2-B993-9F7BC1F10DB4");
+
// Add a new public symbol to the NGEN PDB.
new void AddSymbol([MarshalAs(UnmanagedType.BStr)] string pSymbol,
ushort iSection,
@@ -78,7 +145,7 @@ void ModAddSecContribEx(
uint dwRelocCrc);
void QueryPDBNameExW(
- [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pdb,
+ [MarshalAs(UnmanagedType.LPWStr)] char[] pdb,
IntPtr cchMax);
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs
index a16d5bf19a8e67..be0d8af909f727 100644
--- a/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs
@@ -89,7 +89,7 @@ public class PdbWriter
Dictionary _documentToChecksumOffsetMapping;
UIntPtr _pdbMod;
- ISymNGenWriter2 _ngenWriter;
+ SymNgenWriterWrapper _ngenWriter;
static PdbWriter()
{
@@ -116,7 +116,7 @@ private static IntPtr DllImportResolver(string libraryName, Assembly assembly, D
private extern static void CreateNGenPdbWriter(
[MarshalAs(UnmanagedType.LPWStr)] string ngenImagePath,
[MarshalAs(UnmanagedType.LPWStr)] string pdbPath,
- [MarshalAs(UnmanagedType.Interface)] out ISymNGenWriter2 ngenPdbWriter);
+ out IntPtr ngenPdbWriterPtr);
public PdbWriter(string pdbPath, PDBExtraData pdbExtraData, TargetDetails target)
{
@@ -138,23 +138,14 @@ public void WritePDBData(string dllPath, IEnumerable methods)
{
try
{
- try
- {
- WritePDBDataHelper(dllPath, methods);
- }
- finally
- {
- if ((_ngenWriter != null) && (_pdbMod != UIntPtr.Zero))
- {
- _ngenWriter.CloseMod(_pdbMod);
- }
- }
+ WritePDBDataHelper(dllPath, methods);
}
finally
{
- if (_ngenWriter != null)
+ if ((_ngenWriter != null) && (_pdbMod != UIntPtr.Zero))
{
- Marshal.FinalReleaseComObject(_ngenWriter);
+ _ngenWriter.CloseMod(_pdbMod);
+ _ngenWriter?.Dispose();
}
}
@@ -217,14 +208,16 @@ private void WritePDBDataHelper(string dllPath, IEnumerable methods)
// Delete any preexisting PDB file upfront, otherwise CreateNGenPdbWriter silently opens it
File.Delete(_pdbFilePath);
- CreateNGenPdbWriter(dllPath, _pdbFilePath, out _ngenWriter);
+ var comWrapper = new ILCompilerComWrappers();
+ CreateNGenPdbWriter(dllPath, _pdbFilePath, out var pdbWriterInst);
+ _ngenWriter = (SymNgenWriterWrapper)comWrapper.GetOrCreateObjectForComInstance(pdbWriterInst, CreateObjectFlags.UniqueInstance);
{
// PDB file is now created. Get its path and update _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));
+ const int capacity = 1024;
+ var pdbFilePathBuilder = new char[capacity];
+ _ngenWriter.QueryPDBNameExW(pdbFilePathBuilder, new IntPtr(capacity - 1) /* remove 1 byte for null */);
_pdbFilePath = pdbFilePathBuilder.ToString();
}
@@ -428,9 +421,9 @@ private void WriteCompilerVersion()
byte iLanguage = (byte)CV_CFL_LANG.CV_CFL_MSIL;
writer.Write(iLanguage);
// Write rest of flags
- writer.Write((byte)0);
- writer.Write((byte)0);
- writer.Write((byte)0);
+ writer.Write((byte)0);
+ writer.Write((byte)0);
+ writer.Write((byte)0);
switch (_target.Architecture)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/SymNgenWriterWrapper.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/SymNgenWriterWrapper.cs
new file mode 100644
index 00000000000000..8a1166f43a39bf
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/SymNgenWriterWrapper.cs
@@ -0,0 +1,149 @@
+
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+#nullable enable
+
+namespace Microsoft.DiaSymReader
+{
+ internal class SymNgenWriterWrapper : ISymNGenWriter2, IDisposable
+ {
+ private bool _isDisposed = false;
+ public IntPtr ISymNGenWriter2Inst { get; }
+
+ private SymNgenWriterWrapper(IntPtr writer2Inst)
+ {
+ ISymNGenWriter2Inst = writer2Inst;
+ }
+
+ public static SymNgenWriterWrapper? CreateIfSupported(IntPtr ptr)
+ {
+ var iid = ISymNGenWriter2.IID;
+ int hr = Marshal.QueryInterface(ptr, ref iid, out IntPtr ngenWriterInst);
+ if (hr != 0)
+ {
+ return null;
+ }
+
+ return new SymNgenWriterWrapper(ngenWriterInst);
+ }
+
+ ~SymNgenWriterWrapper()
+ {
+ DisposeInternal();
+ }
+
+ public void Dispose()
+ {
+ DisposeInternal();
+ GC.SuppressFinalize(this);
+ }
+
+ private void DisposeInternal()
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+ Marshal.Release(ISymNGenWriter2Inst);
+ _isDisposed = true;
+ }
+
+ public unsafe void AddSymbol(string pSymbol, ushort iSection, ulong rva)
+ {
+ IntPtr strLocal = Marshal.StringToBSTR(pSymbol);
+ var inst = ISymNGenWriter2Inst;
+ var func = (delegate* unmanaged)(*(*(void***)inst + 3 /* ISymNGenWriter2.AddSymbol slot */));
+ int hr = func(inst, strLocal, iSection, rva);
+ Marshal.FreeBSTR(strLocal);
+ if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+
+ public unsafe void AddSection(ushort iSection, OMF flags, int offset, int cb)
+ {
+ var inst = ISymNGenWriter2Inst;
+ var func = (delegate* unmanaged)(*(*(void***)inst + 4));
+ int hr = func(inst, iSection, flags, offset, cb);
+ if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+
+ public unsafe void OpenModW(string wszModule, string wszObjFile, out UIntPtr ppmod)
+ {
+ var inst = ISymNGenWriter2Inst;
+ fixed (char* wszModulePtr = wszModule)
+ fixed (char* wszObjFilePtr = wszObjFile)
+ {
+ UIntPtr ppmodPtr;
+ var func = (delegate* unmanaged)(*(*(void***)inst + 5));
+ int hr = func(inst, wszModulePtr, wszObjFilePtr, &ppmodPtr);
+ ppmod = ppmodPtr;
+ if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+ }
+
+ public unsafe void CloseMod(UIntPtr pmod)
+ {
+ var inst = ISymNGenWriter2Inst;
+ var func = (delegate* unmanaged)(*(*(void***)inst + 6));
+ int hr = func(inst, pmod);
+ if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+
+ public unsafe void ModAddSymbols(UIntPtr pmod, byte[] pbSym, int cb)
+ {
+ fixed (byte* pbSymPtr = pbSym)
+ {
+ var pbSymLocal = (IntPtr)pbSymPtr;
+ var inst = ISymNGenWriter2Inst;
+ var func = (delegate* unmanaged)(*(*(void***)inst + 7));
+ int hr = func(inst, pmod, pbSymLocal, cb);
+ if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+ }
+
+ public unsafe void ModAddSecContribEx(UIntPtr pmod, ushort isect, int off, int cb, uint dwCharacteristics, uint dwDataCrc, uint dwRelocCrc)
+ {
+ var inst = ISymNGenWriter2Inst;
+ var func = (delegate* unmanaged)(*(*(void***)inst + 8));
+ int hr = func(inst, pmod, isect, off, cb, dwCharacteristics, dwDataCrc, dwRelocCrc);
+ if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+
+ public unsafe void QueryPDBNameExW(char[] pdb, IntPtr cchMax)
+ {
+ fixed (char* pdbPtr = pdb)
+ {
+ var pdbLocal = (IntPtr)pdbPtr;
+ var inst = ISymNGenWriter2Inst;
+ var func = (delegate* unmanaged)(*(*(void***)inst + 9));
+ int hr = func(inst, pdbPtr, cchMax);
+ if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+ }
+ }
+
+ void ISymNGenWriter.AddSymbol(string pSymbol, ushort iSection, ulong rva) => AddSymbol(pSymbol, iSection, rva);
+ void ISymNGenWriter.AddSection(ushort iSection, OMF flags, int offset, int cb) => AddSection(iSection, flags, offset, cb);
+ }
+}