Implement NativeLibrary.GetEntryPointModuleHandle (#57610)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Mon, 21 Mar 2022 17:08:25 +0000 (10:08 -0700)
committerGitHub <noreply@github.com>
Mon, 21 Mar 2022 17:08:25 +0000 (10:08 -0700)
24 files changed:
src/coreclr/hosts/corerun/CMakeLists.txt
src/coreclr/hosts/corerun/corerun.cpp
src/coreclr/hosts/corerun/corerun.hpp
src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
src/coreclr/vm/nativelibrarynative.cpp
src/libraries/Common/src/Interop/Unix/System.Native/Interop.DynamicLoad.cs
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.cs
src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
src/mono/mono/metadata/native-library.c
src/native/libs/System.Native/entrypoints.c
src/native/libs/System.Native/pal_dynamicload.c
src/native/libs/System.Native/pal_dynamicload.h
src/tests/Common/CoreCLRTestLibrary/Utilities.cs
src/tests/Interop/NativeLibrary/API/GetLibraryExportTests.cs [new file with mode: 0644]
src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs [new file with mode: 0644]
src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs
src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj
src/tests/Interop/NativeLibrary/API/TestHelpers.cs [new file with mode: 0644]
src/tests/Interop/NativeLibrary/NativeLibraryToLoad/CMakeLists.txt
src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GlobalLoadHelper.cpp [new file with mode: 0644]
src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GloballyLoadedNativeLibrary.cpp [new file with mode: 0644]
src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibrary.cpp
src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs

index b95f4c8..c5ce086 100644 (file)
@@ -8,6 +8,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
   dotenv.cpp
@@ -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)
index 67a43e2..60b5c55 100644 (file)
@@ -564,16 +564,13 @@ 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)
+extern "C" DLL_EXPORT HRESULT CDECL GetCurrentClrDetails(void** clrInstance, unsigned int* appDomainId)
 {
     assert(clrInstance != nullptr && appDomainId != nullptr);
     *clrInstance = CurrentClrInstance;
     *appDomainId = CurrentAppDomainId;
     return S_OK;
 }
-#endif // TARGET_WINDOWS
 
 //
 // Self testing for corerun.
index 14cb9ec..c2f9ae4 100644 (file)
@@ -47,8 +47,10 @@ namespace pal
 }
 
 #ifdef TARGET_WINDOWS
+#define CDECL __cdecl
 #include <Windows.h>
 
+#define DLL_EXPORT __declspec(dllexport)
 #define MAIN __cdecl wmain
 #define W(str) L ## str
 
@@ -316,10 +318,16 @@ public:
 #include <config.h>
 #include <minipal/getexepath.h>
 
+#if __GNUC__ >= 4
+#define DLL_EXPORT __attribute__ ((visibility ("default")))
+#else
+#define DLL_EXPORT
+#endif
+#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
@@ -327,6 +335,8 @@ public:
 #define DT_LNK 10
 #endif
 
+typedef int HRESULT;
+
 namespace pal
 {
     using char_t = char;
index 523b6b8..a4d2dd6 100644 (file)
     <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.SchedGetCpu.cs">
       <Link>Interop\Unix\System.Native\Interop.SchedGetCpu.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.DynamicLoad.cs">
-      <Link>Interop\Unix\System.Native\Interop.DynamicLoad.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Threading.cs">
       <Link>Interop\Unix\System.Native\Interop.Threading.cs</Link>
     </Compile>
index bce6ea6..c459452 100644 (file)
@@ -70,4 +70,3 @@ extern "C" INT_PTR QCALLTYPE NativeLibrary_GetSymbol(INT_PTR handle, LPCWSTR sym
 
     return address;
 }
-
index 1bcc468..f806b69 100644 (file)
@@ -19,5 +19,8 @@ internal static partial class Interop
 
         [LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_FreeLibrary")]
         internal static partial void FreeLibrary(IntPtr handle);
+
+        [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultSearchOrderPseudoHandle", SetLastError = true)]
+        internal static partial IntPtr GetDefaultSearchOrderPseudoHandle();
     }
 }
index 3a846e7..ccebd9c 100644 (file)
     <Compile Include="$(CommonPath)Interop\Windows\User32\Interop.USEROBJECTFLAGS.cs">
       <Link>Common\Interop\Windows\User32\Interop.USEROBJECTFLAGS.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetModuleHandle.cs">
+      <Link>Common\Interop\Windows\Kernel32\Interop.GetModuleHandle.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)System\IO\FileSystem.Attributes.Windows.cs">
       <Link>Common\System\IO\FileSystem.Attributes.Windows.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.CopyFile.cs">
       <Link>Common\Interop\Unix\System.Native\Interop.CopyFile.cs</Link>
     </Compile>
