From 634e68bf1131b5bef1d2adf60d333c423edcdce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Thu, 26 May 2022 14:59:49 +0900 Subject: [PATCH 1/4] Implement LoadLibraryErrorTracker Finishes the LoadLibraryErrorTracker port from CoreCLR VM to C#. This allows us to report more details about the reason of `DllImport` resolution failures at runtime. The logic matches CoreCLR. There might be a slight behavior difference for empty string literal `DllImport`/`NativeLibrary.Load`. Not sure we care enough (I couldn't discern what the CoreCLR behavior is - I think we report an arbitrary old message from the last load attempt since we never even try to `dlopen` the empty string). Instead of: ``` System.DllNotFoundException: Unable to load native library 'bruh' or one of its dependencies. ``` We can now do: ``` System.DllNotFoundException: Unable to load DLL 'bruh' or one of its dependencies: The process cannot access the file because it is being used by another process. ``` --- .../NativeLibrary.NativeAot.cs | 93 +++++++++++++------ .../Unix/System.Native/Interop.DynamicLoad.cs | 3 + .../src/Interop/Windows/Interop.Errors.cs | 1 + .../src/Resources/Strings.resx | 10 +- src/native/libs/System.Native/entrypoints.c | 1 + .../libs/System.Native/pal_dynamicload.c | 5 + .../libs/System.Native/pal_dynamicload.h | 2 + 7 files changed, 86 insertions(+), 29 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs index c8c1d47c00eeda..7535dd0d7f929f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable enable using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; + using LibraryNameVariation = System.Runtime.Loader.LibraryNameVariation; namespace System.Runtime.InteropServices @@ -126,30 +126,19 @@ private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadL } int lastError = Marshal.GetLastWin32Error(); - if (lastError != LoadLibErrorTracker.ERROR_INVALID_PARAMETER) + if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER) { errorTracker.TrackErrorCode(lastError); } return ret; #else - IntPtr ret = IntPtr.Zero; - if (libraryName == null) - { - errorTracker.TrackErrorCode(LoadLibErrorTracker.ERROR_MOD_NOT_FOUND); - } - else if (libraryName == string.Empty) + // TODO: FileDosToUnixPathA + IntPtr ret = Interop.Sys.LoadLibrary(libraryName); + if (ret == IntPtr.Zero) { - errorTracker.TrackErrorCode(LoadLibErrorTracker.ERROR_INVALID_PARAMETER); - } - else - { - // TODO: FileDosToUnixPathA - ret = Interop.Sys.LoadLibrary(libraryName); - if (ret == IntPtr.Zero) - { - errorTracker.TrackErrorCode(LoadLibErrorTracker.ERROR_MOD_NOT_FOUND); - } + string? message = Marshal.PtrToStringAnsi(Interop.Sys.GetLoadLibraryError()); + errorTracker.TrackErrorMessage(message); } return ret; @@ -188,30 +177,80 @@ private static unsafe IntPtr GetSymbol(IntPtr handle, string symbolName, bool th return ret; } - // TODO: copy the nice error logic from CoreCLR's LoadLibErrorTracker - // to get fine-grained error messages that take into account access denied, etc. + // Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places + // if earlier loads fail and those later loads obliterate error codes. + // + // This tracker object will keep track of the error code in accordance to priority: + // + // low-priority: unknown error code (should never happen) + // medium-priority: dll not found + // high-priority: dll found but error during loading + // + // We will overwrite the previous load's error code only if the new error code is higher priority. internal struct LoadLibErrorTracker { - internal const int ERROR_INVALID_PARAMETER = 0x57; - internal const int ERROR_MOD_NOT_FOUND = 126; - internal const int ERROR_BAD_EXE_FORMAT = 193; - +#if TARGET_WINDOWS private int _errorCode; + private int _priority; + + private const int PriorityNotFound = 10; + private const int PriorityAccessDenied = 20; + private const int PriorityCouldNotLoad = 99999; public void Throw(string libraryName) { - if (_errorCode == ERROR_BAD_EXE_FORMAT) + if (_errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT) { throw new BadImageFormatException(); } - throw new DllNotFoundException(SR.Format(SR.Arg_DllNotFoundExceptionParameterized, libraryName)); + string message = Interop.Kernel32.GetMessage(_errorCode); + throw new DllNotFoundException(SR.Format(SR.DllNotFound_Windows, libraryName, message)); } public void TrackErrorCode(int errorCode) { - _errorCode = errorCode; + int priority = errorCode switch + { + Interop.Errors.ERROR_FILE_NOT_FOUND or + Interop.Errors.ERROR_PATH_NOT_FOUND or + Interop.Errors.ERROR_MOD_NOT_FOUND or + Interop.Errors.ERROR_DLL_NOT_FOUND => PriorityNotFound, + + // If we can't access a location, we can't know if the dll's there or if it's good. + // Still, this is probably more unusual (and thus of more interest) than a dll-not-found + // so give it an intermediate priority. + Interop.Errors.ERROR_ACCESS_DENIED => PriorityAccessDenied, + + // Assume all others are "dll found but couldn't load." + _ => PriorityCouldNotLoad, + }; + + if (priority > _priority) + { + _errorCode = errorCode; + _priority = priority; + } + } +#else + // On Unix systems we don't have detailed programatic information on why load failed + // so there's no priorities. + private string? _errorMessage; + + public void Throw(string libraryName) + { +#if TARGET_OSX + throw new DllNotFoundException(SR.Format(SR.DllNotFound_Mac, libraryName, _errorMessage)); +#else + throw new DllNotFoundException(SR.Format(SR.DllNotFound_Linux, libraryName, _errorMessage)); +#endif } + + public void TrackErrorMessage(string? message) + { + _errorMessage = message; + } +#endif } } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs index f806b691ae3b95..1da7dd154190e4 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs @@ -11,6 +11,9 @@ internal unsafe partial class Sys [LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_LoadLibrary", StringMarshalling = StringMarshalling.Utf8)] internal static partial IntPtr LoadLibrary(string filename); + [LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetLoadLibraryError")] + internal static partial IntPtr GetLoadLibraryError(); + [LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetProcAddress")] internal static partial IntPtr GetProcAddress(IntPtr handle, byte* symbol); diff --git a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs index 5a38c911ebb2a7..6af8abca7164b0 100644 --- a/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs +++ b/src/libraries/Common/src/Interop/Windows/Interop.Errors.cs @@ -34,6 +34,7 @@ internal static partial class Errors internal const int ERROR_CALL_NOT_IMPLEMENTED = 0x78; internal const int ERROR_INSUFFICIENT_BUFFER = 0x7A; internal const int ERROR_INVALID_NAME = 0x7B; + internal const int ERROR_MOD_NOT_FOUND = 0x7E; internal const int ERROR_NEGATIVE_SEEK = 0x83; internal const int ERROR_DIR_NOT_EMPTY = 0x91; internal const int ERROR_BAD_PATHNAME = 0xA1; diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index a65214fc04e2e6..94a5f50e88f672 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3910,8 +3910,14 @@ Unable to find an entry point named '{0}' in native library. - - Unable to load native library '{0}' or one of its dependencies. + + Unable to load DLL '{0}' or one of its dependencies: {1} + + + Unable to load shared library '{0}' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable: {1} + + + Unable to load shared library '{0}' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: {1} COM Interop requires ComWrapper instance registered for marshalling. diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c index 6a960a42ae14cb..fd7d2127a704c0 100644 --- a/src/native/libs/System.Native/entrypoints.c +++ b/src/native/libs/System.Native/entrypoints.c @@ -234,6 +234,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_LowLevelMonitor_TimedWait) DllImportEntry(SystemNative_LowLevelMonitor_Signal_Release) DllImportEntry(SystemNative_LoadLibrary) + DllImportEntry(SystemNative_GetLoadLibraryError) DllImportEntry(SystemNative_GetProcAddress) DllImportEntry(SystemNative_FreeLibrary) DllImportEntry(SystemNative_GetDefaultSearchOrderPseudoHandle) diff --git a/src/native/libs/System.Native/pal_dynamicload.c b/src/native/libs/System.Native/pal_dynamicload.c index 66a49a68c48e33..6aaf70f50fcdd1 100644 --- a/src/native/libs/System.Native/pal_dynamicload.c +++ b/src/native/libs/System.Native/pal_dynamicload.c @@ -38,6 +38,11 @@ void* SystemNative_LoadLibrary(const char* filename) return dlopen(filename, RTLD_LAZY); } +void* SystemNative_GetLoadLibraryError(void) +{ + return dlerror(); +} + void* SystemNative_GetProcAddress(void* handle, const char* symbol) { // We're not trying to disambiguate between "symbol was not found" and "symbol found, but diff --git a/src/native/libs/System.Native/pal_dynamicload.h b/src/native/libs/System.Native/pal_dynamicload.h index 71ce8bf844be4d..21d570363120a9 100644 --- a/src/native/libs/System.Native/pal_dynamicload.h +++ b/src/native/libs/System.Native/pal_dynamicload.h @@ -8,6 +8,8 @@ PALEXPORT void* SystemNative_LoadLibrary(const char* filename); +PALEXPORT void* SystemNative_GetLoadLibraryError(void); + PALEXPORT void* SystemNative_GetProcAddress(void* handle, const char* symbol); PALEXPORT void SystemNative_FreeLibrary(void* handle); From 6368b08e42d1d3a5aa3abe9429ca7f915a361a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 27 May 2022 16:01:12 +0900 Subject: [PATCH 2/4] Split unix/windows stuff --- .../src/System.Private.CoreLib.csproj | 2 + .../NativeLibrary.NativeAot.Unix.cs | 54 +++++++ .../NativeLibrary.NativeAot.Windows.cs | 103 ++++++++++++++ .../NativeLibrary.NativeAot.cs | 132 +----------------- 4 files changed, 160 insertions(+), 131 deletions(-) create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 7fd43a5dedce8d..f4ee5e6a309331 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -258,6 +258,7 @@ + System\Runtime\InteropServices\Variant.cs @@ -317,6 +318,7 @@ + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs new file mode 100644 index 00000000000000..2930dd23a4765a --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Runtime.InteropServices +{ + public static partial class NativeLibrary + { + private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadLibErrorTracker errorTracker) + { + // TODO: FileDosToUnixPathA + IntPtr ret = Interop.Sys.LoadLibrary(libraryName); + if (ret == IntPtr.Zero) + { + string? message = Marshal.PtrToStringAnsi(Interop.Sys.GetLoadLibraryError()); + errorTracker.TrackErrorMessage(message); + } + + return ret; + } + + private static void FreeLib(IntPtr handle) + { + Debug.Assert(handle != IntPtr.Zero); + + Interop.Sys.FreeLibrary(handle); + } + + private static unsafe IntPtr GetSymbolOrNull(IntPtr handle, string symbolName) + { + return Interop.Sys.GetProcAddress(handle, symbolName); + } + + internal struct LoadLibErrorTracker + { + private string? _errorMessage; + + public void Throw(string libraryName) + { +#if TARGET_OSX + throw new DllNotFoundException(SR.Format(SR.DllNotFound_Mac, libraryName, _errorMessage)); +#else + throw new DllNotFoundException(SR.Format(SR.DllNotFound_Linux, libraryName, _errorMessage)); +#endif + } + + public void TrackErrorMessage(string? message) + { + _errorMessage = message; + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs new file mode 100644 index 00000000000000..9e2738a5497338 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs @@ -0,0 +1,103 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Text; + +namespace System.Runtime.InteropServices +{ + public static partial class NativeLibrary + { + private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadLibErrorTracker errorTracker) + { + IntPtr ret = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, flags); + if (ret != IntPtr.Zero) + { + return ret; + } + + int lastError = Marshal.GetLastWin32Error(); + if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER) + { + errorTracker.TrackErrorCode(lastError); + } + + return ret; + } + + private static void FreeLib(IntPtr handle) + { + Debug.Assert(handle != IntPtr.Zero); + + bool result = Interop.Kernel32.FreeLibrary(handle); + if (!result) + throw new InvalidOperationException(); + } + + private static unsafe IntPtr GetSymbolOrNull(IntPtr handle, string symbolName) + { + var symbolBytes = new byte[Encoding.UTF8.GetByteCount(symbolName) + 1]; + Encoding.UTF8.GetBytes(symbolName, symbolBytes); + fixed (byte* pSymbolBytes = symbolBytes) + { + return Interop.Kernel32.GetProcAddress(handle, pSymbolBytes); + } + } + + // Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places + // if earlier loads fail and those later loads obliterate error codes. + // + // This tracker object will keep track of the error code in accordance to priority: + // + // low-priority: unknown error code (should never happen) + // medium-priority: dll not found + // high-priority: dll found but error during loading + // + // We will overwrite the previous load's error code only if the new error code is higher priority. + internal struct LoadLibErrorTracker + { + private int _errorCode; + private int _priority; + + private const int PriorityNotFound = 10; + private const int PriorityAccessDenied = 20; + private const int PriorityCouldNotLoad = 99999; + + public void Throw(string libraryName) + { + if (_errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT) + { + throw new BadImageFormatException(); + } + + string message = Interop.Kernel32.GetMessage(_errorCode); + throw new DllNotFoundException(SR.Format(SR.DllNotFound_Windows, libraryName, message)); + } + + public void TrackErrorCode(int errorCode) + { + int priority = errorCode switch + { + Interop.Errors.ERROR_FILE_NOT_FOUND or + Interop.Errors.ERROR_PATH_NOT_FOUND or + Interop.Errors.ERROR_MOD_NOT_FOUND or + Interop.Errors.ERROR_DLL_NOT_FOUND => PriorityNotFound, + + // If we can't access a location, we can't know if the dll's there or if it's good. + // Still, this is probably more unusual (and thus of more interest) than a dll-not-found + // so give it an intermediate priority. + Interop.Errors.ERROR_ACCESS_DENIED => PriorityAccessDenied, + + // Assume all others are "dll found but couldn't load." + _ => PriorityCouldNotLoad, + }; + + if (priority > _priority) + { + _errorCode = errorCode; + _priority = priority; + } + } + } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs index 7535dd0d7f929f..c98ea2c93e4a23 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs @@ -1,10 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.IO; using System.Reflection; -using System.Text; using LibraryNameVariation = System.Runtime.Loader.LibraryNameVariation; @@ -116,141 +114,13 @@ private static IntPtr LoadFromPath(string libraryName, bool throwOnError) return ret; } - private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadLibErrorTracker errorTracker) - { -#if TARGET_WINDOWS - IntPtr ret = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, flags); - if (ret != IntPtr.Zero) - { - return ret; - } - - int lastError = Marshal.GetLastWin32Error(); - if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER) - { - errorTracker.TrackErrorCode(lastError); - } - - return ret; -#else - // TODO: FileDosToUnixPathA - IntPtr ret = Interop.Sys.LoadLibrary(libraryName); - if (ret == IntPtr.Zero) - { - string? message = Marshal.PtrToStringAnsi(Interop.Sys.GetLoadLibraryError()); - errorTracker.TrackErrorMessage(message); - } - - return ret; -#endif - } - - private static void FreeLib(IntPtr handle) - { - Debug.Assert(handle != IntPtr.Zero); - -#if !TARGET_UNIX - bool result = Interop.Kernel32.FreeLibrary(handle); - if (!result) - throw new InvalidOperationException(); -#else - Interop.Sys.FreeLibrary(handle); -#endif - } - private static unsafe IntPtr GetSymbol(IntPtr handle, string symbolName, bool throwOnError) { - IntPtr ret; -#if !TARGET_UNIX - var symbolBytes = new byte[Encoding.UTF8.GetByteCount(symbolName) + 1]; - Encoding.UTF8.GetBytes(symbolName, symbolBytes); - fixed (byte* pSymbolBytes = symbolBytes) - { - ret = Interop.Kernel32.GetProcAddress(handle, pSymbolBytes); - } -#else - ret = Interop.Sys.GetProcAddress(handle, symbolName); -#endif + IntPtr ret = GetSymbolOrNull(handle, symbolName); if (throwOnError && ret == IntPtr.Zero) throw new EntryPointNotFoundException(SR.Format(SR.Arg_EntryPointNotFoundExceptionParameterizedNoLibrary, symbolName)); return ret; } - - // Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places - // if earlier loads fail and those later loads obliterate error codes. - // - // This tracker object will keep track of the error code in accordance to priority: - // - // low-priority: unknown error code (should never happen) - // medium-priority: dll not found - // high-priority: dll found but error during loading - // - // We will overwrite the previous load's error code only if the new error code is higher priority. - internal struct LoadLibErrorTracker - { -#if TARGET_WINDOWS - private int _errorCode; - private int _priority; - - private const int PriorityNotFound = 10; - private const int PriorityAccessDenied = 20; - private const int PriorityCouldNotLoad = 99999; - - public void Throw(string libraryName) - { - if (_errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT) - { - throw new BadImageFormatException(); - } - - string message = Interop.Kernel32.GetMessage(_errorCode); - throw new DllNotFoundException(SR.Format(SR.DllNotFound_Windows, libraryName, message)); - } - - public void TrackErrorCode(int errorCode) - { - int priority = errorCode switch - { - Interop.Errors.ERROR_FILE_NOT_FOUND or - Interop.Errors.ERROR_PATH_NOT_FOUND or - Interop.Errors.ERROR_MOD_NOT_FOUND or - Interop.Errors.ERROR_DLL_NOT_FOUND => PriorityNotFound, - - // If we can't access a location, we can't know if the dll's there or if it's good. - // Still, this is probably more unusual (and thus of more interest) than a dll-not-found - // so give it an intermediate priority. - Interop.Errors.ERROR_ACCESS_DENIED => PriorityAccessDenied, - - // Assume all others are "dll found but couldn't load." - _ => PriorityCouldNotLoad, - }; - - if (priority > _priority) - { - _errorCode = errorCode; - _priority = priority; - } - } -#else - // On Unix systems we don't have detailed programatic information on why load failed - // so there's no priorities. - private string? _errorMessage; - - public void Throw(string libraryName) - { -#if TARGET_OSX - throw new DllNotFoundException(SR.Format(SR.DllNotFound_Mac, libraryName, _errorMessage)); -#else - throw new DllNotFoundException(SR.Format(SR.DllNotFound_Linux, libraryName, _errorMessage)); -#endif - } - - public void TrackErrorMessage(string? message) - { - _errorMessage = message; - } -#endif - } } } From b01ef037fe6b246b32e7efe18c3e141b4c6efa34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Fri, 27 May 2022 17:15:54 +0900 Subject: [PATCH 3/4] FileDosToUnixPathA --- .../Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs index 2930dd23a4765a..6a60bae4f4727a 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs @@ -9,7 +9,9 @@ public static partial class NativeLibrary { private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadLibErrorTracker errorTracker) { - // TODO: FileDosToUnixPathA + // do the Dos/Unix conversion + libraryName = libraryName.Replace('\\', '/'); + IntPtr ret = Interop.Sys.LoadLibrary(libraryName); if (ret == IntPtr.Zero) { From 491a8830c4762445bd967cdbe9a24e15ab7d0d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Sat, 28 May 2022 09:46:16 +0900 Subject: [PATCH 4/4] Feedback --- .../NativeLibrary.NativeAot.Unix.cs | 2 ++ .../NativeLibrary.NativeAot.Windows.cs | 36 +++++++++++-------- .../NativeLibrary.NativeAot.cs | 4 +-- .../Windows/Kernel32/Interop.DynamicLoad.cs | 3 ++ 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs index 6a60bae4f4727a..572a7f28294407 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Unix.cs @@ -7,6 +7,8 @@ namespace System.Runtime.InteropServices { public static partial class NativeLibrary { + private const int LoadWithAlteredSearchPathFlag = 0; + private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadLibErrorTracker errorTracker) { // do the Dos/Unix conversion diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs index 9e2738a5497338..6f5587fa0c33d2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.Windows.cs @@ -2,27 +2,40 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Text; namespace System.Runtime.InteropServices { public static partial class NativeLibrary { + private const int LoadWithAlteredSearchPathFlag = 0x8; /* LOAD_WITH_ALTERED_SEARCH_PATH */ + private static IntPtr LoadLibraryHelper(string libraryName, int flags, ref LoadLibErrorTracker errorTracker) { - IntPtr ret = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, flags); - if (ret != IntPtr.Zero) + IntPtr hmod; + + if (((uint)flags & 0xFFFFFF00) != 0) { - return ret; + hmod = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, (int)((uint)flags & 0xFFFFFF00)); + if (hmod != IntPtr.Zero) + { + return hmod; + } + + int lastError = Marshal.GetLastWin32Error(); + if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER) + { + errorTracker.TrackErrorCode(lastError); + return hmod; + } } - int lastError = Marshal.GetLastWin32Error(); - if (lastError != Interop.Errors.ERROR_INVALID_PARAMETER) + hmod = Interop.Kernel32.LoadLibraryEx(libraryName, IntPtr.Zero, flags & 0xFF); + if (hmod == IntPtr.Zero) { - errorTracker.TrackErrorCode(lastError); + errorTracker.TrackErrorCode(Marshal.GetLastWin32Error()); } - return ret; + return hmod; } private static void FreeLib(IntPtr handle) @@ -36,12 +49,7 @@ private static void FreeLib(IntPtr handle) private static unsafe IntPtr GetSymbolOrNull(IntPtr handle, string symbolName) { - var symbolBytes = new byte[Encoding.UTF8.GetByteCount(symbolName) + 1]; - Encoding.UTF8.GetBytes(symbolName, symbolBytes); - fixed (byte* pSymbolBytes = symbolBytes) - { - return Interop.Kernel32.GetProcAddress(handle, pSymbolBytes); - } + return Interop.Kernel32.GetProcAddress(handle, symbolName); } // Preserving good error info from DllImport-driven LoadLibrary is tricky because we keep loading from different places diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs index c98ea2c93e4a23..3708b256808e88 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs @@ -58,7 +58,7 @@ internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssembl { IntPtr ret; - int loadWithAlteredPathFlags = 0; + int loadWithAlteredPathFlags = LoadWithAlteredSearchPathFlag; bool libNameIsRelativePath = !Path.IsPathFullyQualified(libraryName); // P/Invokes are often declared with variations on the actual library name. @@ -105,7 +105,7 @@ internal static IntPtr LoadBySearch(Assembly callingAssembly, bool searchAssembl private static IntPtr LoadFromPath(string libraryName, bool throwOnError) { LoadLibErrorTracker errorTracker = default; - IntPtr ret = LoadLibraryHelper(libraryName, 0, ref errorTracker); + IntPtr ret = LoadLibraryHelper(libraryName, LoadWithAlteredSearchPathFlag, ref errorTracker); if (throwOnError && ret == IntPtr.Zero) { errorTracker.Throw(libraryName); diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DynamicLoad.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DynamicLoad.cs index e6ce9d5730437f..e940c7769c3119 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DynamicLoad.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.DynamicLoad.cs @@ -12,5 +12,8 @@ internal static unsafe partial class Kernel32 { [LibraryImport(Libraries.Kernel32)] internal static partial IntPtr GetProcAddress(IntPtr hModule, byte* lpProcName); + + [LibraryImport(Libraries.Kernel32, StringMarshalling = StringMarshalling.Utf8)] + internal static partial IntPtr GetProcAddress(IntPtr hModule, string lpProcName); } }