Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 3 additions & 74 deletions csharp/src/Microsoft.ML.OnnxRuntime/NativeMethods.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,8 @@ internal class NativeLib
#if !NETSTANDARD2_0 && !__ANDROID__ && !__IOS__
/// <summary>
/// Custom DllImportResolver to handle platform-specific library loading.
/// On Windows, it explicitly loads the library with a lowercase .dll extension to handle
/// case-sensitive filesystems.
/// It handles the addition of "lib" prefix and file extensions (.so/.dylib) for Linux/macOS,
/// and .dll extension for Windows (handling case-sensitivity).
/// </summary>
private static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
Expand All @@ -915,89 +915,18 @@ private static IntPtr DllImportResolver(string libraryName, Assembly assembly, D

if (mappedName != null)
{
// 1. Try default loading (name only)
if (NativeLibrary.TryLoad(mappedName, assembly, searchPath, out IntPtr handle))
{
return handle;
}

// 2. Try relative to assembly location (look into runtimes subfolders)
string assemblyLocation = null;
try { assemblyLocation = assembly.Location; } catch { }
if (!string.IsNullOrEmpty(assemblyLocation))
{
string assemblyDir = System.IO.Path.GetDirectoryName(assemblyLocation);
string rid = RuntimeInformation.RuntimeIdentifier;

// Probe the specific RID first, then common fallbacks for the current OS
string[] ridsToTry;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
ridsToTry = new[] { rid, "win-x64", "win-arm64" };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
ridsToTry = new[] { rid, "linux-x64", "linux-arm64" };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
// We no longer provide osx-x64 in official package since 1.24.
// However, we keep it in the list for build-from-source users.
ridsToTry = new[] { rid, "osx-arm64", "osx-x64" };
}
else
{
ridsToTry = new[] { rid };
}

foreach (var tryRid in ridsToTry)
{
string probePath = System.IO.Path.Combine(assemblyDir, "runtimes", tryRid, "native", mappedName);
if (System.IO.File.Exists(probePath) && NativeLibrary.TryLoad(probePath, assembly, searchPath, out handle))
{
LogLibLoad($"[DllImportResolver] Loaded {mappedName} from: {probePath}");
return handle;
}
}
}

// 3. Try AppContext.BaseDirectory as a fallback
string baseDir = AppContext.BaseDirectory;
if (!string.IsNullOrEmpty(baseDir))
{
string probePath = System.IO.Path.Combine(baseDir, mappedName);
if (NativeLibrary.TryLoad(probePath, assembly, searchPath, out handle))
{
LogLibLoad($"[DllImportResolver] Loaded {mappedName} from: {probePath}");
return handle;
}

string rid = RuntimeInformation.RuntimeIdentifier;
probePath = System.IO.Path.Combine(baseDir, "runtimes", rid, "native", mappedName);
if (NativeLibrary.TryLoad(probePath, assembly, searchPath, out handle))
{
LogLibLoad($"[DllImportResolver] Loaded {mappedName} from: {probePath}");
return handle;
}
}

LogLibLoad($"[DllImportResolver] Failed loading {mappedName} (RID: {RuntimeInformation.RuntimeIdentifier}, Assembly: {assemblyLocation})");

}
}

// Fall back to default resolution
return IntPtr.Zero;
}

private static void LogLibLoad(string message)
{
System.Diagnostics.Trace.WriteLine(message);
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ORT_LOADER_VERBOSITY")))
{
Console.WriteLine(message);
}
}

#endif

[DllImport(NativeLib.DllName, CharSet = CharSet.Ansi)]
Expand Down
120 changes: 120 additions & 0 deletions csharp/src/Microsoft.ML.OnnxRuntime/targets/netstandard/props.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,125 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>

<!-- linux-x64 -->
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime.so"
Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime.so')">
<Link>libonnxruntime.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
Comment on lines +147 to +153
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Condition for copying linux-x64 native assets is based only on PlatformTarget/Prefer32Bit. This will also evaluate true when building on non-Linux hosts (e.g., Windows x64) and can copy Linux .so files into the output unintentionally. Consider conditioning on the target/runtime RID (e.g., $(RuntimeIdentifier) / $(NETCoreSdkRuntimeIdentifier)) and/or $([MSBuild]::IsOSPlatform('Linux')) so only the intended platform’s native assets are copied.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need to update all the copy commands to use the runtime identifier as the condition, but getting that can be non-trivial.

