Skip to content

Commit 11bc6de

Browse files
committed
Remove dependency on Microsoft.NET.HostModel
As [stated by Vitek Karas][1]: > Also note that the `Microsoft.NET.HostModel` package is not intended as a public API, it's only intended use is from the SDK. [1]: dotnet/runtime#67386 (comment)
1 parent 91e2b5e commit 11bc6de

File tree

3 files changed

+131
-76
lines changed

3 files changed

+131
-76
lines changed

src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,5 @@
4141

4242
<ItemGroup Condition="('$(TargetFramework)' == 'netstandard2.0') Or ('$(TargetFramework)' == 'net461')">
4343
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
44-
<PackageReference Include="Microsoft.NET.HostModel" Version="3.1.16" />
4544
</ItemGroup>
4645
</Project>

src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs

Lines changed: 23 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,6 @@
33
using System.Reflection;
44
using Microsoft.Extensions.DependencyModel;
55

6-
#if !NET451
7-
using System.Diagnostics;
8-
using System.IO;
9-
using System.IO.MemoryMappedFiles;
10-
using System.Text;
11-
#endif
12-
136
namespace Serilog.Settings.Configuration.Assemblies
147
{
158
abstract class AssemblyFinder
@@ -23,8 +16,29 @@ protected static bool IsCaseInsensitiveMatch(string text, string textToFind)
2316

2417
public static AssemblyFinder Auto()
2518
{
26-
var dependencyContext = GetDependencyContext();
27-
return dependencyContext != null ? new DependencyContextAssemblyFinder(dependencyContext) : new DllScanningAssemblyFinder();
19+
var entryAssembly = Assembly.GetEntryAssembly();
20+
if (entryAssembly != null)
21+
{
22+
var isBundled = entryAssembly.Location.Length == 0;
23+
if (isBundled)
24+
{
25+
using var depsJsonStream = SingleFileApplication.GetDepsJsonStream();
26+
if (depsJsonStream != null)
27+
{
28+
using var depsJsonReader = new DependencyContextJsonReader();
29+
var dependencyContext = depsJsonReader.Read(depsJsonStream);
30+
return new DependencyContextAssemblyFinder(dependencyContext);
31+
}
32+
}
33+
34+
var entryAssemblyContext = DependencyContext.Load(entryAssembly);
35+
if (entryAssemblyContext != null)
36+
{
37+
return new DependencyContextAssemblyFinder(entryAssemblyContext);
38+
}
39+
}
40+
41+
return new DllScanningAssemblyFinder();
2842
}
2943

3044
public static AssemblyFinder ForSource(ConfigurationAssemblySource configurationAssemblySource)
@@ -41,71 +55,5 @@ public static AssemblyFinder ForDependencyContext(DependencyContext dependencyCo
4155
{
4256
return new DependencyContextAssemblyFinder(dependencyContext);
4357
}
44-
45-
static DependencyContext GetDependencyContext()
46-
{
47-
var isBundled = string.IsNullOrEmpty(Assembly.GetEntryAssembly()?.Location);
48-
if (!isBundled)
49-
{
50-
return DependencyContext.Default;
51-
}
52-
53-
#if !NET451
54-
try
55-
{
56-
var currentProcessPath = Process.GetCurrentProcess().MainModule?.FileName;
57-
if (currentProcessPath != null)
58-
{
59-
var currentProcessExeLength = new FileInfo(currentProcessPath).Length;
60-
using var currentProcessFile = MemoryMappedFile.CreateFromFile(currentProcessPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
61-
using var currentProcessView = currentProcessFile.CreateViewAccessor(0, currentProcessExeLength, MemoryMappedFileAccess.Read);
62-
// See https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs#L216
63-
byte[] bundleSignature = {
64-
// 32 bytes represent the bundle signature: SHA-256 for ".net core bundle"
65-
0x8b, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38, 0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32,
66-
0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18, 0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae
67-
};
68-
// Can't use BinaryUtils.SearchInFile(currentProcessPath, bundleSignature) because it calls MemoryMappedFile.CreateFromFile(currentProcessPath)
69-
// without specifying `MemoryMappedFileAccess.Read` which eventually cause an IO exception to be thrown on Windows:
70-
// > System.IO.IOException: The process cannot access the file
71-
// Note: HostWriter.IsBundle(currentProcessPath, out var bundleHeaderOffset) calls BinaryUtils.SearchInFile(currentProcessPath, bundleSignature)
72-
// So the internal SearchInFile that takes a MemoryMappedViewAccessor is used instead
73-
// Using this internal method is a proof of concept, it needs to be properly rewritten and thus the `Microsoft.NET.HostModel` dependency can be removed.
74-
// internal static unsafe int SearchInFile(MemoryMappedViewAccessor accessor, byte[] searchPattern)
75-
const BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
76-
var parameterTypes = new[] { typeof(MemoryMappedViewAccessor), typeof(byte[]) };
77-
var searchInFile = typeof(Microsoft.NET.HostModel.AppHost.BinaryUtils).GetMethod("SearchInFile", bindingFlags, null, parameterTypes, null);
78-
var bundleSignatureIndex = (int?)searchInFile?.Invoke(null, new object[] { currentProcessView, bundleSignature }) ?? -1;
79-
if (bundleSignatureIndex > 0 && bundleSignatureIndex < currentProcessExeLength)
80-
{
81-
var bundleHeaderOffset = currentProcessView.ReadInt64(bundleSignatureIndex - 8);
82-
using var currentProcessStream = currentProcessFile.CreateViewStream(0, currentProcessExeLength, MemoryMappedFileAccess.Read);
83-
using var reader = new BinaryReader(currentProcessStream, Encoding.UTF8);
84-
// See https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs#L32-L39
85-
// and https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs#L144-L155
86-
reader.BaseStream.Position = bundleHeaderOffset;
87-
var majorVersion = reader.ReadUInt32();
88-
_ = reader.ReadUInt32(); // minorVersion
89-
_ = reader.ReadInt32(); // numEmbeddedFiles
90-
_ = reader.ReadString(); // bundleId
91-
if (majorVersion >= 2)
92-
{
93-
var depsJsonOffset = reader.ReadInt64();
94-
var depsJsonSize = reader.ReadInt64();
95-
using var depsJsonStream = currentProcessFile.CreateViewStream(depsJsonOffset, depsJsonSize, MemoryMappedFileAccess.Read);
96-
using var depsJsonReader = new DependencyContextJsonReader();
97-
return depsJsonReader.Read(depsJsonStream);
98-
}
99-
}
100-
}
101-
}
102-
catch
103-
{
104-
return null;
105-
}
106-
#endif
107-
108-
return null;
109-
}
11058
}
11159
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using System.Diagnostics;
2+
using System.IO;
3+
using System.IO.MemoryMappedFiles;
4+
5+
namespace Serilog.Settings.Configuration.Assemblies
6+
{
7+
static class SingleFileApplication
8+
{
9+
static byte[] bundleSignature =
10+
{
11+
// 32 bytes represent the bundle signature: SHA-256 for ".net core bundle"
12+
// The first byte is actually 0x8b but we don't want to accidentally have a second place where the bundle
13+
// signature can appear in the single file application so the first byte is set in the static constructor
14+
// See https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs#L216-L222
15+
0x00, 0x12, 0x02, 0xb9, 0x6a, 0x61, 0x20, 0x38, 0x72, 0x7b, 0x93, 0x02, 0x14, 0xd7, 0xa0, 0x32,
16+
0x13, 0xf5, 0xb9, 0xe6, 0xef, 0xae, 0x33, 0x18, 0xee, 0x3b, 0x2d, 0xce, 0x24, 0xb3, 0x6a, 0xae
17+
};
18+
19+
static SingleFileApplication()
20+
{
21+
bundleSignature[0] = 0x8b;
22+
}
23+
24+
public static Stream GetDepsJsonStream()
25+
{
26+
var appHostPath = Process.GetCurrentProcess().MainModule?.FileName;
27+
if (appHostPath == null)
28+
{
29+
return null;
30+
}
31+
32+
using var appHostFile = MemoryMappedFile.CreateFromFile(appHostPath, FileMode.Open, null, 0, MemoryMappedFileAccess.Read);
33+
using Stream appHostStream = appHostFile.CreateViewStream(0, 0, MemoryMappedFileAccess.Read);
34+
var bundleSignatureIndex = SearchBundleSignature(appHostStream);
35+
if (bundleSignatureIndex == -1)
36+
{
37+
return null;
38+
}
39+
40+
using var appHostReader = new BinaryReader(appHostStream);
41+
appHostReader.BaseStream.Position = bundleSignatureIndex - 8;
42+
var bundleHeaderOffset = appHostReader.ReadInt64();
43+
if (bundleHeaderOffset <= 0 || bundleHeaderOffset >= appHostStream.Length)
44+
{
45+
return null;
46+
}
47+
48+
// See https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs#L32-L39
49+
// and https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs#L144-L155
50+
appHostReader.BaseStream.Position = bundleHeaderOffset;
51+
var majorVersion = appHostReader.ReadUInt32();
52+
_ = appHostReader.ReadUInt32(); // minorVersion
53+
var numEmbeddedFiles = appHostReader.ReadInt32();
54+
_ = appHostReader.ReadString(); // bundleId
55+
if (majorVersion >= 2)
56+
{
57+
var depsJsonOffset = appHostReader.ReadInt64();
58+
var depsJsonSize = appHostReader.ReadInt64();
59+
return appHostFile.CreateViewStream(depsJsonOffset, depsJsonSize, MemoryMappedFileAccess.Read);
60+
}
61+
62+
// For version < 2 all the file entries must be enumerated until the `DepsJson` type is found
63+
// See https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileEntry.cs#L43-L54
64+
for (var i = 0; i < numEmbeddedFiles; i++)
65+
{
66+
var offset = appHostReader.ReadInt64();
67+
var size = appHostReader.ReadInt64();
68+
var type = appHostReader.ReadByte();
69+
_ = appHostReader.ReadString(); // relativePath
70+
if (type == 3)
71+
{
72+
// type 3 is the .deps.json configuration file
73+
// See https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/FileType.cs#L17
74+
return appHostFile.CreateViewStream(offset, size, MemoryMappedFileAccess.Read);
75+
}
76+
}
77+
78+
return null;
79+
}
80+
81+
static int SearchBundleSignature(Stream stream)
82+
{
83+
var m = 0;
84+
var i = 0;
85+
86+
var length = stream.Length;
87+
while (m + i < length)
88+
{
89+
stream.Position = m + i;
90+
if (bundleSignature[i] == stream.ReadByte())
91+
{
92+
if (i == bundleSignature.Length - 1)
93+
{
94+
return m;
95+
}
96+
i++;
97+
}
98+
else
99+
{
100+
m += i == 0 ? 1 : i;
101+
i = 0;
102+
}
103+
}
104+
105+
return -1;
106+
}
107+
}
108+
}

0 commit comments

Comments
 (0)