Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Commit 8a83381

Browse files
committed
Update NativeLibraryLoader.cs to fix an issue where it was not loading dynamic libraries correctly in Linux.
1 parent e12b2c8 commit 8a83381

File tree

4 files changed

+130
-79
lines changed

4 files changed

+130
-79
lines changed

src/Data.HashFunction.Blake3/Hasher.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,15 @@ internal unsafe class Hasher : IDisposable
7171
private static NativeLibraryLoader lib_loader;
7272
static Hasher()
7373
{
74-
lib_loader = NativeLibraryLoader.Load("blake3_dotnet");
74+
OperatingSystemHelper.PlatformType pType = OperatingSystemHelper.GetCurrentPlatfom();
75+
76+
if (pType == OperatingSystemHelper.PlatformType.Undefined)
77+
throw new PlatformNotSupportedException("Blake3 modules are not available for this operating system.");
78+
79+
if (pType == OperatingSystemHelper.PlatformType.Windows)
80+
lib_loader = NativeLibraryLoader.Load("blake3_dotnet");
81+
else
82+
lib_loader = NativeLibraryLoader.Load("libblake3_dotnet");
7583

7684
lib_loader.LoadFunction<blake3_new_del>("blake3_new", out blake3_new);
7785
lib_loader.LoadFunction<blake3_new_keyed_del>("blake3_new_keyed", out blake3_new_keyed);

src/Data.HashFunction.Core/NativeLibraryLoader/Libdl.cs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,49 @@ namespace Data.HashFunction.Core.LibraryLoader
77
{
88
internal static class Libdl
99
{
10-
[DllImport("libdl.so", EntryPoint = "dlopen")]
11-
public static extern IntPtr dlopen_linux(string fileName, int flags);
10+
[DllImport("libdl.so.2", EntryPoint = "dlopen", CallingConvention = CallingConvention.Cdecl)]
11+
public static extern IntPtr dlopen_linux([MarshalAs(UnmanagedType.LPStr)] string fileName, int flags);
1212

13-
[DllImport("libdl.so", EntryPoint = "dlsym")]
13+
[DllImport("libdl.so.2", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl)]
1414
public static extern IntPtr dlsym_linux(IntPtr handle, string name);
1515

16-
[DllImport("libdl.so", EntryPoint = "dlclose")]
16+
[DllImport("libdl.so.2", EntryPoint = "dlclose", CallingConvention = CallingConvention.Cdecl)]
1717
public static extern int dlclose_linux(IntPtr handle);
1818

19-
[DllImport("libdl.so", EntryPoint = "dlerror")]
20-
public static extern string dlerror_linux();
19+
[DllImport("libdl.so.2", EntryPoint = "dlerror", CallingConvention = CallingConvention.Cdecl)]
20+
public static extern IntPtr dlerror_linux();
2121

2222

23-
[DllImport("libdl.dylib", EntryPoint = "dlopen")]
24-
public static extern IntPtr dlopen_osx(string fileName, int flags);
23+
[DllImport("libdl.dylib", EntryPoint = "dlopen", CallingConvention = CallingConvention.Cdecl)]
24+
public static extern IntPtr dlopen_osx([MarshalAs(UnmanagedType.LPStr)] string fileName, int flags);
2525

26-
[DllImport("libdl.dylib", EntryPoint = "dlsym")]
26+
[DllImport("libdl.dylib", EntryPoint = "dlsym", CallingConvention = CallingConvention.Cdecl)]
2727
public static extern IntPtr dlsym_osx(IntPtr handle, string name);
2828

29-
[DllImport("libdl.dylib", EntryPoint = "dlclose")]
29+
[DllImport("libdl.dylib", EntryPoint = "dlclose", CallingConvention = CallingConvention.Cdecl)]
3030
public static extern int dlclose_osx(IntPtr handle);
3131

32-
[DllImport("libdl.dylib", EntryPoint = "dlerror")]
33-
public static extern string dlerror_osx();
32+
[DllImport("libdl.dylib", EntryPoint = "dlerror", CallingConvention = CallingConvention.Cdecl)]
33+
public static extern IntPtr dlerror_osx();
3434

3535
public const int RTLD_NOW = 0x002;
36-
}
36+
37+
public static string DLError()
38+
{
39+
IntPtr ptr = IntPtr.Zero;
40+
41+
OperatingSystemHelper.PlatformType pt = OperatingSystemHelper.GetCurrentPlatfom();
42+
if (pt == OperatingSystemHelper.PlatformType.Linux || pt == OperatingSystemHelper.PlatformType.Android)
43+
ptr = dlerror_linux();
44+
else if (pt == OperatingSystemHelper.PlatformType.MacOS || pt == OperatingSystemHelper.PlatformType.iOS)
45+
ptr = dlerror_osx();
46+
47+
if (ptr == IntPtr.Zero)
48+
return null;
49+
50+
string str = Marshal.PtrToStringAnsi(ptr);
51+
return str.Trim();
52+
}
53+
}
3754
}
3855

