diff --git a/README.md b/README.md
index eac0365f8e..0b9689faad 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
Reader of [Portable PDBs](https://github.com/dotnet/core/blob/main/Documentation/diagnostics/portable_pdb.md) format that implements DiaSymReader interfaces ([ISymUnmanagedReader](https://msdn.microsoft.com/en-us/library/ms232131.aspx), [ISymUnmanagedBinder](https://msdn.microsoft.com/en-us/library/ms232451.aspx), etc.).
The definitions of the DiaSymReader COM interfaces are provided by [Microsoft.DiaSymReader](https://www.nuget.org/packages/Microsoft.DiaSymReader) package.
-The implementation of these interfaces for Windows PDBs is provided by [Microsoft.DiaSymReader.Native](https://www.nuget.org/packages/Microsoft.DiaSymReader.Native) package.
+The implementation of these interfaces for Windows PDBs is provided by `Microsoft.DiaSymReader.Native.{platform}.dll`, which ships in the .NET runtime.
It is recommended that new applications and libraries read Portable PDBs directly using APIs provided by [System.Reflection.Metadata](https://www.nuget.org/packages/System.Reflection.Metadata) package. These APIs are much more efficient than DiaSymReader APIs. Microsoft.DiaSymReader.PortablePdb bridge is recommended for existings apps that already use DiaSymReader APIs and need to be able to read Portable PDBs without significant changes to their source.
diff --git a/global.json b/global.json
index 68b114e5c9..0b39638943 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,11 @@
{
"tools": {
- "dotnet": "11.0.100-preview.5.26227.104"
+ "dotnet": "11.0.100-preview.5.26227.104",
+ "runtimes": {
+ "dotnet/x86": [
+ "11.0.0-preview.5.26227.104"
+ ]
+ }
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26310.1"
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index 8888098f03..fffa6e5430 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -2,4 +2,10 @@
+
+
+
+ x64;x86
+
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 09568fd521..d5a27a6f62 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -10,7 +10,6 @@
-
\ No newline at end of file
diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj
index efd5a69247..9bfb889de3 100644
--- a/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj
+++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/Microsoft.DiaSymReader.PortablePdb.UnitTests.csproj
@@ -3,21 +3,12 @@
$(NetCurrent);$(NetFrameworkCurrent)
true
-
- x64;x86
-
-
-
-
@@ -175,17 +166,4 @@
-
-
- PreserveNewest
- false
- Microsoft.DiaSymReader.Native.x86.dll
-
-
- PreserveNewest
- false
- Microsoft.DiaSymReader.Native.amd64.dll
-
-
-
diff --git a/src/Microsoft.DiaSymReader.PortablePdb.Tests/TestHelpers/DiaSymReaderNativeRuntime.cs b/src/Microsoft.DiaSymReader.PortablePdb.Tests/TestHelpers/DiaSymReaderNativeRuntime.cs
new file mode 100644
index 0000000000..1016ceeec0
--- /dev/null
+++ b/src/Microsoft.DiaSymReader.PortablePdb.Tests/TestHelpers/DiaSymReaderNativeRuntime.cs
@@ -0,0 +1,162 @@
+// 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.txt file in the project root for more information.
+
+#if NETFRAMEWORK
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Roslyn.Test.Utilities
+{
+ internal static class DiaSymReaderNativeRuntime
+ {
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ private static extern IntPtr AddDllDirectory(string newDirectory);
+
+ [ModuleInitializer]
+ internal static void AddInstalledDotNetRuntimeDirectory()
+ {
+ string directory = GetInstalledDotNetRuntimeDirectory();
+ if (string.IsNullOrEmpty(directory))
+ {
+ throw new InvalidOperationException("Could not locate an installed .NET runtime containing Microsoft.DiaSymReader.Native.");
+ }
+
+ if (AddDllDirectory(directory) == IntPtr.Zero)
+ {
+ throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
+ }
+ }
+
+ private static string GetInstalledDotNetRuntimeDirectory()
+ {
+ string root = Environment.GetEnvironmentVariable("DOTNET_ROOT")
+ ?? Environment.GetEnvironmentVariable("DOTNET_INSTALL_DIR");
+
+ if (string.IsNullOrEmpty(root))
+ {
+ string hostPath = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH");
+ if (!string.IsNullOrEmpty(hostPath))
+ {
+ root = Path.GetDirectoryName(hostPath);
+ }
+ }
+
+ if (string.IsNullOrEmpty(root))
+ {
+ string dotnetHost = FindDotNetHost();
+ if (dotnetHost != null)
+ {
+ root = Path.GetDirectoryName(dotnetHost);
+ }
+ }
+
+ if (string.IsNullOrEmpty(root))
+ {
+ root = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet");
+ }
+
+ string dllName = IntPtr.Size == 4
+ ? "Microsoft.DiaSymReader.Native.x86.dll"
+ : RuntimeInformation.ProcessArchitecture == Architecture.Arm64
+ ? "Microsoft.DiaSymReader.Native.arm64.dll"
+ : "Microsoft.DiaSymReader.Native.amd64.dll";
+
+ foreach (string candidate in GetRuntimeRootCandidates(root))
+ {
+ string directory = FindRuntimeDirectory(candidate, dllName);
+ if (directory != null)
+ {
+ return directory;
+ }
+ }
+
+ return null;
+ }
+
+ private static IEnumerable GetRuntimeRootCandidates(string root)
+ {
+ if (IntPtr.Size == 4)
+ {
+ string rootX86 = Environment.GetEnvironmentVariable("DOTNET_ROOT(x86)")
+ ?? Environment.GetEnvironmentVariable("DOTNET_ROOT_X86");
+ if (!string.IsNullOrEmpty(rootX86))
+ {
+ yield return rootX86;
+ }
+
+ if (!string.IsNullOrEmpty(root))
+ {
+ yield return Path.Combine(root, "x86");
+ }
+ }
+
+ if (!string.IsNullOrEmpty(root))
+ {
+ yield return root;
+ }
+ }
+
+ private static string FindRuntimeDirectory(string root, string dllName)
+ {
+ string shared = Path.Combine(root, "shared", "Microsoft.NETCore.App");
+ if (!Directory.Exists(shared))
+ {
+ return null;
+ }
+
+ return Directory.GetDirectories(shared)
+ .Select(dir => (dir, version: TryParseVersion(Path.GetFileName(dir))))
+ .Where(item => item.version != null && File.Exists(Path.Combine(item.dir, dllName)))
+ .OrderByDescending(item => item.version)
+ .Select(item => item.dir)
+ .FirstOrDefault();
+ }
+
+ private static Version TryParseVersion(string name)
+ {
+ string core = name.Split('-')[0];
+ return Version.TryParse(core, out var version) ? version : null;
+ }
+
+ private static string FindDotNetHost()
+ {
+ string pathEnv = Environment.GetEnvironmentVariable("PATH");
+ if (pathEnv == null)
+ {
+ return null;
+ }
+
+ foreach (string dir in pathEnv.Split(Path.PathSeparator))
+ {
+ if (string.IsNullOrWhiteSpace(dir))
+ {
+ continue;
+ }
+
+ string candidate = Path.Combine(dir.Trim(), "dotnet.exe");
+ if (File.Exists(candidate))
+ {
+ return candidate;
+ }
+ }
+
+ return null;
+ }
+ }
+}
+
+namespace System.Runtime.CompilerServices
+{
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ internal sealed class ModuleInitializerAttribute : Attribute
+ {
+ }
+}
+
+#endif