From 252c19bb8b1e66ff3c1f0a21b16152c04f84998f Mon Sep 17 00:00:00 2001 From: Jason Naylor Date: Wed, 10 Feb 2021 15:56:57 -0800 Subject: [PATCH] Add Mac support for library loading --- source/icu.net/NativeMethods/NativeMethods.cs | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/source/icu.net/NativeMethods/NativeMethods.cs b/source/icu.net/NativeMethods/NativeMethods.cs index a3344afb..173aba1d 100644 --- a/source/icu.net/NativeMethods/NativeMethods.cs +++ b/source/icu.net/NativeMethods/NativeMethods.cs @@ -57,7 +57,7 @@ static NativeMethods() #region Dynamic method loading - #region Native methods for Linux + #region Native methods for Unix private const int RTLD_NOW = 2; @@ -81,7 +81,26 @@ private static string dlerror() var ptr = _dlerror(); return Marshal.PtrToStringAnsi(ptr); } + private const string MAC_LIBDL_NAME = "libdl.dylib"; + [DllImport(MAC_LIBDL_NAME, SetLastError = true)] + private static extern IntPtr mac_dlopen(string file, int mode); + + [DllImport(MAC_LIBDL_NAME, SetLastError = true)] + private static extern int mac_dlclose(IntPtr handle); + + [DllImport(MAC_LIBDL_NAME, SetLastError = true)] + private static extern IntPtr mac_dlsym(IntPtr handle, string name); + + [DllImport(MAC_LIBDL_NAME, EntryPoint = "dlerror")] + private static extern IntPtr _mac_dlerror(); + + private static string mac_dlerror() + { + // Don't free the string returned from _dlerror()! + var ptr = _mac_dlerror(); + return Marshal.PtrToStringAnsi(ptr); + } #endregion #region Native methods for Windows @@ -120,6 +139,7 @@ private enum LoadLibraryFlags : uint private static IntPtr _IcuI18NLibHandle; private static bool IsWindows => Platform.OperatingSystem == OperatingSystemType.Windows; + private static bool IsMac = Platform.OperatingSystem == OperatingSystemType.MacOSX; private static IntPtr IcuCommonLibHandle { @@ -295,6 +315,17 @@ private static IntPtr GetIcuLibHandle(string basename, int icuVersion) Trace.WriteLineIf(handle == IntPtr.Zero && lastError != 0, $"Unable to load [{libPath}]. Error: {new Win32Exception(lastError).Message}"); } + if (IsMac) + { + var libName = $"lib{basename}.{icuVersion}.dylib"; + libPath = string.IsNullOrEmpty(_IcuPath) ? libName : Path.Combine(_IcuPath, libName); + + handle = mac_dlopen(libPath, RTLD_NOW); + lastError = Marshal.GetLastWin32Error(); + + Trace.WriteLineIf(handle == IntPtr.Zero && lastError != 0, + $"Unable to load [{libPath}]. Error: {lastError} ({dlerror()})"); + } else { var libName = $"lib{basename}.so.{icuVersion}"; @@ -306,10 +337,11 @@ private static IntPtr GetIcuLibHandle(string basename, int icuVersion) Trace.WriteLineIf(handle == IntPtr.Zero && lastError != 0, $"Unable to load [{libPath}]. Error: {lastError} ({dlerror()})"); } + if (handle == IntPtr.Zero) { Trace.TraceWarning("{0} of {1} failed with error {2}", - IsWindows ? "LoadLibraryEx" : "dlopen", + IsWindows ? "LoadLibraryEx" : IsMac ? "mac_dlopen" : "dlopen", libPath, lastError); return GetIcuLibHandle(basename, icuVersion - 1); } @@ -339,6 +371,13 @@ public static void Cleanup() if (_IcuI18NLibHandle != IntPtr.Zero) FreeLibrary(_IcuI18NLibHandle); } + else if (IsMac) + { + if (_IcuCommonLibHandle != IntPtr.Zero) + mac_dlclose(_IcuCommonLibHandle); + if (_IcuI18NLibHandle != IntPtr.Zero) + mac_dlclose(_IcuI18NLibHandle); + } else { if (_IcuCommonLibHandle != IntPtr.Zero) @@ -389,14 +428,14 @@ private static T GetMethod(IntPtr handle, string methodName, bool missingInMi var versionedMethodName = $"{methodName}_{IcuVersion}"; var methodPointer = IsWindows ? GetProcAddress(handle, versionedMethodName) : - dlsym(handle, versionedMethodName); + IsMac ? mac_dlsym(handle, versionedMethodName) : dlsym(handle, versionedMethodName); // Some systems (eg. Tizen) don't use methods with IcuVersion suffix if (methodPointer == IntPtr.Zero) { methodPointer = IsWindows ? GetProcAddress(handle, methodName) : - dlsym(handle, methodName); + IsMac ? mac_dlsym(handle, methodName) : dlsym(handle, methodName); } if (methodPointer != IntPtr.Zero) {