+    <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.DynamicLoad.cs">
+      <Link>Common\Interop\Unix\System.Native\Interop.DynamicLoad.cs</Link>
+    </Compile>
     <Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ErrNo.cs">
       <Link>Common\Interop\Unix\System.Native\Interop.ErrNo.cs</Link>
     </Compile>
index 0cd98bb..d95a8c4 100644 (file)
@@ -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;
@@ -231,5 +232,26 @@ namespace System.Runtime.InteropServices
 
             return resolver(libraryName, assembly, hasDllImportSearchPathFlags ? (DllImportSearchPath?)dllImportSearchPathFlags : null);
         }
+
+        /// <summary>
+        /// Get a handle that can be used with <see cref="GetExport" /> or <see cref="TryGetExport" /> to resolve exports from the entry point module.
+        /// </summary>
+        /// <returns> The handle that can be used to resolve exports from the entry point module.</returns>
+        public static IntPtr GetMainProgramHandle()
+        {
+            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)
+            {
+                throw new Win32Exception(Marshal.GetLastPInvokeError());
+            }
+            return result;
+        }
     }
 }
index 72b9191..663c8ad 100644 (file)
@@ -784,6 +784,7 @@ namespace System.Runtime.InteropServices
     public static partial class NativeLibrary
     {
         public static void Free(System.IntPtr handle) { }
+        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; }
index a1fc8b4..6882348 100644 (file)
@@ -757,13 +757,27 @@ netcore_check_alc_cache (MonoAssemblyLoadContext *alc, const char *scope)
        return result;
 }
 
