From e4e799ca477653c8197a3e1bf57a085487c5e89a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 10 Aug 2021 14:54:35 -0700 Subject: [PATCH 01/35] Bring in some more QCall infrastructure that matches the CoreCLR design to make it easier to share QCalls and to hook them up in Mono. --- src/mono/mono/metadata/native-library-qcall.c | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/native-library-qcall.c b/src/mono/mono/metadata/native-library-qcall.c index 232b2df120b793..e513368aa18f93 100644 --- a/src/mono/mono/metadata/native-library-qcall.c +++ b/src/mono/mono/metadata/native-library-qcall.c @@ -16,15 +16,33 @@ enum { func_flag_end_of_array = 0x01, func_flag_has_signature = 0x02, func_flag_unreferenced = 0x04, // Suppress unused fcall check - func_flag_qcall = 0x08, // QCall - mscorlib.dll to mscorwks.dll transition implemented as PInvoke + func_flag_qcall = 0x08, // QCall - System.Private.CoreLib.dll to native runtime transition implemented as PInvoke }; +#ifndef DISABLE_QCALLS + #define FCFuncStart(name) static const MonoQCallFunc name[] = { + #define FCFuncEnd() { func_flag_end_of_array, NULL, NULL } }; + #define QCFuncElement(name,impl) { func_flag_qcall, (void*)(impl), name }, + #define FCClassElement(name, namespace, funcs) + #include "mono/metadata/qcall-def.h" + #undef FCClassElement + #undef QCFuncElement + #undef FCFuncEnd + #undef FCFuncStart +#endif + static const MonoQCallDef c_qcalls[] = { #ifndef DISABLE_QCALLS + #define FCFuncStart(name) + #define FCFuncEnd() + #define QCFuncElement(name,impl) #define FCClassElement(name,namespace,funcs) {name, namespace, funcs}, #include "mono/metadata/qcall-def.h" #undef FCClassElement + #undef QCFuncElement + #undef FCFuncEnd + #undef FCFuncStart #endif }; From 5f16ec8d743e082e4bcca9a631c864b7ce503156 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 10 Aug 2021 14:55:53 -0700 Subject: [PATCH 02/35] Expose method from CoreRun for use in testing for desktop scenarios. --- src/coreclr/hosts/corerun/corerun.cpp | 3 --- src/coreclr/hosts/corerun/corerun.hpp | 8 +++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index a5d22c5411e034..a0b7e5f7de68ef 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -555,8 +555,6 @@ int MAIN(const int argc, const char_t* argv[]) return exit_code; } -#ifdef TARGET_WINDOWS -// Used by CoreShim to determine running CoreCLR details. extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId) { assert(clrInstance != nullptr && appDomainId != nullptr); @@ -564,7 +562,6 @@ extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void** clr *appDomainId = CurrentAppDomainId; return S_OK; } -#endif // TARGET_WINDOWS // // Self testing for corerun. diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp index 35116db198bb37..61d12cdc644441 100644 --- a/src/coreclr/hosts/corerun/corerun.hpp +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -48,6 +48,7 @@ namespace pal #ifdef TARGET_WINDOWS #include +#define DLL_EXPORT __declspec(dllexport) #define MAIN __cdecl wmain #define W(str) L ## str @@ -307,6 +308,11 @@ class platform_specific_actions final #include #include +#if __GNUC__ >= 4 +#define DLL_EXPORT __attribute__ ((visibility ("default"))) +#else +#define DLL_EXPORT +#endif #define MAIN main #define W(str) str #define FAILED(result) (result < 0) @@ -573,7 +579,7 @@ namespace pal pal::ensure_trailing_delimiter(coreclr_path); coreclr_path.append(pal::coreclr_lib); coreclr_path.append(pal::nativelib_ext); - + hMod = (pal::mod_t)dlopen(coreclr_path.c_str(), RTLD_NOW | RTLD_LOCAL); if (hMod == nullptr) { From e0542ee6b2bd4f1ba8adb532d1eab70cb61d244a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 10 Aug 2021 14:56:43 -0700 Subject: [PATCH 03/35] Refactor out the implementation of the lookup for "__Internal" so it can be used as a QCall entrypoint in Mono. --- src/mono/mono/metadata/native-library.c | 28 ++++++++++++++----------- src/mono/mono/metadata/native-library.h | 2 ++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index dd67477d092d4c..ae663a0816b169 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -756,6 +756,21 @@ netcore_check_alc_cache (MonoAssemblyLoadContext *alc, const char *scope) return result; } +MonoDl* +netcore_lookup_self_native_handle() +{ + char *error_msg = NULL; + if (!internal_module) + internal_module = mono_dl_open_self (&error_msg); + + if (!internal_module) { + mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg); + g_free (error_msg); + } + mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via __Internal."); + return internal_module; +} + static MonoDl * netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, const char *scope, guint32 flags) { @@ -770,18 +785,7 @@ netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, c // We allow a special name to dlopen from the running process namespace, which is not present in CoreCLR if (strcmp (scope, "__Internal") == 0) { - if (!internal_module) - internal_module = mono_dl_open_self (&error_msg); - module = internal_module; - - if (!module) { - mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_DLLIMPORT, "DllImport error loading library '__Internal': '%s'.", error_msg); - g_free (error_msg); - } - - mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Native library found via __Internal: '%s'.", scope); - - return module; + return netcore_lookup_self_native_handle(); } /* diff --git a/src/mono/mono/metadata/native-library.h b/src/mono/mono/metadata/native-library.h index 4ce9ce70a8d7df..b47829a451cfda 100644 --- a/src/mono/mono/metadata/native-library.h +++ b/src/mono/mono/metadata/native-library.h @@ -48,4 +48,6 @@ typedef struct MonoQCallFunc { void mono_loader_install_pinvoke_override (PInvokeOverrideFn override_fn); +MonoDl* +netcore_lookup_self_native_handle(); #endif From 57643bca21d6059cb1396d86ce525df25e388476 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 10 Aug 2021 15:33:06 -0700 Subject: [PATCH 04/35] First pass implementing API on corelcr and mono. --- src/coreclr/vm/ecalllist.h | 1 + src/coreclr/vm/nativelibrarynative.cpp | 19 ++++++++++++++++++ src/coreclr/vm/nativelibrarynative.h | 2 +- .../Runtime/InteropServices/NativeLibrary.cs | 20 +++++++++++++++++++ .../ref/System.Runtime.InteropServices.cs | 1 + src/mono/mono/metadata/qcall-def.h | 6 +++++- .../NativeLibrary/API/NativeLibraryTests.cs | 20 +++++++++++++++++++ 7 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index c83248c00e2d0d..33be0f270b56d0 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -798,6 +798,7 @@ FCFuncStart(gInteropNativeLibraryFuncs) QCFuncElement("LoadByName", NativeLibraryNative::LoadByName) QCFuncElement("FreeLib", NativeLibraryNative::FreeLib) QCFuncElement("GetSymbol", NativeLibraryNative::GetSymbol) + QCFuncElement("GetEntryPointModuleHandleInternal", NativeLibraryNative::GetEntryPointModuleHandle) FCFuncEnd() FCFuncStart(gTypeLoadExceptionFuncs) diff --git a/src/coreclr/vm/nativelibrarynative.cpp b/src/coreclr/vm/nativelibrarynative.cpp index f2c26e0d468e0f..f8d9c9d20e5505 100644 --- a/src/coreclr/vm/nativelibrarynative.cpp +++ b/src/coreclr/vm/nativelibrarynative.cpp @@ -71,3 +71,22 @@ INT_PTR QCALLTYPE NativeLibraryNative::GetSymbol(INT_PTR handle, LPCWSTR symbolN return address; } +//static +INT_PTR QCALLTYPE NativeLibraryNative::GetEntryPointModuleHandle() +{ + QCALL_CONTRACT; + + INT_PTR handle = NULL; + + BEGIN_QCALL; + +#if TARGET_WINDOWS + handle = GetModuleHandle(NULL); +#else + handle = dlopen(NULL); +#endif + + END_QCALL; + + return handle; +} diff --git a/src/coreclr/vm/nativelibrarynative.h b/src/coreclr/vm/nativelibrarynative.h index e34ef68a74b397..e80d58d8a49407 100644 --- a/src/coreclr/vm/nativelibrarynative.h +++ b/src/coreclr/vm/nativelibrarynative.h @@ -19,7 +19,7 @@ class NativeLibraryNative BOOL throwOnError); static void QCALLTYPE FreeLib(INT_PTR handle); static INT_PTR QCALLTYPE GetSymbol(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError); - + static INT_PTR QCALLTYPE GetEntryPointModuleHandle(); }; #endif // __NATIVELIBRARYNATIVE_H__ diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs index 9f790cd9ac2eed..0cf4328e52171f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -256,5 +257,24 @@ internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly asse return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null); } + + [DllImport(RuntimeHelpers.QCall, SetLastError = true)] + private static extern IntPtr GetEntryPointModuleHandleInternal(); + + /// + /// Get a handle that can be used with or to resolve exports from the entry point module. + /// + /// The handle that can be used to resolve exports from the entry point module. + public static IntPtr GetEntryPointModuleHandle() + { + IntPtr result = GetEntryPointModuleHandleInternal(); + // I don't know when a failure case can occur here, but checking for it and throwing an exception + // if we encounter it. + if (result == IntPtr.Zero) + { + Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError()); + } + return result; + } } } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 0deb9b0e3aeba3..fc1300dea79032 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -753,6 +753,7 @@ public MarshalDirectiveException(string? message, System.Exception? inner) { } public static partial class NativeLibrary { public static void Free(System.IntPtr handle) { } + public static System.IntPtr GetEntryPointModuleHandle(); public static System.IntPtr GetExport(System.IntPtr handle, string name) { throw null; } public static System.IntPtr Load(string libraryPath) { throw null; } public static System.IntPtr Load(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath) { throw null; } diff --git a/src/mono/mono/metadata/qcall-def.h b/src/mono/mono/metadata/qcall-def.h index 7341edddef2929..2f1524ae717bde 100644 --- a/src/mono/mono/metadata/qcall-def.h +++ b/src/mono/mono/metadata/qcall-def.h @@ -12,4 +12,8 @@ * have to end with a func_flag_end_of_array (0x01) entry. **/ -FCClassElement("", "", NULL) +FCFuncStart(gInteropNativeLibraryFuncs) + QCFuncElement("GetEntryPointModuleHandleInternal", netcore_lookup_self_native_handle) +FCFuncEnd() + +FCClassElement("NativeLibary", "System.Runtime.InteropServices", gInteropNativeLibraryFuncs) diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 4742b2e0b5bdbb..c7a269e6736854 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -179,6 +179,11 @@ public static int Main() success &= EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure); NativeLibrary.Free(handle); + + // ----------------------------------------------- + // GetLibraryExport Tests + // ----------------------------------------------- + success &= EXPECT(GetEntryPointModuleHandle()); } catch (Exception e) { @@ -365,6 +370,21 @@ static TestResult TryGetLibraryExport(IntPtr handle, string name) }); } + static TestResult GetEntryPointModuleHandle() + { + CurrentTest = nameof(GetEntryPointModuleHandle); + return Run(() => { + IntPtr moduleHandle = NativeLibrary.GetEntryPointModuleHandle(); + // Get a well-known export name from corerun to validate that we have a valid handle. + bool success = NativeLibrary.TryGetExport(moduleHandle, "GetCurrentClrDetails", out address); + if (!success) + return TestResult.ReturnFailure; + if (address == IntPtr.Zero) + return TestResult.ReturnNull; + return TestResult.Success; + }); + } + [DllImport(NativeLibraryToLoad.Name)] static extern int RunExportedFunction(IntPtr address, int arg1, int arg2); } From 48431651f401409761a79673c8448eb0b62ffdd5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 17 Aug 2021 15:07:41 -0700 Subject: [PATCH 05/35] Fix build and test failures. --- src/coreclr/vm/nativelibrary.cpp | 42 +++++++++++++++++++ src/coreclr/vm/nativelibrary.h | 2 + src/coreclr/vm/nativelibrarynative.cpp | 10 ++--- .../ref/System.Runtime.InteropServices.cs | 2 +- .../NativeLibrary/API/NativeLibraryTests.cs | 2 +- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/coreclr/vm/nativelibrary.cpp b/src/coreclr/vm/nativelibrary.cpp index 8a894f2a115f04..4981ac472387b5 100644 --- a/src/coreclr/vm/nativelibrary.cpp +++ b/src/coreclr/vm/nativelibrary.cpp @@ -181,6 +181,28 @@ namespace return hmod; } + // Load the library directly and return the raw system handle + NATIVE_LIBRARY_HANDLE GetEntryPointModuleHandleHelper(LoadLibErrorTracker *pErrorTracker ) + { + STANDARD_VM_CONTRACT; + + NATIVE_LIBRARY_HANDLE hmod = NULL; + +#ifndef TARGET_UNIX + hmod = GetModuleHandleW(NULL); + +#else // !TARGET_UNIX + hmod = PAL_LoadLibraryDirect(NULL); +#endif // !TARGET_UNIX + + if (hmod == NULL) + { + pErrorTracker->TrackErrorCode(); + } + + return hmod; + } + // DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags. // There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute, // CoreCLR's LoadLibrary implementation uses the following defaults. @@ -907,3 +929,23 @@ NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryFromMethodDesc(NDirectMethodDesc RETURN hmod; } + +NATIVE_LIBRARY_HANDLE NativeLibrary::GetEntryPointModuleHandle() +{ + CONTRACT(NATIVE_LIBRARY_HANDLE) + { + STANDARD_VM_CHECK; + POSTCONDITION(RETVAL != NULL); + } + CONTRACT_END; + + LoadLibErrorTracker errorTracker; + NATIVE_LIBRARY_HANDLE hmod = GetEntryPointModuleHandleHelper(&errorTracker); + if (hmod == NULL) + { + StackSString ssLibName(SString::Utf8, "Entry Point Handle"); + errorTracker.Throw(ssLibName); + } + + RETURN hmod; +} diff --git a/src/coreclr/vm/nativelibrary.h b/src/coreclr/vm/nativelibrary.h index f05146862c285d..33639a1351eefc 100644 --- a/src/coreclr/vm/nativelibrary.h +++ b/src/coreclr/vm/nativelibrary.h @@ -17,6 +17,8 @@ class NativeLibrary static INT_PTR GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError); static NATIVE_LIBRARY_HANDLE LoadLibraryFromMethodDesc(NDirectMethodDesc *pMD); + + static NATIVE_LIBRARY_HANDLE GetEntryPointModuleHandle(); }; #endif // _NATIVELIBRARY_H_ diff --git a/src/coreclr/vm/nativelibrarynative.cpp b/src/coreclr/vm/nativelibrarynative.cpp index f8d9c9d20e5505..4386f877239141 100644 --- a/src/coreclr/vm/nativelibrarynative.cpp +++ b/src/coreclr/vm/nativelibrarynative.cpp @@ -76,17 +76,13 @@ INT_PTR QCALLTYPE NativeLibraryNative::GetEntryPointModuleHandle() { QCALL_CONTRACT; - INT_PTR handle = NULL; + NATIVE_LIBRARY_HANDLE handle = NULL; BEGIN_QCALL; -#if TARGET_WINDOWS - handle = GetModuleHandle(NULL); -#else - handle = dlopen(NULL); -#endif + handle = NativeLibrary::GetEntryPointModuleHandle(); END_QCALL; - return handle; + return reinterpret_cast(handle); } diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index fc1300dea79032..36ef3db4dad035 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -753,7 +753,7 @@ public MarshalDirectiveException(string? message, System.Exception? inner) { } public static partial class NativeLibrary { public static void Free(System.IntPtr handle) { } - public static System.IntPtr GetEntryPointModuleHandle(); + public static System.IntPtr GetEntryPointModuleHandle() { throw null; } public static System.IntPtr GetExport(System.IntPtr handle, string name) { throw null; } public static System.IntPtr Load(string libraryPath) { throw null; } public static System.IntPtr Load(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath) { throw null; } diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index c7a269e6736854..757abb1a8db84e 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -376,7 +376,7 @@ static TestResult GetEntryPointModuleHandle() return Run(() => { IntPtr moduleHandle = NativeLibrary.GetEntryPointModuleHandle(); // Get a well-known export name from corerun to validate that we have a valid handle. - bool success = NativeLibrary.TryGetExport(moduleHandle, "GetCurrentClrDetails", out address); + bool success = NativeLibrary.TryGetExport(moduleHandle, "GetCurrentClrDetails", out IntPtr address); if (!success) return TestResult.ReturnFailure; if (address == IntPtr.Zero) From 376cd454273cb4f5f7ada00828e2075073202ea6 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 11:32:23 -0700 Subject: [PATCH 06/35] Fix typos and do basic cleanup. --- src/coreclr/hosts/corerun/corerun.cpp | 2 +- src/coreclr/hosts/corerun/corerun.hpp | 1 + src/coreclr/vm/ecalllist.h | 2 +- .../src/System/Runtime/InteropServices/NativeLibrary.cs | 1 - src/mono/mono/metadata/qcall-def.h | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index a0b7e5f7de68ef..91bea2c87cf2a0 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -555,7 +555,7 @@ int MAIN(const int argc, const char_t* argv[]) return exit_code; } -extern "C" __declspec(dllexport) HRESULT __cdecl GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId) +extern "C" DLL_EXPORT HRESULT CDECL GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId) { assert(clrInstance != nullptr && appDomainId != nullptr); *clrInstance = CurrentClrInstance; diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp index 61d12cdc644441..abece3d29b8bfe 100644 --- a/src/coreclr/hosts/corerun/corerun.hpp +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -313,6 +313,7 @@ class platform_specific_actions final #else #define DLL_EXPORT #endif +#define CDECL #define MAIN main #define W(str) str #define FAILED(result) (result < 0) diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 33be0f270b56d0..abe2c4ae056a53 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -798,7 +798,7 @@ FCFuncStart(gInteropNativeLibraryFuncs) QCFuncElement("LoadByName", NativeLibraryNative::LoadByName) QCFuncElement("FreeLib", NativeLibraryNative::FreeLib) QCFuncElement("GetSymbol", NativeLibraryNative::GetSymbol) - QCFuncElement("GetEntryPointModuleHandleInternal", NativeLibraryNative::GetEntryPointModuleHandle) + QCFuncElement("GetEntryPointModuleHandleInternal", NativeLibraryNative::GetEntryPointModuleHandle) FCFuncEnd() FCFuncStart(gTypeLoadExceptionFuncs) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs index 0cf4328e52171f..aba14a481d669f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; diff --git a/src/mono/mono/metadata/qcall-def.h b/src/mono/mono/metadata/qcall-def.h index 2f1524ae717bde..d99a06ebfec9c2 100644 --- a/src/mono/mono/metadata/qcall-def.h +++ b/src/mono/mono/metadata/qcall-def.h @@ -16,4 +16,4 @@ FCFuncStart(gInteropNativeLibraryFuncs) QCFuncElement("GetEntryPointModuleHandleInternal", netcore_lookup_self_native_handle) FCFuncEnd() -FCClassElement("NativeLibary", "System.Runtime.InteropServices", gInteropNativeLibraryFuncs) +FCClassElement("NativeLibrary", "System.Runtime.InteropServices", gInteropNativeLibraryFuncs) From d19ed4abde4eda8584f47fa68594fc94a0949d93 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 13:19:11 -0700 Subject: [PATCH 07/35] Enhance testing. --- .../NativeLibrary/API/NativeLibraryTests.cs | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 757abb1a8db84e..0a114ecaa101c3 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.InteropServices; @@ -181,9 +182,25 @@ public static int Main() NativeLibrary.Free(handle); // ----------------------------------------------- - // GetLibraryExport Tests + // GetEntryPointModuleHandle Tests // ----------------------------------------------- - success &= EXPECT(GetEntryPointModuleHandle()); + + // Mobile test runs aren't hosted by corerun, so we don't have a well-known export to test here + if (IsHostedByCoreRun()) + { + // Export from CoreRun host + success &= EXPECT(GetSymbolFromEntryPointModuleHandle("GetCurrentClrDetails")); + success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonExistentCoreRunExport"), TestResult.ReturnFailure); + } + + handle = NativeLibrary.Load(libName); + + // On non-Windows, the returned handle can load any global symbol, + // so try loading a symbol from the native test library. + success &= EXPECT(GetSymbolFromEntryPointModuleHandle(TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), OperatingSystem.IsWindows() ? TestResult.ReturnFailure : TestResult.Success); + success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonNativeSum"), TestResult.ReturnFailure); + + NativeLibrary.Free(handle); } catch (Exception e) { @@ -197,6 +214,11 @@ public static int Main() return (success) ? 100 : -100; } + static bool IsHostedByCoreRun() + { + return Process.GetCurrentProcess().MainModule.ModuleName is "corerun" or "corerun.exe"; + } + static bool EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success) { if (actualValue == expectedValue) @@ -370,13 +392,12 @@ static TestResult TryGetLibraryExport(IntPtr handle, string name) }); } - static TestResult GetEntryPointModuleHandle() + static TestResult GetSymbolFromEntryPointModuleHandle(string symbolToLoadFromHandle) { - CurrentTest = nameof(GetEntryPointModuleHandle); + CurrentTest = nameof(GetSymbolFromEntryPointModuleHandle); return Run(() => { IntPtr moduleHandle = NativeLibrary.GetEntryPointModuleHandle(); - // Get a well-known export name from corerun to validate that we have a valid handle. - bool success = NativeLibrary.TryGetExport(moduleHandle, "GetCurrentClrDetails", out IntPtr address); + bool success = NativeLibrary.TryGetExport(moduleHandle, symbolToLoadFromHandle, out IntPtr address); if (!success) return TestResult.ReturnFailure; if (address == IntPtr.Zero) From d50f07f0c2acefad5b08fdcc0a3aeb76eb967955 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 13:35:57 -0700 Subject: [PATCH 08/35] Update comment for mono QCalls so the comment explains how to define one without having to follow the pattern (makes it easier to remove this new QCall and move it to the libraries native portion) --- src/mono/mono/metadata/qcall-def.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/qcall-def.h b/src/mono/mono/metadata/qcall-def.h index d99a06ebfec9c2..a229e606cc52f6 100644 --- a/src/mono/mono/metadata/qcall-def.h +++ b/src/mono/mono/metadata/qcall-def.h @@ -8,8 +8,15 @@ * where symbol_name is an array of MonoQCallFunc. * * FCClassElements have to be sorted by name then namespace, - * but that the functions in each one can be in any order, but - * have to end with a func_flag_end_of_array (0x01) entry. + * but the functions in each one can be in any order, + * specified by the QCFuncElement macro as follows: + * FCFuncStart(class_symbol_name) + * QCFuncElement(method_name, symbol_name) + * FCFuncEnd() + * where class_symbol_name is the symbol for the corresponding + * class specified in FCClassElement, method_name is the name of the + * method on the managed side, and symbol_name is the name + * of the implementing method on the native side. **/ FCFuncStart(gInteropNativeLibraryFuncs) From d338682540d8c6fdfe4dc6e7f64d15e07cddf387 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 14:20:31 -0700 Subject: [PATCH 09/35] Fix corerun build on non-Windows. --- src/coreclr/hosts/corerun/corerun.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp index abece3d29b8bfe..20d51fcfc72204 100644 --- a/src/coreclr/hosts/corerun/corerun.hpp +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -316,8 +316,8 @@ class platform_specific_actions final #define CDECL #define MAIN main #define W(str) str -#define FAILED(result) (result < 0) - +#define S_OK 0 +#define FAILED(result) (result < S_OK) #if !HAVE_DIRENT_D_TYPE #define DT_UNKNOWN 0 #define DT_DIR 4 @@ -325,6 +325,8 @@ class platform_specific_actions final #define DT_LNK 10 #endif +typedef int HRESULT; + namespace pal { using char_t = char; From 3710af5237761df9d4963353cc4a997c62ea09b0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 14:21:39 -0700 Subject: [PATCH 10/35] Move API implementation to libraries. --- src/coreclr/vm/ecalllist.h | 1 - src/coreclr/vm/nativelibrary.cpp | 42 ------------------- src/coreclr/vm/nativelibrary.h | 2 - src/coreclr/vm/nativelibrarynative.cpp | 16 ------- src/coreclr/vm/nativelibrarynative.h | 1 - .../Interop/Unix/System.Native/Interop.Dl.cs | 15 +++++++ .../Native/Unix/System.Native/pal_dl.c | 22 ++++++++++ .../Native/Unix/System.Native/pal_dl.h | 8 ++++ .../System.Private.CoreLib.Shared.projitems | 8 +++- .../Runtime/InteropServices/NativeLibrary.cs | 12 +++--- src/mono/mono/metadata/native-library-qcall.c | 5 ++- src/mono/mono/metadata/qcall-def.h | 6 --- 12 files changed, 63 insertions(+), 75 deletions(-) create mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs create mode 100644 src/libraries/Native/Unix/System.Native/pal_dl.c create mode 100644 src/libraries/Native/Unix/System.Native/pal_dl.h diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index abe2c4ae056a53..c83248c00e2d0d 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -798,7 +798,6 @@ FCFuncStart(gInteropNativeLibraryFuncs) QCFuncElement("LoadByName", NativeLibraryNative::LoadByName) QCFuncElement("FreeLib", NativeLibraryNative::FreeLib) QCFuncElement("GetSymbol", NativeLibraryNative::GetSymbol) - QCFuncElement("GetEntryPointModuleHandleInternal", NativeLibraryNative::GetEntryPointModuleHandle) FCFuncEnd() FCFuncStart(gTypeLoadExceptionFuncs) diff --git a/src/coreclr/vm/nativelibrary.cpp b/src/coreclr/vm/nativelibrary.cpp index 4981ac472387b5..8a894f2a115f04 100644 --- a/src/coreclr/vm/nativelibrary.cpp +++ b/src/coreclr/vm/nativelibrary.cpp @@ -181,28 +181,6 @@ namespace return hmod; } - // Load the library directly and return the raw system handle - NATIVE_LIBRARY_HANDLE GetEntryPointModuleHandleHelper(LoadLibErrorTracker *pErrorTracker ) - { - STANDARD_VM_CONTRACT; - - NATIVE_LIBRARY_HANDLE hmod = NULL; - -#ifndef TARGET_UNIX - hmod = GetModuleHandleW(NULL); - -#else // !TARGET_UNIX - hmod = PAL_LoadLibraryDirect(NULL); -#endif // !TARGET_UNIX - - if (hmod == NULL) - { - pErrorTracker->TrackErrorCode(); - } - - return hmod; - } - // DllImportSearchPathFlags is a special enumeration, whose values are tied closely with LoadLibrary flags. // There is no "default" value DllImportSearchPathFlags. In the absence of DllImportSearchPath attribute, // CoreCLR's LoadLibrary implementation uses the following defaults. @@ -929,23 +907,3 @@ NATIVE_LIBRARY_HANDLE NativeLibrary::LoadLibraryFromMethodDesc(NDirectMethodDesc RETURN hmod; } - -NATIVE_LIBRARY_HANDLE NativeLibrary::GetEntryPointModuleHandle() -{ - CONTRACT(NATIVE_LIBRARY_HANDLE) - { - STANDARD_VM_CHECK; - POSTCONDITION(RETVAL != NULL); - } - CONTRACT_END; - - LoadLibErrorTracker errorTracker; - NATIVE_LIBRARY_HANDLE hmod = GetEntryPointModuleHandleHelper(&errorTracker); - if (hmod == NULL) - { - StackSString ssLibName(SString::Utf8, "Entry Point Handle"); - errorTracker.Throw(ssLibName); - } - - RETURN hmod; -} diff --git a/src/coreclr/vm/nativelibrary.h b/src/coreclr/vm/nativelibrary.h index 33639a1351eefc..f05146862c285d 100644 --- a/src/coreclr/vm/nativelibrary.h +++ b/src/coreclr/vm/nativelibrary.h @@ -17,8 +17,6 @@ class NativeLibrary static INT_PTR GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError); static NATIVE_LIBRARY_HANDLE LoadLibraryFromMethodDesc(NDirectMethodDesc *pMD); - - static NATIVE_LIBRARY_HANDLE GetEntryPointModuleHandle(); }; #endif // _NATIVELIBRARY_H_ diff --git a/src/coreclr/vm/nativelibrarynative.cpp b/src/coreclr/vm/nativelibrarynative.cpp index 4386f877239141..f8815539abac59 100644 --- a/src/coreclr/vm/nativelibrarynative.cpp +++ b/src/coreclr/vm/nativelibrarynative.cpp @@ -70,19 +70,3 @@ INT_PTR QCALLTYPE NativeLibraryNative::GetSymbol(INT_PTR handle, LPCWSTR symbolN return address; } - -//static -INT_PTR QCALLTYPE NativeLibraryNative::GetEntryPointModuleHandle() -{ - QCALL_CONTRACT; - - NATIVE_LIBRARY_HANDLE handle = NULL; - - BEGIN_QCALL; - - handle = NativeLibrary::GetEntryPointModuleHandle(); - - END_QCALL; - - return reinterpret_cast(handle); -} diff --git a/src/coreclr/vm/nativelibrarynative.h b/src/coreclr/vm/nativelibrarynative.h index e80d58d8a49407..569718179183d5 100644 --- a/src/coreclr/vm/nativelibrarynative.h +++ b/src/coreclr/vm/nativelibrarynative.h @@ -19,7 +19,6 @@ class NativeLibraryNative BOOL throwOnError); static void QCALLTYPE FreeLib(INT_PTR handle); static INT_PTR QCALLTYPE GetSymbol(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError); - static INT_PTR QCALLTYPE GetEntryPointModuleHandle(); }; #endif // __NATIVELIBRARYNATIVE_H__ diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs new file mode 100644 index 00000000000000..607cb623eeb46d --- /dev/null +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class Sys + { + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)] + internal static extern IntPtr GetDefaultSearchOrderPseudoHandle(); + } +} diff --git a/src/libraries/Native/Unix/System.Native/pal_dl.c b/src/libraries/Native/Unix/System.Native/pal_dl.c new file mode 100644 index 00000000000000..e2e3a8e4e28ee4 --- /dev/null +++ b/src/libraries/Native/Unix/System.Native/pal_dl.c @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include "pal_dl.h" +#include "dlfcn.h" + +#ifdef TARGET_ANDROID +void* SystemNative_GetDefaultSearchOrderPseudoHandle() +{ + return (void*)RTLD_DEFAULT; +} +#else +void* g_defaultSearchOrderPseudoHandle = NULL; +void* SystemNative_GetDefaultSearchOrderPseudoHandle() +{ + if (g_defaultSearchOrderPseudoHandle == NULL) + { + g_defaultSearchOrderPseudoHandle = dlopen(NULL, RTLD_LAZY); + } + return g_defaultSearchOrderPseudoHandle; +} +#endif diff --git a/src/libraries/Native/Unix/System.Native/pal_dl.h b/src/libraries/Native/Unix/System.Native/pal_dl.h new file mode 100644 index 00000000000000..07854304d6fbca --- /dev/null +++ b/src/libraries/Native/Unix/System.Native/pal_dl.h @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma once + +#include "pal_compiler.h" + +PALEXPORT void* SystemNative_GetDefaultSearchOrderPseudoHandle(); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index df824ab16bab37..a4c3e56f3b7185 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1792,6 +1792,9 @@ Common\Interop\Windows\User32\Interop.USEROBJECTFLAGS.cs + + Common\Interop\Windows\Kernel32\Interop.GetModuleHandle.cs + Common\System\IO\FileSystem.Attributes.Windows.cs @@ -1934,6 +1937,9 @@ Common\Interop\Unix\System.Native\Interop.CopyFile.cs + + Common\Interop\Unix\System.Native\Interop.Dl.cs + Common\Interop\Unix\System.Native\Interop.ErrNo.cs @@ -2326,4 +2332,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs index aba14a481d669f..8270afd514dad0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs @@ -257,21 +257,23 @@ internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly asse return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null); } - [DllImport(RuntimeHelpers.QCall, SetLastError = true)] - private static extern IntPtr GetEntryPointModuleHandleInternal(); - /// /// Get a handle that can be used with or to resolve exports from the entry point module. /// /// The handle that can be used to resolve exports from the entry point module. public static IntPtr GetEntryPointModuleHandle() { - IntPtr result = GetEntryPointModuleHandleInternal(); + IntPtr result = IntPtr.Zero; +#if TARGET_WINDOWS + result = Interop.Kernel32.GetModuleHandle(null); +#else + result = Interop.Sys.GetDefaultSearchOrderPseudoHandle(); +#endif // I don't know when a failure case can occur here, but checking for it and throwing an exception // if we encounter it. if (result == IntPtr.Zero) { - Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError()); + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } return result; } diff --git a/src/mono/mono/metadata/native-library-qcall.c b/src/mono/mono/metadata/native-library-qcall.c index e513368aa18f93..fe471167694ed3 100644 --- a/src/mono/mono/metadata/native-library-qcall.c +++ b/src/mono/mono/metadata/native-library-qcall.c @@ -44,9 +44,12 @@ static const MonoQCallDef c_qcalls[] = #undef FCFuncEnd #undef FCFuncStart #endif + { NULL, NULL, NULL } }; -const int c_nECClasses = sizeof (c_qcalls) / sizeof (c_qcalls[0]); +// Number of actual entries in c_qcalls. +// Subtract one to ignore the all-NULL entry at the end. +const int c_nECClasses = (sizeof (c_qcalls) / sizeof (c_qcalls[0])) - 1; static gboolean is_end_of_array (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_end_of_array); } static gboolean has_signature (MonoQCallFunc *func) { return !!((int)func->flags & func_flag_has_signature); } diff --git a/src/mono/mono/metadata/qcall-def.h b/src/mono/mono/metadata/qcall-def.h index a229e606cc52f6..67e5bc45c8485a 100644 --- a/src/mono/mono/metadata/qcall-def.h +++ b/src/mono/mono/metadata/qcall-def.h @@ -18,9 +18,3 @@ * method on the managed side, and symbol_name is the name * of the implementing method on the native side. **/ - -FCFuncStart(gInteropNativeLibraryFuncs) - QCFuncElement("GetEntryPointModuleHandleInternal", netcore_lookup_self_native_handle) -FCFuncEnd() - -FCClassElement("NativeLibrary", "System.Runtime.InteropServices", gInteropNativeLibraryFuncs) From 93fc783806839fb4b35968d6d6a5681f67d4d811 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 15:50:03 -0700 Subject: [PATCH 11/35] Make netcore_lookup_self_native_handle static so it doesn't require a prototype (it's no longer exposed outside native-library.c since it's no longer used in a QCall) --- src/mono/mono/metadata/native-library.c | 2 +- src/mono/mono/metadata/native-library.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index ae663a0816b169..c96494488e36c9 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -756,7 +756,7 @@ netcore_check_alc_cache (MonoAssemblyLoadContext *alc, const char *scope) return result; } -MonoDl* +static MonoDl* netcore_lookup_self_native_handle() { char *error_msg = NULL; diff --git a/src/mono/mono/metadata/native-library.h b/src/mono/mono/metadata/native-library.h index b47829a451cfda..4ce9ce70a8d7df 100644 --- a/src/mono/mono/metadata/native-library.h +++ b/src/mono/mono/metadata/native-library.h @@ -48,6 +48,4 @@ typedef struct MonoQCallFunc { void mono_loader_install_pinvoke_override (PInvokeOverrideFn override_fn); -MonoDl* -netcore_lookup_self_native_handle(); #endif From 58554e0ca66ffb3faa854e1e060f79987cc86b7e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 15:50:54 -0700 Subject: [PATCH 12/35] Compile pal_dl.c in the System.Native shim --- src/libraries/Native/Unix/System.Native/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index a28952750b3ef9..8c60bbc9ac3f04 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -11,6 +11,7 @@ endif () include_directories("${CLR_SRC_NATIVE_DIR}/common") set(NATIVE_SOURCES + pal_dl.c pal_errno.c pal_interfaceaddresses.c pal_io.c From 952903e20096f5d8a02c46238e1bfb8984812786 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 16:12:06 -0700 Subject: [PATCH 13/35] Add `void` to make the C compiler happy. --- src/libraries/Native/Unix/System.Native/pal_dl.c | 1 + src/libraries/Native/Unix/System.Native/pal_dl.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_dl.c b/src/libraries/Native/Unix/System.Native/pal_dl.c index e2e3a8e4e28ee4..b9b7532a21171e 100644 --- a/src/libraries/Native/Unix/System.Native/pal_dl.c +++ b/src/libraries/Native/Unix/System.Native/pal_dl.c @@ -3,6 +3,7 @@ #include "pal_dl.h" #include "dlfcn.h" +#include #ifdef TARGET_ANDROID void* SystemNative_GetDefaultSearchOrderPseudoHandle() diff --git a/src/libraries/Native/Unix/System.Native/pal_dl.h b/src/libraries/Native/Unix/System.Native/pal_dl.h index 07854304d6fbca..a8b3c644ac09e1 100644 --- a/src/libraries/Native/Unix/System.Native/pal_dl.h +++ b/src/libraries/Native/Unix/System.Native/pal_dl.h @@ -5,4 +5,4 @@ #include "pal_compiler.h" -PALEXPORT void* SystemNative_GetDefaultSearchOrderPseudoHandle(); +PALEXPORT void* SystemNative_GetDefaultSearchOrderPseudoHandle(void); From 90871363966ce416a9abee1786440e43aafad040 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 16:42:15 -0700 Subject: [PATCH 14/35] Make the cached local static --- src/libraries/Native/Unix/System.Native/pal_dl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_dl.c b/src/libraries/Native/Unix/System.Native/pal_dl.c index b9b7532a21171e..df8667aa93c09d 100644 --- a/src/libraries/Native/Unix/System.Native/pal_dl.c +++ b/src/libraries/Native/Unix/System.Native/pal_dl.c @@ -11,7 +11,7 @@ void* SystemNative_GetDefaultSearchOrderPseudoHandle() return (void*)RTLD_DEFAULT; } #else -void* g_defaultSearchOrderPseudoHandle = NULL; +static void* g_defaultSearchOrderPseudoHandle = NULL; void* SystemNative_GetDefaultSearchOrderPseudoHandle() { if (g_defaultSearchOrderPseudoHandle == NULL) From 071b11927f4d795acf59aceb49c59e934155b51d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 18 Aug 2021 16:52:39 -0700 Subject: [PATCH 15/35] Add to entrypoints.c --- src/libraries/Native/Unix/System.Native/entrypoints.c | 2 ++ src/libraries/Native/Unix/System.Native/pal_dl.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index c8b678ec08340b..34ff77be87c926 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -7,6 +7,7 @@ #include "pal_autoreleasepool.h" #include "pal_console.h" #include "pal_datetime.h" +#include "pal_dl.h" #include "pal_errno.h" #include "pal_interfaceaddresses.h" #include "pal_io.h" @@ -257,6 +258,7 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_SetPosixSignalHandler) DllImportEntry(SystemNative_GetPlatformSignalNumber) DllImportEntry(SystemNative_GetGroups) + DllImportEntry(SystemNative_GetDefaultSearchOrderPseudoHandle) }; EXTERN_C const void* SystemResolveDllImport(const char* name); diff --git a/src/libraries/Native/Unix/System.Native/pal_dl.c b/src/libraries/Native/Unix/System.Native/pal_dl.c index df8667aa93c09d..6052f8d48bb186 100644 --- a/src/libraries/Native/Unix/System.Native/pal_dl.c +++ b/src/libraries/Native/Unix/System.Native/pal_dl.c @@ -6,13 +6,13 @@ #include #ifdef TARGET_ANDROID -void* SystemNative_GetDefaultSearchOrderPseudoHandle() +void* SystemNative_GetDefaultSearchOrderPseudoHandle(void) { return (void*)RTLD_DEFAULT; } #else static void* g_defaultSearchOrderPseudoHandle = NULL; -void* SystemNative_GetDefaultSearchOrderPseudoHandle() +void* SystemNative_GetDefaultSearchOrderPseudoHandle(void) { if (g_defaultSearchOrderPseudoHandle == NULL) { From af3223107081488bf645bd798cc8b98e5073e5f2 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 19 Aug 2021 10:05:56 -0700 Subject: [PATCH 16/35] Remove unused variable. --- src/mono/mono/metadata/native-library.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index c96494488e36c9..fd9e5fb8ee2918 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -777,7 +777,6 @@ netcore_lookup_native_library (MonoAssemblyLoadContext *alc, MonoImage *image, c MonoDl *module = NULL; MonoDl *cached; MonoAssembly *assembly = mono_image_get_assembly (image); - char *error_msg = NULL; MONO_REQ_GC_UNSAFE_MODE; From d71bc7be72e31da75f37f1edf364931aba04e005 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 19 Aug 2021 14:29:10 -0700 Subject: [PATCH 17/35] Set the right flags to enable loading symbols from corerun with the new API on non-Windows. --- src/coreclr/hosts/corerun/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index da545877ca3180..c20fe514e8d42b 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -9,6 +9,9 @@ else(CLR_CMAKE_HOST_WIN32) include(configure.cmake) endif(CLR_CMAKE_HOST_WIN32) +#Required to expose symbols for global symbol discovery. +set(CLR_CMAKE_KEEP_NATIVE_SYMBOLS TRUE) + add_executable_clr(corerun corerun.cpp native.rc @@ -25,6 +28,8 @@ if(CLR_CMAKE_HOST_WIN32) ) else(CLR_CMAKE_HOST_WIN32) target_link_libraries(corerun ${CMAKE_DL_LIBS}) + # Required to expose symbols for global symbol discovery + target_link_libraries(corerun -rdynamic) # Android implements pthread natively if(NOT CLR_CMAKE_TARGET_ANDROID) From 4db27cb51434defd2de7f5ce107069a635b5e81f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 19 Aug 2021 14:48:45 -0700 Subject: [PATCH 18/35] Add tests that actually pass for the global symbol lookup. --- .../NativeLibrary/API/NativeLibraryTests.cs | 22 ++++++++++++++++--- .../NativeLibraryToLoad/CMakeLists.txt | 11 +++++----- .../GloballyLoadedNativeLibrary.cpp | 9 ++++++++ .../NativeLibraryToLoad/NativeLibrary.cpp | 12 ++++++++++ 4 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GloballyLoadedNativeLibrary.cpp diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 0a114ecaa101c3..2eea0c59fda22f 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -195,12 +195,25 @@ public static int Main() handle = NativeLibrary.Load(libName); - // On non-Windows, the returned handle can load any global symbol, - // so try loading a symbol from the native test library. - success &= EXPECT(GetSymbolFromEntryPointModuleHandle(TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), OperatingSystem.IsWindows() ? TestResult.ReturnFailure : TestResult.Success); + // NativeLibrary does not load symbols globally, so we shouldn't be able to discover symbols from libaries loaded + // with NativeLibary.Load. + success &= EXPECT(GetSymbolFromEntryPointModuleHandle(TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), TestResult.ReturnFailure); success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonNativeSum"), TestResult.ReturnFailure); NativeLibrary.Free(handle); + + if (!OperatingSystem.IsWindows()) + { + // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. + // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code + // with the right flags to test the scenario. + handle = LoadLibraryGlobally(Path.Combine(testBinDir, "libGloballyLoadedNativeLibrary.so")); + + success &= EXPECT(GetSymbolFromEntryPointModuleHandle(TestLibrary.Utilities.IsX86 ? "_NativeMultiply@8" : "NativeMultiply")); + success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonNativeMultiply"), TestResult.ReturnFailure); + + NativeLibrary.Free(handle); + } } catch (Exception e) { @@ -408,4 +421,7 @@ static TestResult GetSymbolFromEntryPointModuleHandle(string symbolToLoadFromHan [DllImport(NativeLibraryToLoad.Name)] static extern int RunExportedFunction(IntPtr address, int arg1, int arg2); + + [DllImport(NativeLibraryToLoad.Name)] + static extern IntPtr LoadLibraryGlobally(string name); } diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt index 8d9c552fac275c..afa8f71f59d069 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt @@ -1,10 +1,11 @@ project (NativeLibrary) include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") -set(SOURCES NativeLibrary.cpp) +set(SOURCES ) # add the shared library -add_library (NativeLibrary SHARED ${SOURCES}) -target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL}) +add_library (NativeLibrary SHARED NativeLibrary.cpp) +target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL} dl) -# add the install targets -install (TARGETS NativeLibrary DESTINATION bin) +# add the shared library +add_library (GloballyLoadedNativeLibrary SHARED GloballyLoadedNativeLibrary.cpp) +target_link_libraries(GloballyLoadedNativeLibrary ${LINK_LIBRARIES_ADDITIONAL}) diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GloballyLoadedNativeLibrary.cpp b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GloballyLoadedNativeLibrary.cpp new file mode 100644 index 00000000000000..0b8a92491002ca --- /dev/null +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GloballyLoadedNativeLibrary.cpp @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include +#include + +extern "C" DLL_EXPORT int NativeMultiply(int a, int b) +{ + return a * b; +} diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp index 6b556c04a93474..e0e5e3bc9f1c10 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. #include #include +#ifndef TARGET_WINDOWS +#include +#endif extern "C" DLL_EXPORT int NativeSum(int a, int b) { @@ -15,3 +18,12 @@ extern "C" DLL_EXPORT int RunExportedFunction(void *function, int arg1, int arg2 } +extern "C" DLL_EXPORT void* LoadLibraryGlobally(const char* name) +{ +#ifdef TARGET_WINDOWS + // Windows doesn't support global symbol loading. + return NULL; +#else + return dlopen(name, RTLD_GLOBAL | RTLD_LAZY); +#endif +} \ No newline at end of file From 9c41825f2e03fe741a947c94eda9a09e9d3a259a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 23 Aug 2021 10:43:04 -0700 Subject: [PATCH 19/35] Rename API based on offline conversation. --- .../src/System/Runtime/InteropServices/NativeLibrary.cs | 2 +- .../ref/System.Runtime.InteropServices.cs | 2 +- src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs index 8270afd514dad0..2875eb7fb3792f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs @@ -261,7 +261,7 @@ internal static IntPtr LoadLibraryCallbackStub(string libraryName, Assembly asse /// Get a handle that can be used with or to resolve exports from the entry point module. /// /// The handle that can be used to resolve exports from the entry point module. - public static IntPtr GetEntryPointModuleHandle() + public static IntPtr GetMainProgramHandle() { IntPtr result = IntPtr.Zero; #if TARGET_WINDOWS diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index 36ef3db4dad035..cf17f17dae86c3 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -753,7 +753,7 @@ public MarshalDirectiveException(string? message, System.Exception? inner) { } public static partial class NativeLibrary { public static void Free(System.IntPtr handle) { } - public static System.IntPtr GetEntryPointModuleHandle() { throw null; } + public static System.IntPtr GetMainProgramHandle() { throw null; } public static System.IntPtr GetExport(System.IntPtr handle, string name) { throw null; } public static System.IntPtr Load(string libraryPath) { throw null; } public static System.IntPtr Load(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath) { throw null; } diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 2eea0c59fda22f..55eb398a0a732b 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -182,7 +182,7 @@ public static int Main() NativeLibrary.Free(handle); // ----------------------------------------------- - // GetEntryPointModuleHandle Tests + // GetMainProgramHandle Tests // ----------------------------------------------- // Mobile test runs aren't hosted by corerun, so we don't have a well-known export to test here @@ -409,7 +409,7 @@ static TestResult GetSymbolFromEntryPointModuleHandle(string symbolToLoadFromHan { CurrentTest = nameof(GetSymbolFromEntryPointModuleHandle); return Run(() => { - IntPtr moduleHandle = NativeLibrary.GetEntryPointModuleHandle(); + IntPtr moduleHandle = NativeLibrary.GetMainProgramHandle(); bool success = NativeLibrary.TryGetExport(moduleHandle, symbolToLoadFromHandle, out IntPtr address); if (!success) return TestResult.ReturnFailure; From 5f368383c5d0dd0f631940f7ddad5e069bad7ce5 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 23 Aug 2021 15:07:21 -0700 Subject: [PATCH 20/35] Add some scenario names for the API testing to help make debugging issues easier. --- .../NativeLibrary/API/NativeLibraryTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 55eb398a0a732b..30f1ddf9d06ff3 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -189,16 +189,16 @@ public static int Main() if (IsHostedByCoreRun()) { // Export from CoreRun host - success &= EXPECT(GetSymbolFromEntryPointModuleHandle("GetCurrentClrDetails")); - success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonExistentCoreRunExport"), TestResult.ReturnFailure); + success &= EXPECT(GetSymbolfromMainProgramHandle("HostExecutable", "GetCurrentClrDetails")); + success &= EXPECT(GetSymbolfromMainProgramHandle("HostExecutable", "NonExistentCoreRunExport"), TestResult.ReturnFailure); } handle = NativeLibrary.Load(libName); // NativeLibrary does not load symbols globally, so we shouldn't be able to discover symbols from libaries loaded // with NativeLibary.Load. - success &= EXPECT(GetSymbolFromEntryPointModuleHandle(TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), TestResult.ReturnFailure); - success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonNativeSum"), TestResult.ReturnFailure); + success &= EXPECT(GetSymbolfromMainProgramHandle("LocallyLoadedNativeLib", TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), TestResult.ReturnFailure); + success &= EXPECT(GetSymbolfromMainProgramHandle("LocallyLoadedNativeLib", "NonNativeSum"), TestResult.ReturnFailure); NativeLibrary.Free(handle); @@ -209,8 +209,8 @@ public static int Main() // with the right flags to test the scenario. handle = LoadLibraryGlobally(Path.Combine(testBinDir, "libGloballyLoadedNativeLibrary.so")); - success &= EXPECT(GetSymbolFromEntryPointModuleHandle(TestLibrary.Utilities.IsX86 ? "_NativeMultiply@8" : "NativeMultiply")); - success &= EXPECT(GetSymbolFromEntryPointModuleHandle("NonNativeMultiply"), TestResult.ReturnFailure); + success &= EXPECT(GetSymbolfromMainProgramHandle("GloballyLoadedNativeLib", TestLibrary.Utilities.IsX86 ? "_NativeMultiply@8" : "NativeMultiply")); + success &= EXPECT(GetSymbolfromMainProgramHandle("GloballyLoadedNativeLib", "NonNativeMultiply"), TestResult.ReturnFailure); NativeLibrary.Free(handle); } @@ -405,9 +405,9 @@ static TestResult TryGetLibraryExport(IntPtr handle, string name) }); } - static TestResult GetSymbolFromEntryPointModuleHandle(string symbolToLoadFromHandle) + static TestResult GetSymbolfromMainProgramHandle(string scenarioName, string symbolToLoadFromHandle) { - CurrentTest = nameof(GetSymbolFromEntryPointModuleHandle); + CurrentTest = $"{nameof(GetSymbolfromMainProgramHandle)}-{scenarioName}"; return Run(() => { IntPtr moduleHandle = NativeLibrary.GetMainProgramHandle(); bool success = NativeLibrary.TryGetExport(moduleHandle, symbolToLoadFromHandle, out IntPtr address); From e9b70aea6c2f3cbab45e4670169b4c4088d5150d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 24 Aug 2021 09:57:06 -0700 Subject: [PATCH 21/35] Use Win32Exception for the native error code. --- .../src/System/Runtime/InteropServices/NativeLibrary.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs index 2875eb7fb3792f..5cf96c46cfe6b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.ComponentModel; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -273,7 +274,7 @@ public static IntPtr GetMainProgramHandle() // if we encounter it. if (result == IntPtr.Zero) { - Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + throw new Win32Exception(Marshal.GetLastPInvokeError()); } return result; } From 19bd0a96a19c51bdbbd72381515b501c09cc3e91 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 2 Sep 2021 15:45:00 -0700 Subject: [PATCH 22/35] Fix Windows test build. --- .../Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt index afa8f71f59d069..1c43e6a13f9a84 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt @@ -4,7 +4,10 @@ set(SOURCES ) # add the shared library add_library (NativeLibrary SHARED NativeLibrary.cpp) -target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL} dl) + +if (CLR_CMAKE_TARGET_UNIX) + target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL} dl) +endif() # add the shared library add_library (GloballyLoadedNativeLibrary SHARED GloballyLoadedNativeLibrary.cpp) From 745c9bcf8449a5899dbb5d42d756336356699db1 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 10 Feb 2022 17:38:00 -0800 Subject: [PATCH 23/35] Rewrite NativeLibrary tests to use the new xunit generator model so we can more easily annotate where we have platform-specific behavior in these tests. --- .../Common/CoreCLRTestLibrary/Utilities.cs | 1 + .../API/GetLibraryExportTests.cs | 87 ++++ .../API/GetMainProgramHandleTests.cs | 114 +++++ .../NativeLibrary/API/NativeLibraryTests.cs | 455 ++++++------------ .../API/NativeLibraryTests.csproj | 1 - .../Interop/NativeLibrary/API/TestHelpers.cs | 71 +++ 6 files changed, 413 insertions(+), 316 deletions(-) create mode 100644 src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs create mode 100644 src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs create mode 100644 src/tests/Interop/NativeLibrary/API/TestHelpers.cs diff --git a/src/tests/Common/CoreCLRTestLibrary/Utilities.cs b/src/tests/Common/CoreCLRTestLibrary/Utilities.cs index ad4d5bac5d3f8f..0ab8a56ce7e2ca 100644 --- a/src/tests/Common/CoreCLRTestLibrary/Utilities.cs +++ b/src/tests/Common/CoreCLRTestLibrary/Utilities.cs @@ -59,6 +59,7 @@ public static bool Verbose } public static bool IsX86 => (RuntimeInformation.ProcessArchitecture == Architecture.X86); + public static bool IsNotX86 => !IsX86; public static bool IsX64 => (RuntimeInformation.ProcessArchitecture == Architecture.X64); public static bool IsArm => (RuntimeInformation.ProcessArchitecture == Architecture.Arm); public static bool IsArm64 => (RuntimeInformation.ProcessArchitecture == Architecture.Arm64); diff --git a/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs new file mode 100644 index 00000000000000..f4ce2e8e540d91 --- /dev/null +++ b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using Xunit; +using static TestHelpers; + +class GetLibraryExportTests : IDisposable +{ + private readonly IntPtr handle; + + public GetLibraryExportTests() + { + handle = NativeLibrary.Load(NativeLibraryToLoad.GetFullPath()); + } + + [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsX86))] + public void GetValidExport_ManualMangling() + { + EXPECT(GetLibraryExport(handle, "_NativeSum@8")); + EXPECT(TryGetLibraryExport(handle, "_NativeSum@8")); + } + + [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsX86))] + public void GetValidExport() + { + EXPECT(GetLibraryExport(handle, "NativeSum")); + EXPECT(TryGetLibraryExport(handle, "NativeSum")); + } + + [Fact] + public void NullHandle() + { + EXPECT(GetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); + EXPECT(TryGetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); + } + + [Fact] + public void NullExport() + { + EXPECT(GetLibraryExport(handle, null), TestResult.ArgumentNull); + EXPECT(TryGetLibraryExport(handle, null), TestResult.ArgumentNull); + } + + [Fact] + public void ExportDoesNotExist() + { + EXPECT(GetLibraryExport(handle, "NonNativeSum"), TestResult.EntryPointNotFound); + EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure); + } + + + public void Dispose() => NativeLibrary.Free(handle); + + static TestResult GetLibraryExport(IntPtr handle, string name) + { + return Run(() => { + IntPtr address = NativeLibrary.GetExport(handle, name); + if (address == IntPtr.Zero) + return TestResult.ReturnNull; + if (RunExportedFunction(address, 1, 1) != 2) + return TestResult.IncorrectEvaluation; + return TestResult.Success; + }); + } + + static TestResult TryGetLibraryExport(IntPtr handle, string name) + { + return Run(() => { + IntPtr address = IntPtr.Zero; + bool success = NativeLibrary.TryGetExport(handle, name, out address); + if (!success) + return TestResult.ReturnFailure; + if (address == IntPtr.Zero) + return TestResult.ReturnNull; + if (RunExportedFunction(address, 1, 1) != 2) + return TestResult.IncorrectEvaluation; + return TestResult.Success; + }); + } + + [DllImport(NativeLibraryToLoad.Name)] + static extern int RunExportedFunction(IntPtr address, int arg1, int arg2); +} diff --git a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs new file mode 100644 index 00000000000000..fc3ad5ba14ca2e --- /dev/null +++ b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using Xunit; +using static TestHelpers; + +class GetMainProgramHandleTests +{ + // Mobile test runs aren't hosted by corerun, so we don't have a well-known export to test here + [ConditionalFact(nameof(IsHostedByCoreRun))] + public static void CanAccessCoreRunExportFromMainProgramHandle() + { + EXPECT(GetSymbolFromMainProgramHandle("HostExecutable", "GetCurrentClrDetails")); + EXPECT(GetSymbolFromMainProgramHandle("HostExecutable", "NonExistentCoreRunExport"), TestResult.ReturnFailure); + } + + [Fact] + public static void NativeLibraryLoadDoesNotLoadSymbolsGlobally() + { + IntPtr handle = NativeLibrary.Load(NativeLibraryToLoad.GetFullPath()); + try + { + // NativeLibrary does not load symbols globally, so we shouldn't be able to discover symbols from libaries loaded + // with NativeLibary.Load. + EXPECT(GetSymbolFromMainProgramHandle("LocallyLoadedNativeLib", TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), TestResult.ReturnFailure); + EXPECT(GetSymbolFromMainProgramHandle("LocallyLoadedNativeLib", "NonNativeSum"), TestResult.ReturnFailure); + + } + finally + { + NativeLibrary.Free(handle); + } + } + + [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotX86))] + [PlatformSpecific(~TestPlatforms.Windows)] + public static void GloballyLoadedLibrarySymbolsVisibleFromMainProgramHandle() + { + // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. + // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code + // with the right flags to test the scenario. + IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "libGloballyLoadedNativeLibrary.so")); + + try + { + EXPECT(GetSymbolFromMainProgramHandle("GloballyLoadedNativeLib", "NativeMultiply")); + } + finally + { + NativeLibrary.Free(handle); + } + } + + [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotX86))] + [PlatformSpecific(~TestPlatforms.Windows)] + public static void InvalidSymbolName_Fails() + { + // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. + // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code + // with the right flags to test the scenario. + IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "libGloballyLoadedNativeLibrary.so")); + + try + { + EXPECT(GetSymbolFromMainProgramHandle("GloballyLoadedNativeLib", "NonNativeMultiply"), TestResult.ReturnFailure); + } + finally + { + NativeLibrary.Free(handle); + } + } + + [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsX86))] + [PlatformSpecific(~TestPlatforms.Windows)] + public static void GloballyLoadedLibrarySymbolsVisibleFromMainProgramHandle_Mangling() + { + // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. + // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code + // with the right flags to test the scenario. + IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "libGloballyLoadedNativeLibrary.so")); + + try + { + EXPECT(GetSymbolFromMainProgramHandle("GloballyLoadedNativeLib", "_NativeMultiply@8")); + } + finally + { + NativeLibrary.Free(handle); + } + } + + public static bool IsHostedByCoreRun { get; } = Process.GetCurrentProcess().MainModule.ModuleName is "corerun" or "corerun.exe"; + + static TestResult GetSymbolFromMainProgramHandle(string scenarioName, string symbolToLoadFromHandle) + { + return Run(() => { + IntPtr moduleHandle = NativeLibrary.GetMainProgramHandle(); + bool success = NativeLibrary.TryGetExport(moduleHandle, symbolToLoadFromHandle, out IntPtr address); + if (!success) + return TestResult.ReturnFailure; + if (address == IntPtr.Zero) + return TestResult.ReturnNull; + return TestResult.Success; + }); + } + + + [DllImport(NativeLibraryToLoad.Name)] + static extern IntPtr LoadLibraryGlobally(string name); +} diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 85d45133907b6b..1d1fb72f06893e 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -6,291 +6,175 @@ using System.Reflection; using System.Runtime.InteropServices; using Xunit; +using static TestHelpers; -enum TestResult { - Success, - ReturnFailure, - ReturnNull, - IncorrectEvaluation, - ArgumentNull, - ArgumentBad, - DllNotFound, - BadImage, - InvalidOperation, - EntryPointNotFound, - GenericException - }; - -public class NativeLibraryTest +public class NativeLibraryTests : IDisposable { - static string CurrentTest; - static bool Verbose = false; + private readonly Assembly assembly; + private readonly string testBinDir; + private readonly string libFullPath; - public static int Main() + public NativeLibraryTests() { - bool success = true; + assembly = System.Reflection.Assembly.GetExecutingAssembly(); + testBinDir = Path.GetDirectoryName(assembly.Location); + libFullPath = NativeLibraryToLoad.GetFullPath(); + } - Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly(); - string testBinDir = Path.GetDirectoryName(assembly.Location); - string libFullPath = NativeLibraryToLoad.GetFullPath(); - string libName; - IntPtr handle; + [Fact] + public void LoadLibraryFullPath_NameOnly() + { + string libName = libFullPath; + EXPECT(LoadLibrary_NameOnly(libName)); + EXPECT(TryLoadLibrary_NameOnly(libName)); + } - try - { - // ----------------------------------------------- - // Simple LoadLibrary() API Tests - // ----------------------------------------------- - - // Calls on correct full-path to native lib - libName = libFullPath; - success &= EXPECT(LoadLibrarySimple(libName)); - success &= EXPECT(TryLoadLibrarySimple(libName)); - - // Calls on non-existant file - libName = Path.Combine(testBinDir, "notfound"); - success &= EXPECT(LoadLibrarySimple(libName), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); - - // Calls on an invalid file - libName = Path.Combine(testBinDir, "NativeLibrary.cpp"); - success &= EXPECT(LoadLibrarySimple(libName), - (TestLibrary.Utilities.IsWindows) ? TestResult.BadImage : TestResult.DllNotFound); - success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ReturnFailure); - - // Calls on null input - libName = null; - success &= EXPECT(LoadLibrarySimple(libName), TestResult.ArgumentNull); - success &= EXPECT(TryLoadLibrarySimple(libName), TestResult.ArgumentNull); - - // ----------------------------------------------- - // Advanced LoadLibrary() API Tests - // ----------------------------------------------- - - // Advanced LoadLibrary() API Tests - // Calls on correct full-path to native lib - libName = libFullPath; - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); - - // Calls on non-existant file - libName = Path.Combine(testBinDir, "notfound"); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), TestResult.ReturnFailure); - - // Calls on an invalid file - libName = Path.Combine(testBinDir, "NativeLibrary.cpp"); - // The VM can only distinguish BadImageFormatException from DllNotFoundException on Windows. - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null), - (TestLibrary.Utilities.IsWindows) ? TestResult.BadImage : TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), TestResult.ReturnFailure); - - // Calls on just Native Library name - libName = NativeLibraryToLoad.Name; - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); - - // Calls on Native Library name with correct prefix-suffix - libName = NativeLibraryToLoad.GetFileName(); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null)); - - // Calls on full path without prefix-siffix - libName = Path.Combine(testBinDir, NativeLibraryToLoad.Name); - // DllImport doesn't add a prefix if the name is preceeded by a path specification. - // Windows only needs a suffix, but Linux and Mac need both prefix and suffix - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null), - (TestLibrary.Utilities.IsWindows) ? TestResult.Success : TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null), - (TestLibrary.Utilities.IsWindows) ? TestResult.Success : TestResult.ReturnFailure); - - // Check for loading a native binary in the system32 directory. - // The choice of the binary is arbitrary, and may not be available on - // all Windows platforms (ex: Nano server) - libName = "url.dll"; - if (TestLibrary.Utilities.IsWindows && - File.Exists(Path.Combine(Environment.SystemDirectory, libName))) - { - // Calls on a valid library from System32 directory - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.System32)); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.System32)); - - // Calls on a valid library from application directory - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.ReturnFailure); - } - - // Calls with null libName input - success &= EXPECT(LoadLibraryAdvanced(null, assembly, null), TestResult.ArgumentNull); - success &= EXPECT(TryLoadLibraryAdvanced(null, assembly, null), TestResult.ArgumentNull); - - // Calls with null assembly - libName = NativeLibraryToLoad.Name; - success &= EXPECT(LoadLibraryAdvanced(libName, null, null), TestResult.ArgumentNull); - success &= EXPECT(TryLoadLibraryAdvanced(libName, null, null), TestResult.ArgumentNull); - - // Ensure that a lib is not picked up from current directory when - // a different full-path is specified. - libName = Path.Combine(testBinDir, Path.Combine("lib", NativeLibraryToLoad.Name)); - success &= EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.DllNotFound); - success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.ReturnFailure); - - // ----------------------------------------------- - // FreeLibrary Tests - // ----------------------------------------------- - - libName = libFullPath; - handle = NativeLibrary.Load(libName); - - // Valid Free - success &= EXPECT(FreeLibrary(handle)); - - // Double Free - if (TestLibrary.Utilities.IsWindows) - { - // The FreeLibrary() implementation simply calls the appropriate OS API - // with the supplied handle. Not all OSes consistently return an error - // when a library is double-freed. - success &= EXPECT(FreeLibrary(handle), TestResult.InvalidOperation); - } - - // Null Free - success &= EXPECT(FreeLibrary(IntPtr.Zero)); - - // ----------------------------------------------- - // GetLibraryExport Tests - // ----------------------------------------------- - libName = libFullPath; - handle = NativeLibrary.Load(libName); - - // Valid Call (with some hard-coded name mangling) - success &= EXPECT(GetLibraryExport(handle, TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum")); - success &= EXPECT(TryGetLibraryExport(handle, TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum")); - - // Call with null handle - success &= EXPECT(GetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); - success &= EXPECT(TryGetLibraryExport(IntPtr.Zero, "NativeSum"), TestResult.ArgumentNull); - - // Call with null string - success &= EXPECT(GetLibraryExport(handle, null), TestResult.ArgumentNull); - success &= EXPECT(TryGetLibraryExport(handle, null), TestResult.ArgumentNull); - - // Call with wrong string - success &= EXPECT(GetLibraryExport(handle, "NonNativeSum"), TestResult.EntryPointNotFound); - success &= EXPECT(TryGetLibraryExport(handle, "NonNativeSum"), TestResult.ReturnFailure); + [Fact] + public void LoadLibraryOnNonExistentFile_NameOnly() + { + string libName = Path.Combine(testBinDir, "notfound"); + EXPECT(LoadLibrary_NameOnly(libName)); + EXPECT(TryLoadLibrary_NameOnly(libName)); + } + + [Fact] + public void LoadLibraryOnInvalidFile_NameOnly() + { + string libName = Path.Combine(testBinDir, "NativeLibrary.cpp"); + EXPECT(LoadLibrary_NameOnly(libName), + OperatingSystem.IsWindows() ? TestResult.BadImage : TestResult.DllNotFound); + EXPECT(TryLoadLibrary_NameOnly(libName), TestResult.ReturnFailure); + } - NativeLibrary.Free(handle); + [Fact] + public void LoadLibraryFullPath_WithAssembly() + { + string libName = libFullPath; + EXPECT(LoadLibrary_WithAssembly(libName, assembly, null)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null)); + } - // ----------------------------------------------- - // GetMainProgramHandle Tests - // ----------------------------------------------- + [Fact] + public void LoadLibraryOnNonExistentFile_WithAssembly() + { + string libName = Path.Combine(testBinDir, "notfound"); + EXPECT(LoadLibrary_WithAssembly(libName, assembly, null), TestResult.DllNotFound); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null), TestResult.ReturnFailure); + } - // Mobile test runs aren't hosted by corerun, so we don't have a well-known export to test here - if (IsHostedByCoreRun()) - { - // Export from CoreRun host - success &= EXPECT(GetSymbolfromMainProgramHandle("HostExecutable", "GetCurrentClrDetails")); - success &= EXPECT(GetSymbolfromMainProgramHandle("HostExecutable", "NonExistentCoreRunExport"), TestResult.ReturnFailure); - } + [Fact] + public void LoadLibraryOnInvalidFile_WithAssembly() + { + string libName = Path.Combine(testBinDir, "NativeLibrary.cpp"); + EXPECT(LoadLibrary_WithAssembly(libName, assembly, null), + OperatingSystem.IsWindows() ? TestResult.BadImage : TestResult.DllNotFound); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null), TestResult.ReturnFailure); + } - handle = NativeLibrary.Load(libName); + [Fact] + public void LoadLibraryNameOnly_WithAssembly() + { + string libName = NativeLibraryToLoad.Name; + EXPECT(LoadLibrary_WithAssembly(libName, assembly, null)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null)); + } - // NativeLibrary does not load symbols globally, so we shouldn't be able to discover symbols from libaries loaded - // with NativeLibary.Load. - success &= EXPECT(GetSymbolfromMainProgramHandle("LocallyLoadedNativeLib", TestLibrary.Utilities.IsX86 ? "_NativeSum@8" : "NativeSum"), TestResult.ReturnFailure); - success &= EXPECT(GetSymbolfromMainProgramHandle("LocallyLoadedNativeLib", "NonNativeSum"), TestResult.ReturnFailure); + [Fact] + public void LoadLibraryFileName_WithAssembly() + { + string libName = NativeLibraryToLoad.GetFileName(); + EXPECT(LoadLibrary_WithAssembly(libName, assembly, null)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null)); + } - NativeLibrary.Free(handle); + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public void LoadLibraryFullPathWithoutNativePrefixOrSuffix_WithAssembly_Success() + { + // DllImport doesn't add a prefix if the name is preceeded by a path specification. + // Windows only needs a suffix, so adding only the suffix is successful + string libName = Path.Combine(testBinDir, NativeLibraryToLoad.Name); + EXPECT(LoadLibrary_WithAssembly(libName, assembly, null)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null)); + } - if (!OperatingSystem.IsWindows()) - { - // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. - // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code - // with the right flags to test the scenario. - handle = LoadLibraryGlobally(Path.Combine(testBinDir, "libGloballyLoadedNativeLibrary.so")); + [Fact] + [PlatformSpecific(~TestPlatforms.Windows)] + public void LoadLibraryFullPathWithoutNativePrefixOrSuffix_WithAssembly_Failure() + { + // DllImport doesn't add a prefix if the name is preceeded by a path specification. + // Linux and Mac need both prefix and suffix + string libName = NativeLibraryToLoad.GetFileName(); + EXPECT(LoadLibrary_WithAssembly(libName, assembly, null), TestResult.DllNotFound); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null), TestResult.ReturnFailure); + } - success &= EXPECT(GetSymbolfromMainProgramHandle("GloballyLoadedNativeLib", TestLibrary.Utilities.IsX86 ? "_NativeMultiply@8" : "NativeMultiply")); - success &= EXPECT(GetSymbolfromMainProgramHandle("GloballyLoadedNativeLib", "NonNativeMultiply"), TestResult.ReturnFailure); + public static bool HasKnownLibraryInSystemDirectory => + OperatingSystem.IsWindows() + && File.Exists(Path.Combine(Environment.SystemDirectory, "url.dll")); - NativeLibrary.Free(handle); - } - } - catch (Exception e) - { - // Catch any exceptions in NativeLibrary calls directly within this function. - // These calls are used to setup the environment for tests that follow, and are not expected to fail. - // If they do fail (ex: incorrect build environment) fail with an error code, rather than segmentation fault. - Console.WriteLine(String.Format("Unhandled exception {0}", e)); - success = false; - } + [ConditionalFact(nameof(HasKnownLibraryInSystemDirectory))] + public void LoadSystemLibrary_WithSearchPath() + { + string libName = "url.dll"; + // Calls on a valid library from System32 directory + EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.System32)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.System32)); + + // Calls on a valid library from application directory + EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.DllNotFound); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.ReturnFailure); + } - return (success) ? 100 : -100; + [Fact] + public void LoadLibrary_NullLibName() + { + EXPECT(LoadLibrary_WithAssembly(null, assembly, null), TestResult.ArgumentNull); + EXPECT(TryLoadLibrary_WithAssembly(null, assembly, null), TestResult.ArgumentNull); } - static bool IsHostedByCoreRun() + [Fact] + public void LoadLibrary_NullAssembly() { - return Process.GetCurrentProcess().MainModule.ModuleName is "corerun" or "corerun.exe"; + string libName = NativeLibraryToLoad.Name; + EXPECT(LoadLibrary_WithAssembly(libName, null, null), TestResult.ArgumentNull); + EXPECT(TryLoadLibrary_WithAssembly(libName, null, null), TestResult.ArgumentNull); } - static bool EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success) + [Fact] + public void LoadLibary_UsesFullPath_EvenWhen_AssemblyDirectory_Specified() { - if (actualValue == expectedValue) - { - if (Verbose) - Console.WriteLine(String.Format("{0} : {1} : [OK]", CurrentTest, actualValue)); - return true; - } - else - { - Console.WriteLine(String.Format(" {0} : {1} : [FAIL]", CurrentTest, actualValue)); - return false; - } + string libName = Path.Combine(testBinDir, Path.Combine("lib", NativeLibraryToLoad.Name)); + EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.DllNotFound); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.ReturnFailure); } - static TestResult Run (Func test) + [Fact] + public void Free() { - TestResult result; + string libName = libFullPath; + IntPtr handle = NativeLibrary.Load(libName); - try - { - result = test(); - } - catch (ArgumentNullException) - { - return TestResult.ArgumentNull; - } - catch (ArgumentException) - { - return TestResult.ArgumentBad; - } - catch (DllNotFoundException) - { - return TestResult.DllNotFound; - } - catch (BadImageFormatException) - { - return TestResult.BadImage; - } - catch (InvalidOperationException) - { - return TestResult.InvalidOperation; - } - catch (EntryPointNotFoundException) - { - return TestResult.EntryPointNotFound; - } - catch (Exception) + // Valid Free + EXPECT(FreeLibrary(handle)); + + // Double Free + if (OperatingSystem.IsWindows()) { - return TestResult.GenericException; + // The FreeLibrary() implementation simply calls the appropriate OS API + // with the supplied handle. Not all OSes consistently return an error + // when a library is double-freed. + EXPECT(FreeLibrary(handle), TestResult.InvalidOperation); } - return result; + // Null Free + EXPECT(FreeLibrary(IntPtr.Zero)); } - static TestResult LoadLibrarySimple(string libPath) - { - CurrentTest = String.Format("LoadLibrary({0})", libPath); + public void Dispose() {} + static TestResult LoadLibrary_NameOnly(string libPath) + { IntPtr handle = IntPtr.Zero; TestResult result = Run(() => { @@ -305,10 +189,8 @@ static TestResult LoadLibrarySimple(string libPath) return result; } - static TestResult TryLoadLibrarySimple(string libPath) + static TestResult TryLoadLibrary_NameOnly(string libPath) { - CurrentTest = String.Format("TryLoadLibrary({0})", libPath); - IntPtr handle = IntPtr.Zero; TestResult result = Run(() => { @@ -326,10 +208,8 @@ static TestResult TryLoadLibrarySimple(string libPath) } - static TestResult LoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath) + static TestResult LoadLibrary_WithAssembly(string libName, Assembly assembly, DllImportSearchPath? searchPath) { - CurrentTest = String.Format("LoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); - IntPtr handle = IntPtr.Zero; TestResult result = Run(() => { @@ -344,10 +224,8 @@ static TestResult LoadLibraryAdvanced(string libName, Assembly assembly, DllImpo return result; } - static TestResult TryLoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath) + static TestResult TryLoadLibrary_WithAssembly(string libName, Assembly assembly, DllImportSearchPath? searchPath) { - CurrentTest = String.Format("TryLoadLibrary({0}, {1}, {2})", libName, assembly, searchPath); - IntPtr handle = IntPtr.Zero; TestResult result = Run(() => { @@ -366,62 +244,9 @@ static TestResult TryLoadLibraryAdvanced(string libName, Assembly assembly, DllI static TestResult FreeLibrary(IntPtr handle) { - CurrentTest = String.Format("FreeLibrary({0})", handle); - return Run(() => { NativeLibrary.Free(handle); return TestResult.Success; }); } - - static TestResult GetLibraryExport(IntPtr handle, string name) - { - CurrentTest = String.Format("GetLibraryExport({0}, {1})", handle, name); - - return Run(() => { - IntPtr address = NativeLibrary.GetExport(handle, name); - if (address == IntPtr.Zero) - return TestResult.ReturnNull; - if (RunExportedFunction(address, 1, 1) != 2) - return TestResult.IncorrectEvaluation; - return TestResult.Success; - }); - } - - static TestResult TryGetLibraryExport(IntPtr handle, string name) - { - CurrentTest = String.Format("TryGetLibraryExport({0}, {1})", handle, name); - - return Run(() => { - IntPtr address = IntPtr.Zero; - bool success = NativeLibrary.TryGetExport(handle, name, out address); - if (!success) - return TestResult.ReturnFailure; - if (address == IntPtr.Zero) - return TestResult.ReturnNull; - if (RunExportedFunction(address, 1, 1) != 2) - return TestResult.IncorrectEvaluation; - return TestResult.Success; - }); - } - - static TestResult GetSymbolfromMainProgramHandle(string scenarioName, string symbolToLoadFromHandle) - { - CurrentTest = $"{nameof(GetSymbolfromMainProgramHandle)}-{scenarioName}"; - return Run(() => { - IntPtr moduleHandle = NativeLibrary.GetMainProgramHandle(); - bool success = NativeLibrary.TryGetExport(moduleHandle, symbolToLoadFromHandle, out IntPtr address); - if (!success) - return TestResult.ReturnFailure; - if (address == IntPtr.Zero) - return TestResult.ReturnNull; - return TestResult.Success; - }); - } - - [DllImport(NativeLibraryToLoad.Name)] - static extern int RunExportedFunction(IntPtr address, int arg1, int arg2); - - [DllImport(NativeLibraryToLoad.Name)] - static extern IntPtr LoadLibraryGlobally(string name); } diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj index 51c156e0d9b9e6..e06b921e87a5ea 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj @@ -1,6 +1,5 @@ - Exe true diff --git a/src/tests/Interop/NativeLibrary/API/TestHelpers.cs b/src/tests/Interop/NativeLibrary/API/TestHelpers.cs new file mode 100644 index 00000000000000..25663d0dddfeee --- /dev/null +++ b/src/tests/Interop/NativeLibrary/API/TestHelpers.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using Xunit; + +enum TestResult +{ + Success, + ReturnFailure, + ReturnNull, + IncorrectEvaluation, + ArgumentNull, + ArgumentBad, + DllNotFound, + BadImage, + InvalidOperation, + EntryPointNotFound, + GenericException +}; + +static class TestHelpers +{ + public static void EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success) + { + Assert.Equal(expectedValue, actualValue); + } + + public static TestResult Run (Func test) + { + TestResult result; + + try + { + result = test(); + } + catch (ArgumentNullException) + { + return TestResult.ArgumentNull; + } + catch (ArgumentException) + { + return TestResult.ArgumentBad; + } + catch (DllNotFoundException) + { + return TestResult.DllNotFound; + } + catch (BadImageFormatException) + { + return TestResult.BadImage; + } + catch (InvalidOperationException) + { + return TestResult.InvalidOperation; + } + catch (EntryPointNotFoundException) + { + return TestResult.EntryPointNotFound; + } + catch (Exception) + { + return TestResult.GenericException; + } + + return result; + } +} From efc285fa4f5b79c9957c199641ed3a9fe7286359 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Sat, 12 Feb 2022 12:05:45 -0800 Subject: [PATCH 24/35] Fix typo --- src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 1d1fb72f06893e..ecc3f571c98cf5 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -33,8 +33,8 @@ public void LoadLibraryFullPath_NameOnly() public void LoadLibraryOnNonExistentFile_NameOnly() { string libName = Path.Combine(testBinDir, "notfound"); - EXPECT(LoadLibrary_NameOnly(libName)); - EXPECT(TryLoadLibrary_NameOnly(libName)); + EXPECT(LoadLibrary_NameOnly(libName), TestResult.DllNotFound); + EXPECT(TryLoadLibrary_NameOnly(libName), TestResult.ReturnFailure); } [Fact] From 3f020e81dd5a0328a8ce03907b269d1097752a64 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 15 Mar 2022 15:05:30 -0700 Subject: [PATCH 25/35] Clean up some code and move the new P/Invoke over to be generated. --- .../src/Interop/Unix/System.Native/Interop.Dl.cs | 15 --------------- .../Unix/System.Native/Interop.DynamicLoad.cs | 3 +++ .../src/System.Private.CoreLib.Shared.projitems | 4 ++-- .../NativeLibrary/API/NativeLibraryTests.cs | 2 +- 4 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs deleted file mode 100644 index 607cb623eeb46d..00000000000000 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.Dl.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -internal static partial class Interop -{ - internal static partial class Sys - { - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)] - internal static extern IntPtr GetDefaultSearchOrderPseudoHandle(); - } -} 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 a6ccd9121d84f6..0a3744bfbce9ca 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 @@ -19,5 +19,8 @@ internal unsafe partial class Sys [GeneratedDllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_FreeLibrary")] internal static partial void FreeLibrary(IntPtr handle); + + [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)] + internal static partial IntPtr GetDefaultSearchOrderPseudoHandle(); } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 33269e0a7a9d2e..3733cbf62a1321 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1972,8 +1972,8 @@ Common\Interop\Unix\System.Native\Interop.CopyFile.cs - - Common\Interop\Unix\System.Native\Interop.Dl.cs + + Common\Interop\Unix\System.Native\Interop.DynamicLoad.cs Common\Interop\Unix\System.Native\Interop.ErrNo.cs diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index ecc3f571c98cf5..e46415992e4dd9 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -104,7 +104,7 @@ public void LoadLibraryFullPathWithoutNativePrefixOrSuffix_WithAssembly_Failure( { // DllImport doesn't add a prefix if the name is preceeded by a path specification. // Linux and Mac need both prefix and suffix - string libName = NativeLibraryToLoad.GetFileName(); + string libName = Path.Combine(testBinDir, NativeLibraryToLoad.Name); EXPECT(LoadLibrary_WithAssembly(libName, assembly, null), TestResult.DllNotFound); EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null), TestResult.ReturnFailure); } From 7ffac6828b2c36586eef0ff6f2432046245cc2b7 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 15 Mar 2022 15:51:55 -0700 Subject: [PATCH 26/35] Rename attribute --- .../src/Interop/Unix/System.Native/Interop.DynamicLoad.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c2feacc262f359..f806b691ae3b95 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 @@ -20,7 +20,7 @@ internal unsafe partial class Sys [LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_FreeLibrary")] internal static partial void FreeLibrary(IntPtr handle); - [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)] + [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)] internal static partial IntPtr GetDefaultSearchOrderPseudoHandle(); } } From a175e5c90af9a08bdc21c01672286b4c057da75f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 15 Mar 2022 16:24:11 -0700 Subject: [PATCH 27/35] Fix duplicate source file include --- .../System.Private.CoreLib/src/System.Private.CoreLib.csproj | 3 --- 1 file changed, 3 deletions(-) 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 829c2a570ed2b0..c3934de3a9b662 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 @@ -327,9 +327,6 @@ Interop\Unix\System.Native\Interop.SchedGetCpu.cs - - Interop\Unix\System.Native\Interop.DynamicLoad.cs - Interop\Unix\System.Native\Interop.Threading.cs From 81f9daa81dda8b3a86ccde01e3ae129687cfdac4 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 16 Mar 2022 09:20:05 -0700 Subject: [PATCH 28/35] Fix allocation in NativeLibrary.Free to handle the raw lib handle case on Mono --- src/mono/mono/metadata/native-library.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index 86ca4c95ca9cd3..68823483fb2fd2 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -1222,9 +1222,9 @@ ves_icall_System_Runtime_InteropServices_NativeLibrary_FreeLib (gpointer lib, Mo g_hash_table_add (native_library_module_blocklist, module); mono_dl_close (module); } else { - MonoDl raw_module = { { 0 } }; - raw_module.handle = lib; - mono_dl_close (&raw_module); + MonoDl* raw_module = g_new0(MonoDl, 1); + raw_module->handle = lib; + mono_dl_close (raw_module); } leave: From a6117bae37497ccc05784fef2f20e43e6eb6435a Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 16 Mar 2022 11:28:50 -0700 Subject: [PATCH 29/35] Fix Apple test builds --- .../API/GetMainProgramHandleTests.cs | 29 ++++++++++++++----- .../NativeLibraryToLoad.cs | 11 +++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs index fc3ad5ba14ca2e..f2f8c67363f82b 100644 --- a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs +++ b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection; @@ -19,6 +20,7 @@ public static void CanAccessCoreRunExportFromMainProgramHandle() } [Fact] + [SkipOnPlatform(TestPlatforms.OSX | TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst, "Apple platforms load library symbols globally by default.")] public static void NativeLibraryLoadDoesNotLoadSymbolsGlobally() { IntPtr handle = NativeLibrary.Load(NativeLibraryToLoad.GetFullPath()); @@ -37,13 +39,13 @@ public static void NativeLibraryLoadDoesNotLoadSymbolsGlobally() } [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotX86))] - [PlatformSpecific(~TestPlatforms.Windows)] + [SkipOnPlatform(TestPlatforms.Windows, "Windows does not have a concept of globally loaded symbols")] public static void GloballyLoadedLibrarySymbolsVisibleFromMainProgramHandle() { // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code // with the right flags to test the scenario. - IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "libGloballyLoadedNativeLibrary.so")); + IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary"))); try { @@ -56,13 +58,13 @@ public static void GloballyLoadedLibrarySymbolsVisibleFromMainProgramHandle() } [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotX86))] - [PlatformSpecific(~TestPlatforms.Windows)] + [SkipOnPlatform(TestPlatforms.Windows, "Windows does not have a concept of globally loaded symbols")] public static void InvalidSymbolName_Fails() { // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code // with the right flags to test the scenario. - IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "libGloballyLoadedNativeLibrary.so")); + IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary"))); try { @@ -75,13 +77,13 @@ public static void InvalidSymbolName_Fails() } [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsX86))] - [PlatformSpecific(~TestPlatforms.Windows)] + [SkipOnPlatform(TestPlatforms.Windows, "Windows does not have a concept of globally loaded symbols")] public static void GloballyLoadedLibrarySymbolsVisibleFromMainProgramHandle_Mangling() { // On non-Windows platforms, symbols from globally loaded shared libraries will also be discoverable. // Globally loading symbols is not the .NET default, so we use a call to dlopen in native code // with the right flags to test the scenario. - IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "libGloballyLoadedNativeLibrary.so")); + IntPtr handle = LoadLibraryGlobally(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary"))); try { @@ -108,7 +110,18 @@ static TestResult GetSymbolFromMainProgramHandle(string scenarioName, string sym }); } + static IntPtr LoadLibraryGlobally(string name) + { + IntPtr handle = LoadLibraryGloballyNative(name); + + if (handle == IntPtr.Zero) + { + throw new Win32Exception(Marshal.GetLastPInvokeError()); + } + + return handle; - [DllImport(NativeLibraryToLoad.Name)] - static extern IntPtr LoadLibraryGlobally(string name); + [DllImport(NativeLibraryToLoad.Name, EntryPoint = "LoadLibraryGlobally", SetLastError = true)] + static extern IntPtr LoadLibraryGloballyNative(string name); + } } diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs index 4a61c599961f5a..21dc1ca1c9c986 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs @@ -12,15 +12,20 @@ public class NativeLibraryToLoad public const string InvalidName = "DoesNotExist"; public static string GetFileName() + { + return GetLibraryFileName(Name); + } + + public static string GetLibraryFileName(string name) { if (OperatingSystem.IsWindows()) - return $"{Name}.dll"; + return $"{name}.dll"; if (OperatingSystem.IsLinux()) - return $"lib{Name}.so"; + return $"lib{name}.so"; if (OperatingSystem.IsMacOS()) - return $"lib{Name}.dylib"; + return $"lib{name}.dylib"; throw new PlatformNotSupportedException(); } From f17ec5af60694db4424338037ffaa4458f29b7bb Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 16 Mar 2022 13:19:16 -0700 Subject: [PATCH 30/35] Fix typo in ConditionalFact condition --- src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs index f4ce2e8e540d91..e87095e23b5681 100644 --- a/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs +++ b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs @@ -24,7 +24,7 @@ public void GetValidExport_ManualMangling() EXPECT(TryGetLibraryExport(handle, "_NativeSum@8")); } - [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsX86))] + [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotX86))] public void GetValidExport() { EXPECT(GetLibraryExport(handle, "NativeSum")); From b8aa4febe2bd42322fd207d5e34925020ffd656d Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 16 Mar 2022 16:09:18 -0700 Subject: [PATCH 31/35] Don't P/Invoke into the same NativeLibrary that we're testing our double-free check against. P/Invoking messes up the ref-counts since the runtime loads the native library, so the double free test didn't work. --- .../NativeLibrary/API/GetLibraryExportTests.cs | 6 ++++-- .../API/GetMainProgramHandleTests.cs | 2 +- .../NativeLibraryToLoad/CMakeLists.txt | 3 +++ .../NativeLibraryToLoad/GlobalLoadHelper.cpp | 17 +++++++++++++++++ .../NativeLibraryToLoad/NativeLibrary.cpp | 17 ----------------- 5 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GlobalLoadHelper.cpp diff --git a/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs index e87095e23b5681..51b285cc26d690 100644 --- a/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs +++ b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs @@ -82,6 +82,8 @@ static TestResult TryGetLibraryExport(IntPtr handle, string name) }); } - [DllImport(NativeLibraryToLoad.Name)] - static extern int RunExportedFunction(IntPtr address, int arg1, int arg2); + private static unsafe int RunExportedFunction(IntPtr address, int arg1, int arg2) + { + return ((delegate* unmanaged)address)(arg1, arg2); + } } diff --git a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs index f2f8c67363f82b..437b3586c1af45 100644 --- a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs +++ b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs @@ -121,7 +121,7 @@ static IntPtr LoadLibraryGlobally(string name) return handle; - [DllImport(NativeLibraryToLoad.Name, EntryPoint = "LoadLibraryGlobally", SetLastError = true)] + [DllImport("GlobalLoadHelper", EntryPoint = "LoadLibraryGlobally", SetLastError = true)] static extern IntPtr LoadLibraryGloballyNative(string name); } } diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt index 1c43e6a13f9a84..e0ec4028d03a44 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt @@ -10,5 +10,8 @@ if (CLR_CMAKE_TARGET_UNIX) endif() # add the shared library +add_library (GlobalLoadHelper SHARED GlobalLoadHelper.cpp) +target_link_libraries(GlobalLoadHelper ${LINK_LIBRARIES_ADDITIONAL}) + add_library (GloballyLoadedNativeLibrary SHARED GloballyLoadedNativeLibrary.cpp) target_link_libraries(GloballyLoadedNativeLibrary ${LINK_LIBRARIES_ADDITIONAL}) diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GlobalLoadHelper.cpp b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GlobalLoadHelper.cpp new file mode 100644 index 00000000000000..271634668164a1 --- /dev/null +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GlobalLoadHelper.cpp @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +#include +#include +#ifndef TARGET_WINDOWS +#include +#endif + +extern "C" DLL_EXPORT void* LoadLibraryGlobally(const char* name) +{ +#ifdef TARGET_WINDOWS + // Windows doesn't support global symbol loading. + return NULL; +#else + return dlopen(name, RTLD_GLOBAL | RTLD_LAZY); +#endif +} \ No newline at end of file diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp index e0e5e3bc9f1c10..99f867e9f7c7da 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp @@ -10,20 +10,3 @@ extern "C" DLL_EXPORT int NativeSum(int a, int b) { return a + b; } - -extern "C" DLL_EXPORT int RunExportedFunction(void *function, int arg1, int arg2) -{ - int(*f)(int, int) = reinterpret_cast(function); - return f(arg1, arg2); -} - - -extern "C" DLL_EXPORT void* LoadLibraryGlobally(const char* name) -{ -#ifdef TARGET_WINDOWS - // Windows doesn't support global symbol loading. - return NULL; -#else - return dlopen(name, RTLD_GLOBAL | RTLD_LAZY); -#endif -} \ No newline at end of file From 4e7a69c3418bb140fe2f59fe075d1af1ea7cc13f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 17 Mar 2022 14:41:47 -0700 Subject: [PATCH 32/35] Fix GetCurrentClrDetails exporting on win x86 --- src/coreclr/hosts/corerun/corerun.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/hosts/corerun/corerun.hpp b/src/coreclr/hosts/corerun/corerun.hpp index 886b61bf53cbd6..c2f9ae40f07c34 100644 --- a/src/coreclr/hosts/corerun/corerun.hpp +++ b/src/coreclr/hosts/corerun/corerun.hpp @@ -47,6 +47,7 @@ namespace pal } #ifdef TARGET_WINDOWS +#define CDECL __cdecl #include #define DLL_EXPORT __declspec(dllexport) From c58228be052750b31bf152240ef6e109c37ba2d3 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 17 Mar 2022 15:34:55 -0700 Subject: [PATCH 33/35] Use delegate interop to avoid hitting issues with function pointers and Mono FullAOT --- .../Interop/NativeLibrary/API/GetLibraryExportTests.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs index 51b285cc26d690..4f0453dbdf8b0a 100644 --- a/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs +++ b/src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs @@ -84,6 +84,13 @@ static TestResult TryGetLibraryExport(IntPtr handle, string name) private static unsafe int RunExportedFunction(IntPtr address, int arg1, int arg2) { - return ((delegate* unmanaged)address)(arg1, arg2); + // We use a delegate here instead of a function pointer to avoid hitting issues + // where Mono AOT doesn't generate the managed->native wrapper and then fails + // when in AOT-only mode. + NativeFunctionWrapper wrapper = Marshal.GetDelegateForFunctionPointer(address); + return wrapper(arg1, arg2); } + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + private delegate int NativeFunctionWrapper(int arg1, int arg2); } From 6bc7f4c302724cd8ecebafbfa916f2b662f638c8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 18 Mar 2022 10:56:27 -0700 Subject: [PATCH 34/35] Clean up CMakeLists.txt for NativeLibrary tests --- .../Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt index e0ec4028d03a44..89cae1fd0d9c4d 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt @@ -1,12 +1,11 @@ project (NativeLibrary) include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") -set(SOURCES ) # add the shared library add_library (NativeLibrary SHARED NativeLibrary.cpp) if (CLR_CMAKE_TARGET_UNIX) - target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL} dl) + target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL}) endif() # add the shared library From 4266aabae21f4fd39c72d285afecf525606a9fee Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 18 Mar 2022 12:26:44 -0700 Subject: [PATCH 35/35] Add a test for freeing the result of GetMainProgramHandle --- .../Interop/NativeLibrary/API/GetMainProgramHandleTests.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs index 437b3586c1af45..694446fc8a9b98 100644 --- a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs +++ b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs @@ -95,6 +95,13 @@ public static void GloballyLoadedLibrarySymbolsVisibleFromMainProgramHandle_Mang } } + [Fact] + public static void FreeMainProgramHandle() + { + NativeLibrary.Free(NativeLibrary.GetMainProgramHandle()); + Assert.True(true); + } + public static bool IsHostedByCoreRun { get; } = Process.GetCurrentProcess().MainModule.ModuleName is "corerun" or "corerun.exe"; static TestResult GetSymbolFromMainProgramHandle(string scenarioName, string symbolToLoadFromHandle)