src/Data.HashFunction.Core/NativeLibraryLoader/NativeLibraryLoader.cs

Lines changed: 89 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.IO;
45
using System.Runtime.InteropServices;
@@ -14,13 +15,13 @@ public abstract class NativeLibraryLoader : IDisposable
1415
protected readonly string libraryName;
1516
protected readonly IntPtr libraryHandle;
1617

17-
/// <summary>
18-
/// Initializes a new instance of <see cref="NativeLibraryLoader"/> for use with the given library name/path. This will automatically load the given library correctly for the current running operating system and architecture.
19-
/// </summary>
20-
/// <param name="library">The name/path of the library to be loaded.</param>
21-
/// <exception cref="ArgumentNullException">Thrown when the given parameter is null.</exception>
22-
/// <exception cref="FileLoadException">Thrown when the given library could not be loaded either because it is not found or invalid for the current operating system and architecture.</exception>
23-
public NativeLibraryLoader(string library)
18+
/// <summary>
19+
/// Initializes a new instance of <see cref="NativeLibraryLoader"/> for use with the given library name/path. This will automatically load the given library correctly for the current running operating system and architecture.
20+
/// </summary>
21+
/// <param name="library">The name/path of the library to be loaded.</param>
22+
/// <exception cref="ArgumentNullException">Thrown when the given parameter is null.</exception>
23+
/// <exception cref="FileLoadException">Thrown when the given library could not be loaded either because it is not found or invalid for the current operating system and architecture.</exception>
24+
public NativeLibraryLoader(string library)
2425
{
2526
_ = library ?? throw new ArgumentNullException(nameof(library));
2627

@@ -29,80 +30,92 @@ public NativeLibraryLoader(string library)
2930
{
3031
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
3132
{
32-
library += ".dll";
33+
library += ".dll";
3334
}
3435
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
3536
{
36-
library += ".so";
37-
}
37+
library += ".so";
38+
}
3839
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
3940
{
40-
library += ".dylib";
41-
}
41+
library += ".dylib";
42+
}
4243
}
4344

44-
this.libraryName = library;
45-
libraryHandle = LoadLibrary(this.libraryName);
45+
if (!Path.IsPathRooted(library))
46+
{
47+
library = Path.Combine(AppContext.BaseDirectory, library);
48+
}
4649

47-
if (libraryHandle == IntPtr.Zero)
50+
this.libraryName = library;
51+
52+
List<string> searchPaths = new List<string>()
4853
{
49-
string p = Path.Combine(Path.GetDirectoryName(this.libraryName), "runtimes", Path.GetFileName(this.libraryName));
50-
libraryHandle = LoadLibrary(p);
51-
}
54+
this.libraryName,
55+
Path.Combine(Path.GetDirectoryName(this.libraryName), "runtimes", Path.GetFileName(this.libraryName)),
5256

53-
if (libraryHandle == IntPtr.Zero)
54-
{
55-
string fn = "";
57+
};
5658

57-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
58-
{
59-
fn = "win";
60-
}
61-
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
62-
{
63-
fn = "linux";
64-
}
65-
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
66-
{
67-
fn = "osx";
68-
}
59+
string fn = "";
6960

70-
if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
71-
{
72-
fn += "-x86";
73-
}
74-
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
75-
{
76-
fn += "-x64";
77-
}
78-
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm)
79-
{
80-
fn += "-arm";
81-
}
82-
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
83-
{
84-
fn += "-arm64";
85-
}
61+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
62+
{
63+
fn = "win";
64+
}
65+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
66+
{
67+
fn = "linux";
68+
}
69+
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
70+
{
71+
fn = "osx";
72+
}
8673

87-
string p = Path.Combine(Path.GetDirectoryName(this.libraryName), "runtimes", fn, Path.GetFileName(this.libraryName));
88-
libraryHandle = LoadLibrary(p);
74+
if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
75+
{
76+
fn += "-x86";
77+
}
78+
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
79+
{
80+
fn += "-x64";
81+
}
82+
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm)
83+
{
84+
fn += "-arm";
85+
}
86+
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
87+
{
88+
fn += "-arm64";
89+
}
8990