+static 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)
 {
        MonoDl *module = NULL;
        MonoDl *cached;
        MonoAssembly *assembly = mono_image_get_assembly (image);
-       char *error_msg = NULL;
 
        MONO_REQ_GC_UNSAFE_MODE;
 
@@ -771,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();
        }
 
        /*
@@ -1219,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:
index 9587c9e..7b764d5 100644 (file)
@@ -234,6 +234,7 @@ static const Entry s_sysNative[] =
     DllImportEntry(SystemNative_LoadLibrary)
     DllImportEntry(SystemNative_GetProcAddress)
     DllImportEntry(SystemNative_FreeLibrary)
+    DllImportEntry(SystemNative_GetDefaultSearchOrderPseudoHandle)
     DllImportEntry(SystemNative_SchedGetCpu)
     DllImportEntry(SystemNative_Exit)
     DllImportEntry(SystemNative_Abort)
index 20ac558..39f6714 100644 (file)
@@ -50,3 +50,20 @@ void SystemNative_FreeLibrary(void* handle)
 {
     dlclose(handle);
 }
+
+#ifdef TARGET_ANDROID
+void* SystemNative_GetDefaultSearchOrderPseudoHandle(void)
+{
+    return (void*)RTLD_DEFAULT;
+}
+#else
+static void* g_defaultSearchOrderPseudoHandle = NULL;
+void* SystemNative_GetDefaultSearchOrderPseudoHandle(void)
+{
+    if (g_defaultSearchOrderPseudoHandle == NULL)
+    {
+        g_defaultSearchOrderPseudoHandle = dlopen(NULL, RTLD_LAZY);
+    }
+    return g_defaultSearchOrderPseudoHandle;
+}
+#endif
index 857f45d..71ce8bf 100644 (file)
@@ -11,3 +11,5 @@ PALEXPORT void* SystemNative_LoadLibrary(const char* filename);
 PALEXPORT void* SystemNative_GetProcAddress(void* handle, const char* symbol);
 
 PALEXPORT void SystemNative_FreeLibrary(void* handle);
+
+PALEXPORT void* SystemNative_GetDefaultSearchOrderPseudoHandle(void);
index ad4d5ba..0ab8a56 100644 (file)
@@ -59,6 +59,7 @@ namespace TestLibrary
         }
 
         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 (file)
index 0000000..4f0453d
--- /dev/null
@@ -0,0 +1,96 @@
+// 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.IsNotX86))]
+    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;
+        });
+    }
+
+    private static unsafe int RunExportedFunction(IntPtr address, int arg1, int 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<NativeFunctionWrapper>(address);
+        return wrapper(arg1, arg2);
+    }
+
+    [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+    private delegate int NativeFunctionWrapper(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 (file)
index 0000000..694446f
--- /dev/null
@@ -0,0 +1,134 @@
+// 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;
+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]
+    [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());
+        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))]
+    [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), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary")));
+
+        try
+        {
+            EXPECT(GetSymbolFromMainProgramHandle("GloballyLoadedNativeLib", "NativeMultiply"));
+        }
+        finally
+        {
+            NativeLibrary.Free(handle);
+        }
+    }
+
+    [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNotX86))]
+    [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), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary")));
+
+        try
+        {
+            EXPECT(GetSymbolFromMainProgramHandle("GloballyLoadedNativeLib", "NonNativeMultiply"), TestResult.ReturnFailure);
+        }
+        finally
+        {
+            NativeLibrary.Free(handle);
+        }
+    }
+
+    [ConditionalFact(typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsX86))]
+    [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), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary")));
+
+        try
+        {
+            EXPECT(GetSymbolFromMainProgramHandle("GloballyLoadedNativeLib", "_NativeMultiply@8"));
+        }
+        finally
+        {
+            NativeLibrary.Free(handle);
+        }
+    }
+
+    [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)
+    {
+        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;
+        });
+    }
+
+    static IntPtr LoadLibraryGlobally(string name)
+    {
+        IntPtr handle = LoadLibraryGloballyNative(name);
+
+        if (handle == IntPtr.Zero)
+        {
+            throw new Win32Exception(Marshal.GetLastPInvokeError());
+        }
+
+        return handle;
+
+        [DllImport("GlobalLoadHelper", EntryPoint = "LoadLibraryGlobally", SetLastError = true)]
+        static extern IntPtr LoadLibraryGloballyNative(string name);
+    }
+}
index a85e163..e464159 100644 (file)
 // 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;
 
-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), TestResult.DllNotFound);
+        EXPECT(TryLoadLibrary_NameOnly(libName), TestResult.ReturnFailure);
+    }
+    
+    [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);
-        }
-        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;
-        }
+    [Fact]
+    public void LoadLibraryFullPath_WithAssembly()
+    {
+        string libName = libFullPath;
+        EXPECT(LoadLibrary_WithAssembly(libName, assembly, null));
+        EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null));
+    }
 
-        return (success) ? 100 : -100;
+    [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);
     }
 
-    static bool EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success)
+    [Fact]
+    public void LoadLibraryOnInvalidFile_WithAssembly()
     {
-        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, "NativeLibrary.cpp");
+        EXPECT(LoadLibrary_WithAssembly(libName, assembly, null),
+                OperatingSystem.IsWindows() ? TestResult.BadImage : TestResult.DllNotFound);
+        EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null), TestResult.ReturnFailure);
     }
 
-    static TestResult Run (Func<TestResult> test)
+    [Fact]
+    public void LoadLibraryNameOnly_WithAssembly()
     {
-        TestResult result;
+        string libName = NativeLibraryToLoad.Name;
+        EXPECT(LoadLibrary_WithAssembly(libName, assembly, null));
+        EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null));
+    }
 
-        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)
+    [Fact]
+    public void LoadLibraryFileName_WithAssembly()
+    {
+        string libName = NativeLibraryToLoad.GetFileName();
+        EXPECT(LoadLibrary_WithAssembly(libName, assembly, null));
+        EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null));
+    }
+
+    [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));
+    }
+
+    [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 = Path.Combine(testBinDir, NativeLibraryToLoad.Name);
+        EXPECT(LoadLibrary_WithAssembly(libName, assembly, null), TestResult.DllNotFound);
+        EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, null), TestResult.ReturnFailure);
+    }
+
+    public static bool HasKnownLibraryInSystemDirectory => 
+        OperatingSystem.IsWindows()
+        && File.Exists(Path.Combine(Environment.SystemDirectory, "url.dll"));
+
+    [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);
+    }
+
+    [Fact]
+    public void LoadLibrary_NullLibName()
+    {
+        EXPECT(LoadLibrary_WithAssembly(null, assembly, null), TestResult.ArgumentNull);
+        EXPECT(TryLoadLibrary_WithAssembly(null, assembly, null), TestResult.ArgumentNull);
+    }
+
+    [Fact]
+    public void LoadLibrary_NullAssembly()
+    {
+        string libName = NativeLibraryToLoad.Name;
+        EXPECT(LoadLibrary_WithAssembly(libName, null, null), TestResult.ArgumentNull);
+        EXPECT(TryLoadLibrary_WithAssembly(libName, null, null), TestResult.ArgumentNull);
+    }
+
+    [Fact]
+    public void LoadLibary_UsesFullPath_EvenWhen_AssemblyDirectory_Specified()
+    {
+        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);
+    }
+
+    [Fact]
+    public void Free()
+    {
+        string libName = libFullPath;
+        IntPtr handle = NativeLibrary.Load(libName);
+
+        // 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(() => {
@@ -265,10 +189,8 @@ public class NativeLibraryTest
         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(() => {
@@ -286,10 +208,8 @@ public class NativeLibraryTest
     }
 
 
-    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(() => {
@@ -304,10 +224,8 @@ public class NativeLibraryTest
         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(() => {
@@ -326,45 +244,9 @@ public class NativeLibraryTest
 
     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;
-        });
-    }
-
-    [DllImport(NativeLibraryToLoad.Name)]
-    static extern int RunExportedFunction(IntPtr address, int arg1, int arg2);
 }
index bff4466..50c9834 100644 (file)
@@ -1,6 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <OutputType>Exe</OutputType>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <ItemGroup>
diff --git a/src/tests/Interop/NativeLibrary/API/TestHelpers.cs b/src/tests/Interop/NativeLibrary/API/TestHelpers.cs
new file mode 100644 (file)
index 0000000..25663d0
--- /dev/null
@@ -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<TestResult> 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;
+    }
+}
index 8d9c552..89cae1f 100644 (file)
@@ -1,10 +1,16 @@
 project (NativeLibrary)
 include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
-set(SOURCES NativeLibrary.cpp)
 
 # add the shared library
-add_library (NativeLibrary SHARED ${SOURCES})
-target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL})
+add_library (NativeLibrary SHARED NativeLibrary.cpp)
 
-# add the install targets
-install (TARGETS NativeLibrary DESTINATION bin)
+if (CLR_CMAKE_TARGET_UNIX)
+  target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL})
+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 (file)
index 0000000..2716346
--- /dev/null
@@ -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 <stdio.h>
+#include <platformdefines.h>
+#ifndef TARGET_WINDOWS
+#include <dlfcn.h>
+#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/GloballyLoadedNativeLibrary.cpp b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/GloballyLoadedNativeLibrary.cpp
new file mode 100644 (file)
index 0000000..0b8a924
--- /dev/null
@@ -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 <stdio.h>
+#include <platformdefines.h>
+
+extern "C" DLL_EXPORT int NativeMultiply(int a, int b)
+{
+    return a * b;
+}
index 6b556c0..99f867e 100644 (file)
@@ -2,16 +2,11 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 #include <stdio.h>
 #include <platformdefines.h>
+#ifndef TARGET_WINDOWS
+#include <dlfcn.h>
+#endif
 
 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<int(*)(int,int)>(function);
-   return f(arg1, arg2);
-}
-
-
index 4a61c59..21dc1ca 100644 (file)
@@ -13,14 +13,19 @@ public class NativeLibraryToLoad
 
     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();
     }