AI suggested set of steps:

  <PropertyGroup>

    <!-- Primary: Use explicit RID if set -->
    <_ResolvedRuntimeIdentifier Condition="'$(RuntimeIdentifier)' != ''">
      $(RuntimeIdentifier)
    </_ResolvedRuntimeIdentifier>

    <!-- SDK internal fallback (works in most modern SDK builds) -->
    <_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(NETCoreSdkRuntimeIdentifier)' != ''">
      $(NETCoreSdkRuntimeIdentifier)
    </_ResolvedRuntimeIdentifier>

    <!-- .NET Framework fallback -->
    <_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(TargetFrameworkIdentifier)' == '.NETFramework'">
      win
    </_ResolvedRuntimeIdentifier>

    <!-- netstandard has no runtime -->
    <_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(TargetFrameworkIdentifier)' == '.NETStandard'">
      any
    </_ResolvedRuntimeIdentifier>

    <!-- .NET (Core / 5+) fallback -->
    <_ResolvedRuntimeIdentifier Condition="'$(_ResolvedRuntimeIdentifier)' == '' AND '$(TargetFrameworkIdentifier)' == '.NETCoreApp'">
      portable
    </_ResolvedRuntimeIdentifier>

    <!-- Final exposed property -->
    <ResolvedRuntimeIdentifier>$(_ResolvedRuntimeIdentifier)</ResolvedRuntimeIdentifier>

  </PropertyGroup>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And for added fun, if it's an AnyCPU build we probably need to copy both x64/arm64 if available to the output dir and put them under runtimes/<runtime id>/native/.... Sorry - I overlooked that use case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking a step back, if we have the probing looking at runtimes for development scenarios do we need manual copying at all? The 'publish' step might take care of copying the correct binaries for the publish target so we could be unnecessarily complicating things.

<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_shared.so"
Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_shared.so')">
<Link>libonnxruntime_providers_shared.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_cuda.so"
Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_cuda.so')">
<Link>libonnxruntime_providers_cuda.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_dnnl.so"
Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_dnnl.so')">
<Link>libonnxruntime_providers_dnnl.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_tensorrt.so"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we build this in a package?

Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_tensorrt.so')">
<Link>libonnxruntime_providers_tensorrt.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_openvino.so"
Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-x64\native\libonnxruntime_providers_openvino.so')">
<Link>libonnxruntime_providers_openvino.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>

<!-- linux-arm64 -->
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime.so')">
<Link>libonnxruntime.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so')">
<Link>libonnxruntime_providers_shared.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so')">
<Link>libonnxruntime_providers_cuda.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_dnnl.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Comment on lines +192 to +213
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linux-arm64 assets are copied only when $(PlatformTarget) == 'ARM64'. In many .NET SDK-style projects (including the 'portable .NETCoreApp' case noted in targets.xml), PlatformTarget is often empty/AnyCPU even on arm64 machines, so this block may not run and the native library won’t be copied—while NativeMethods.shared.cs now relies on default probing. Please key this off the runtime RID (e.g., linux-arm64) or $(NETCoreSdkRuntimeIdentifier) to correctly select arm64 assets when running/building on arm64.

Suggested change
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime.so')">
<Link>libonnxruntime.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so')">
<Link>libonnxruntime_providers_shared.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so')">
<Link>libonnxruntime_providers_cuda.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_dnnl.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime.so')">
<Link>libonnxruntime.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so"
Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_shared.so')">
<Link>libonnxruntime_providers_shared.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so"
Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_cuda.so')">
<Link>libonnxruntime_providers_cuda.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_dnnl.so"
Condition="('$(PlatformTarget)' == 'ARM64' OR '$(NETCoreSdkRuntimeIdentifier)' == 'linux-arm64') AND

Copilot uses AI. Check for mistakes.
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_dnnl.so')">
<Link>libonnxruntime_providers_dnnl.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_tensorrt.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_tensorrt.so')">
<Link>libonnxruntime_providers_tensorrt.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_openvino.so"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\linux-arm64\native\libonnxruntime_providers_openvino.so')">
<Link>libonnxruntime_providers_openvino.so</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>

<!-- osx-x64 -->
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime.dylib"
Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime.dylib')">
<Link>libonnxruntime.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime_providers_shared.dylib"
Condition="('$(PlatformTarget)' == 'x64' OR ('$(PlatformTarget)' == 'AnyCPU' AND '$(Prefer32Bit)' != 'true')) AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libonnxruntime_providers_shared.dylib')">
<Link>libonnxruntime_providers_shared.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>

<!-- osx-arm64 -->
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-arm64\native\libonnxruntime.dylib"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\osx-arm64\native\libonnxruntime.dylib')">
<Link>libonnxruntime.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
Comment on lines +235 to +257
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The osx-x64 block is gated by the same x64/AnyCPU PlatformTarget condition, and osx-arm64 is gated by PlatformTarget == 'ARM64'. On Apple Silicon, PlatformTarget is commonly AnyCPU/empty, which could result in no macOS native assets being copied (and osx-x64 assets likely don’t exist in official packages). Suggest using $(RuntimeIdentifier)/$(NETCoreSdkRuntimeIdentifier) to choose between osx-arm64 vs osx-x64, and/or $([MSBuild]::IsOSPlatform('OSX')) to avoid copying macOS libs on other hosts.

Copilot uses AI. Check for mistakes.
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-arm64\native\libonnxruntime_providers_shared.dylib"
Condition="'$(PlatformTarget)' == 'ARM64' AND
Exists('$(MSBuildThisFileDirectory)..\..\runtimes\osx-arm64\native\libonnxruntime_providers_shared.dylib')">
<Link>libonnxruntime_providers_shared.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</None>
</ItemGroup>
</Project>
Loading