90-
if (libraryHandle == IntPtr.Zero)
91-
{
92-
p = Path.Combine(Path.GetDirectoryName(this.libraryName), "runtimes", fn, "native", Path.GetFileName(this.libraryName));
93-
libraryHandle = LoadLibrary(p);
94-
}
95-
}
91+
string p = Path.Combine(Path.GetDirectoryName(this.libraryName), "runtimes", fn, Path.GetFileName(this.libraryName));
92+
searchPaths.Add(p);
93+
94+
p = Path.Combine(Path.GetDirectoryName(this.libraryName), "runtimes", fn, "native", Path.GetFileName(this.libraryName));
95+
searchPaths.Add(p);
9696

97-
if (libraryHandle == IntPtr.Zero)
97+
foreach (string searchPath in searchPaths)
9898
{
99-
throw new FileLoadException("Could not load " + libraryName);
99+
libraryHandle = LoadLibrary(searchPath);
100+
if (libraryHandle != IntPtr.Zero)
101+
break;
100102
}
103+
104+
if (libraryHandle == IntPtr.Zero)
105+
{
106+
string err = GetError();
107+
if (string.IsNullOrEmpty(err))
108+
throw new FileLoadException("Could not load " + searchPaths[searchPaths.Count - 1]);
109+
else
110+
throw new FileLoadException("Could not load " + searchPaths[searchPaths.Count - 1] + ": " + err);
111+
}
101112
}
102113

103114
protected abstract IntPtr LoadLibrary(string libraryName);
104115
protected abstract void FreeLibrary(IntPtr libraryHandle);
105116

117+
protected abstract string GetError();
118+
106119
/// <summary>
107120
/// Tries to load a function with the given name.
108121
/// </summary>
@@ -194,7 +207,12 @@ public override IntPtr LoadFunction(string functionName)
194207
{
195208
return Kernel32.GetProcAddress(libraryHandle, functionName);
196209
}
197-
}
210+
211+
protected override string GetError()
212+
{
213+
return Marshal.GetLastWin32Error().ToString();
214+
}
215+
}
198216

199217
private class LinuxNativeLibrary : NativeLibraryLoader
200218
{
@@ -204,7 +222,6 @@ public LinuxNativeLibrary(string library) : base(library)
204222

205223
protected override IntPtr LoadLibrary(string libraryName)
206224
{
207-
Libdl.dlerror_linux();
208225
IntPtr handle = Libdl.dlopen_linux(libraryName, Libdl.RTLD_NOW);
209226
if (handle == IntPtr.Zero && !Path.IsPathRooted(libraryName))
210227
{
@@ -228,7 +245,12 @@ public override IntPtr LoadFunction(string functionName)
228245
{
229246
return Libdl.dlsym_linux(libraryHandle, functionName);
230247
}
231-
}
248+
249+
protected override string GetError()
250+
{
251+
return Libdl.DLError();
252+
}
253+
}
232254

233255
private class OSXNativeLibrary : NativeLibraryLoader
234256
{
@@ -238,7 +260,6 @@ public OSXNativeLibrary(string library) : base(library)
238260

239261
protected override IntPtr LoadLibrary(string libraryName)
240262
{
241-
Libdl.dlerror_osx();
242263
IntPtr handle = Libdl.dlopen_osx(libraryName, Libdl.RTLD_NOW);
243264
if (handle == IntPtr.Zero && !Path.IsPathRooted(libraryName))
244265
{
@@ -262,6 +283,11 @@ public override IntPtr LoadFunction(string functionName)
262283
{
263284
return Libdl.dlsym_osx(libraryHandle, functionName);
264285
}
286+
287+
protected override string GetError()
288+
{
289+
return Libdl.DLError();
290+
}
265291
}
266292
}
267293
}

src/Data.HashFunction.Core/NativeLibraryLoader/OperatingSystemHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Data.HashFunction.Core.LibraryLoader
99
{
10-
internal static class OperatingSystemHelper
10+
public static class OperatingSystemHelper
1111
{
1212
public static bool IsOSPlatform(PlatformType platform)
1313
{
@@ -87,7 +87,7 @@ public static PlatformType GetCurrentPlatfom()
8787
/// <summary>
8888
/// Specifies the platform type.
8989
/// </summary>
90-
internal enum PlatformType
90+
public enum PlatformType
9191
{
9292
/// <summary>
9393
/// Undefined platform.

0 commit comments

Comments
 (0)