summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSwaroop Sridhar <Swaroop.Sridhar@microsoft.com>2018-11-21 11:42:01 -0800
committerGitHub <noreply@github.com>2018-11-21 11:42:01 -0800
commit175ba1c0794958bb7d006544b87e00675de742fc (patch)
treee5ef7a67f6c11a7407465661155036b958d6beb3
parent5ef00810b53dcb7cbc4f2cb152ca6af971284e82 (diff)
downloadcoreclr-175ba1c0794958bb7d006544b87e00675de742fc.tar.gz
coreclr-175ba1c0794958bb7d006544b87e00675de742fc.tar.bz2
coreclr-175ba1c0794958bb7d006544b87e00675de742fc.zip
Introduce Marshall.LoadLibrary API (#20871)
Implement Native LoadLibrary API This change commits the following changes: 1) Refactoring DllImport code to reuse LodLibrary by search for pInvoke and LoadLibrary cases 2) Implement the new Native Library API in System.Runtime.Interop.Marshall 3) Add tests for the new APIs
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs128
-rw-r--r--src/dlls/mscorrc/mscorrc.rc2
-rw-r--r--src/dlls/mscorrc/resource.h5
-rw-r--r--src/pal/inc/pal.h19
-rw-r--r--src/pal/src/include/pal/module.h14
-rw-r--r--src/pal/src/loader/module.cpp55
-rw-r--r--src/vm/assemblynative.cpp2
-rw-r--r--src/vm/dllimport.cpp190
-rw-r--r--src/vm/dllimport.h12
-rw-r--r--src/vm/ecalllist.h6
-rw-r--r--src/vm/marshalnative.cpp62
-rw-r--r--src/vm/marshalnative.h11
-rw-r--r--tests/src/Common/CoreCLRTestLibrary/Utilities.cs11
-rw-r--r--tests/src/Interop/CMakeLists.txt1
-rw-r--r--tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt13
-rw-r--r--tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp18
-rw-r--r--tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs379
-rw-r--r--tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj45
18 files changed, 911 insertions, 62 deletions
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
index b4f4dfa461..e6a293700d 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs
@@ -1760,5 +1760,133 @@ namespace System.Runtime.InteropServices
RuntimeImports.RhZeroMemory(s, (UIntPtr)(Win32Native.lstrlenW(s) * 2));
FreeHGlobal(s);
}
+
+ /// APIs for managing Native Libraries
+
+ /// <summary>
+ /// NativeLibrary Loader: Simple API
+ /// This method is a wrapper around OS loader, using "default" flags.
+ /// </summary>
+ /// <param name="libraryName">The name of the native library to be loaded</param>
+ /// <returns>The handle for the loaded native library</returns>
+ /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+ /// <exception cref="System.DllNotFoundException ">Thrown if the library can't be found.</exception>
+ /// <exception cref="System.BadImageFormatException">Thrown if the library is not valid.</exception>
+ public static IntPtr LoadLibrary(string libraryPath)
+ {
+ bool throwOnError = true;
+ return LoadLibraryFromPath(libraryPath, throwOnError);
+ }
+
+ /// <summary>
+ /// NativeLibrary Loader: Simple API that doesn't throw
+ /// </summary>
+ /// <param name="libraryName">The name of the native library to be loaded</param>
+ /// <param name="handle">The out-parameter for the loaded native library handle</param>
+ /// <returns>True on successful load, false otherwise</returns>
+ /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+ public static bool TryLoadLibrary(string libraryPath, out IntPtr handle)
+ {
+ bool throwOnError = false;
+ handle = LoadLibraryFromPath(libraryPath, throwOnError);
+ return handle != IntPtr.Zero;
+ }
+
+ /// <summary>
+ /// NativeLibrary Loader: High-level API
+ /// Given a library name, this function searches specific paths based on the
+ /// runtime configuration and attributes of the calling module.
+ /// This LoadLibrary() method does not invoke the managed call-backs for native library resolution:
+ /// * AssemblyLoadContext.LoadUnmanagedDll()
+ /// </summary>
+ /// <param name="libraryName">The name of the native library to be loaded</param>
+ /// <param name="dllImportSearchPath">The search path</param>
+ /// <param name="assembly">The assembly loading the native library</param>
+ /// <returns>The handle for the loaded library</returns>
+ /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+ /// <exception cref="System.ArgumentNullException">If assembly is null</exception>
+ /// <exception cref="System.DllNotFoundException ">Thrown if the library can't be found.</exception>
+ /// <exception cref="System.BadImageFormatException">Thrown if the library is not valid.</exception>
+ public static IntPtr LoadLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ RuntimeAssembly runtimeAssembly = (assembly != null) ? ((RuntimeAssembly)assembly).GetNativeHandle() : null;
+ bool throwOnError = true;
+ uint searchPathFlag = searchPath.HasValue ? (uint)searchPath.Value : 0;
+ return LoadLibraryByName(libraryName, runtimeAssembly, searchPath.HasValue, searchPathFlag, throwOnError);
+ }
+
+ /// <summary>
+ /// NativeLibrary Loader: High-level API that doesn't throw.
+ /// </summary>
+ /// <param name="libraryName">The name of the native library to be loaded</param>
+ /// <param name="dllImportSearchPath">The search path</param>
+ /// <param name="assembly">The assembly loading the native library</param>
+ /// <param name="handle">The out-parameter for the loaded native library handle</param>
+ /// <returns>True on successful load, false otherwise</returns>
+ /// <exception cref="System.ArgumentNullException">If libraryPath is null</exception>
+ /// <exception cref="System.ArgumentNullException">If assembly is null and dllImportSearchPath includes AssemblyDirectory</exception>
+ public static bool TryLoadLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle)
+ {
+ RuntimeAssembly runtimeAssembly = (assembly != null) ? ((RuntimeAssembly)assembly).GetNativeHandle() : null;
+ bool throwOnError = false;
+ uint searchPathFlag = searchPath.HasValue ? (uint)searchPath.Value : 0;
+ handle = LoadLibraryByName(libraryName, runtimeAssembly, searchPath.HasValue, searchPathFlag, throwOnError);
+ return handle != IntPtr.Zero;
+ }
+
+ /// <summary>
+ /// Free a loaded library
+ /// Given a library handle, free it.
+ /// No action if the input handle is null.
+ /// </summary>
+ /// <param name="handle">The native library handle to be freed</param>
+ /// <exception cref="System.InvalidOperationException">If the operation fails</exception>
+ public static void FreeLibrary(IntPtr handle)
+ {
+ FreeNativeLibrary(handle);
+ }
+
+ /// <summary>
+ /// Get the address of an exported Symbol
+ /// This is a simple wrapper around OS calls, and does not perform any name mangling.
+ /// </summary>
+ /// <param name="handle">The native library handle</param>
+ /// <param name="name">The name of the exported symbol</param>
+ /// <returns>The address of the symbol</returns>
+ /// <exception cref="System.ArgumentNullException">If handle or name is null</exception>
+ /// <exception cref="System.ArgumentException">If name cannot be converted to UTF8</exception>
+ /// <exception cref="System.EntryPointNotFoundException">If the symbol is not found</exception>
+ public static IntPtr GetLibraryExport(IntPtr handle, string name)
+ {
+ bool throwOnError = true;
+ return GetNativeLibraryExport(handle, name, throwOnError);
+ }
+
+ /// <summary>
+ /// Get the address of an exported Symbol, but do not throw
+ /// </summary>
+ /// <param name="handle">The native library handle</param>
+ /// <param name="name">The name of the exported symbol</param>
+ /// <param name="address"> The out-parameter for the symbol address, if it exists</param>
+ /// <returns>True on success, false otherwise</returns>
+ public static bool TryGetLibraryExport(IntPtr handle, string name, out IntPtr address)
+ {
+ bool throwOnError = false;
+ address = GetNativeLibraryExport(handle, name, throwOnError);
+ return address != IntPtr.Zero;
+ }
+
+ /// External functions that implement the NativeLibrary interface
+
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ internal static extern IntPtr LoadLibraryFromPath(string libraryName, bool throwOnError);
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ internal static extern IntPtr LoadLibraryByName(string libraryName, RuntimeAssembly callingAssembly,
+ bool hasDllImportSearchPathFlag, uint dllImportSearchPathFlag,
+ bool throwOnError);
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ internal static extern void FreeNativeLibrary(IntPtr handle);
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ internal static extern IntPtr GetNativeLibraryExport(IntPtr handle, string symbolName, bool throwOnError);
}
}
diff --git a/src/dlls/mscorrc/mscorrc.rc b/src/dlls/mscorrc/mscorrc.rc
index bb39c9ac6f..59aa7c338f 100644
--- a/src/dlls/mscorrc/mscorrc.rc
+++ b/src/dlls/mscorrc/mscorrc.rc
@@ -541,6 +541,8 @@ BEGIN
IDS_EE_NDIRECT_LOADLIB_MAC "Unable to load shared library '%1' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: %2"
IDS_EE_NDIRECT_GETPROCADDRESS_WIN "Unable to find an entry point named '%2' in DLL '%1'."
IDS_EE_NDIRECT_GETPROCADDRESS_UNIX "Unable to find an entry point named '%2' in shared library '%1'."
+ IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL "Unable to find an entry point named '%1' in DLL."
+ IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO "Unable to find an entry point named '%1' in shared library."
IDS_EE_NDIRECT_GETPROCADDRESS_NONAME "A library name must be specified in a DllImport attribute applied to non-IJW methods."
IDS_EE_CLASS_CONSTRAINTS_VIOLATION "GenericArguments[%1], '%2', on '%3' violates the constraint of type parameter '%4'."
IDS_EE_METHOD_CONSTRAINTS_VIOLATION "Method %1.%2: type argument '%3' violates the constraint of type parameter '%4'."
diff --git a/src/dlls/mscorrc/resource.h b/src/dlls/mscorrc/resource.h
index 8c315e1abe..735f315c33 100644
--- a/src/dlls/mscorrc/resource.h
+++ b/src/dlls/mscorrc/resource.h
@@ -718,3 +718,8 @@
#define IDS_INVOKE_NULLREF_RETURNED 0x2642
#define IDS_EE_CANNOT_SET_INITONLY_STATIC_FIELD 0x2643
+
+#define IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL 0x2644
+#define IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO 0x2645
+
+
diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h
index 45989998e8..e304d854e9 100644
--- a/src/pal/inc/pal.h
+++ b/src/pal/inc/pal.h
@@ -2552,6 +2552,8 @@ OpenFileMappingW(
#define OpenFileMapping OpenFileMappingA
#endif
+typedef INT_PTR (PALAPI *FARPROC)();
+
PALIMPORT
LPVOID
PALAPI
@@ -2604,9 +2606,22 @@ PALIMPORT
HMODULE
PALAPI
PAL_RegisterLibraryDirect(
- IN void *dl_handle,
+ IN NATIVE_LIBRARY_HANDLE dl_handle,
IN LPCWSTR lpLibFileName);
+PALIMPORT
+BOOL
+PALAPI
+PAL_FreeLibraryDirect(
+ IN NATIVE_LIBRARY_HANDLE dl_handle);
+
+PALIMPORT
+FARPROC
+PALAPI
+PAL_GetProcAddressDirect(
+ IN NATIVE_LIBRARY_HANDLE dl_handle,
+ IN LPCSTR lpProcName);
+
/*++
Function:
PAL_LOADLoadPEFile
@@ -2650,8 +2665,6 @@ PAL_LOADUnloadPEFile(void * ptr);
#define LoadLibraryEx LoadLibraryExA
#endif
-typedef INT_PTR (PALAPI *FARPROC)();
-
PALIMPORT
FARPROC
PALAPI
diff --git a/src/pal/src/include/pal/module.h b/src/pal/src/include/pal/module.h
index 72df268d3c..aacc326c64 100644
--- a/src/pal/src/include/pal/module.h
+++ b/src/pal/src/include/pal/module.h
@@ -31,13 +31,13 @@ typedef VOID (PALAPI *PUNREGISTER_MODULE)(HINSTANCE); /* used to clean
typedef struct _MODSTRUCT
{
- HMODULE self; /* circular reference to this module */
- void *dl_handle; /* handle returned by dlopen() */
- HINSTANCE hinstance; /* handle returned by PAL_RegisterLibrary */
- LPWSTR lib_name; /* full path of module */
- INT refcount; /* reference count */
- /* -1 means infinite reference count - module is never released */
- BOOL threadLibCalls; /* TRUE for DLL_THREAD_ATTACH/DETACH notifications enabled, FALSE if they are disabled */
+ HMODULE self; /* circular reference to this module */
+ NATIVE_LIBRARY_HANDLE dl_handle; /* handle returned by dlopen() */
+ HINSTANCE hinstance; /* handle returned by PAL_RegisterLibrary */
+ LPWSTR lib_name; /* full path of module */
+ INT refcount; /* reference count */
+ /* -1 means infinite reference count - module is never released */
+ BOOL threadLibCalls; /* TRUE for DLL_THREAD_ATTACH/DETACH notifications enabled, FALSE if they are disabled */
#if RETURNS_NEW_HANDLES_ON_REPEAT_DLOPEN
ino_t inode;
diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp
index 04e41c38e5..3f246895a8 100644
--- a/src/pal/src/loader/module.cpp
+++ b/src/pal/src/loader/module.cpp
@@ -630,7 +630,7 @@ PAL_LoadLibraryDirect(
dl_handle = LOADLoadLibraryDirect(lpcstr);
done:
- LOGEXIT("LoadLibraryDirect returns HMODULE %p\n", dl_handle);
+ LOGEXIT("LoadLibraryDirect returns NATIVE_LIBRARY_HANDLE %p\n", dl_handle);
PERF_EXIT(LoadLibraryDirect);
return dl_handle;
}
@@ -689,6 +689,59 @@ done:
return hModule;
}
+/*
+Function:
+ PAL_FreeLibraryDirect
+
+ Free a loaded library
+
+ Returns true on success, false on failure.
+*/
+BOOL
+PALAPI
+PAL_FreeLibraryDirect(
+ IN NATIVE_LIBRARY_HANDLE dl_handle)
+{
+ BOOL retValue = 0;
+ PERF_ENTRY(PAL_FreeLibraryDirect);
+ ENTRY("PAL_FreeLibraryDirect (dl_handle=%p) \n", dl_handle);
+
+ retValue = dlclose(dl_handle) == 0;
+
+ LOGEXIT("PAL_FreeLibraryDirect returns BOOL %p\n", retValue);
+ PERF_EXIT(PAL_FreeLibraryDirect);
+ return retValue;
+}
+
+/*
+Function:
+ PAL_GetProcAddressDirect
+
+ Get the address corresponding to a symbol in a loaded native library.
+
+ Returns the address of the sumbol loaded in memory.
+*/
+FARPROC
+PALAPI
+PAL_GetProcAddressDirect(
+ IN NATIVE_LIBRARY_HANDLE dl_handle,
+ IN LPCSTR lpProcName)
+{
+ INT name_length;
+ FARPROC address = nullptr;
+
+ PERF_ENTRY(PAL_GetProcAddressDirect);
+ ENTRY("PAL_GetProcAddressDirect (lpLibFileName=%p (%S)) \n",
+ lpProcName ? lpProcName : "NULL",
+ lpProcName ? lpProcName : "NULL");
+
+ address = (FARPROC) dlsym(dl_handle, lpProcName);
+
+ LOGEXIT("PAL_GetProcAddressDirect returns FARPROC %p\n", address);
+ PERF_EXIT(PAL_GetProcAddressDirect);
+ return address;
+}
+
/*++
Function:
PAL_RegisterModule
diff --git a/src/vm/assemblynative.cpp b/src/vm/assemblynative.cpp
index f95a18125d..afa46457d8 100644
--- a/src/vm/assemblynative.cpp
+++ b/src/vm/assemblynative.cpp
@@ -314,7 +314,7 @@ INT_PTR QCALLTYPE AssemblyNative::InternalLoadUnmanagedDllFromPath(LPCWSTR unman
BEGIN_QCALL;
- moduleHandle = NDirect::LoadLibraryFromPath(unmanagedLibraryPath);
+ moduleHandle = NDirect::LoadLibraryFromPath(unmanagedLibraryPath, true);
END_QCALL;
diff --git a/src/vm/dllimport.cpp b/src/vm/dllimport.cpp
index 9ad41c55c8..18f404949a 100644
--- a/src/vm/dllimport.cpp
+++ b/src/vm/dllimport.cpp
@@ -48,11 +48,14 @@
#endif // FEATURE_PREJIT
#include "eventtrace.h"
-
-
#include "clr/fs/path.h"
using namespace clr::fs;
+// The Bit 0x2 has different semantics in DllImportSearchPath and LoadLibraryExA flags.
+// In DllImportSearchPath enum, bit 0x2 represents SearchAssemblyDirectory -- which is performed by CLR.
+// Unlike other bits in this enum, this bit shouldn't be directly passed on to LoadLibrary()
+#define DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY 0x2
+
// remove when we get an updated SDK
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
@@ -6069,7 +6072,6 @@ private:
SString m_message;
}; // class LoadLibErrorTracker
-// Local helper function to load a library.
// Load the library directly. On Unix systems, don't register it yet with PAL.
// * External callers like AssemblyNative::InternalLoadUnmanagedDllFromPath() and the upcoming
// System.Runtime.Interop.Marshall.LoadLibrary() need the raw system handle
@@ -6132,14 +6134,18 @@ bool NDirect::s_fSecureLoadLibrarySupported = false;
#endif // !FEATURE_PAL
// static
-NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath)
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError)
{
STANDARD_VM_CONTRACT;
+ if (libraryPath == NULL)
+ COMPlusThrowArgumentNull(W("libraryPath"), W("ArgumentNull_String"));
+
LoadLibErrorTracker errorTracker;
const NATIVE_LIBRARY_HANDLE hmod =
LocalLoadLibraryHelper(libraryPath, GetLoadWithAlteredSearchPathFlag(), &errorTracker);
- if (hmod == nullptr)
+
+ if (throwOnError && (hmod == nullptr))
{
SString libraryPathSString(libraryPath);
errorTracker.Throw(libraryPathSString);
@@ -6147,8 +6153,133 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryFromPath(LPCWSTR libraryPath)
return hmod;
}
-/* static */
-NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, const wchar_t* wszLibName)
+// static
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryByName(LPCWSTR libraryName, Assembly *callingAssembly,
+ BOOL hasDllImportSearchFlag, DWORD dllImportSearchFlag,
+ BOOL throwOnError)
+{
+ STANDARD_VM_CONTRACT;
+
+ LoadLibErrorTracker errorTracker;
+
+ if (libraryName == NULL)
+ COMPlusThrowArgumentNull(W("libraryName"), W("ArgumentNull_String"));
+
+ if (callingAssembly == NULL)
+ COMPlusThrowArgumentNull(W("callingAssembly"), W("ArgumentNull_Assembly"));
+
+ // First checks if a default DllImportSearchPathFlag was passed in, if so, use that value.
+ // Otherwise checks if the assembly has the DefaultDllImportSearchPathsAttribute attribute. If so, use that value.
+ BOOL searchAssemblyDirectory = TRUE;
+ DWORD dllImportSearchPathFlag = 0;
+
+ if (hasDllImportSearchFlag)
+ {
+ dllImportSearchPathFlag = dllImportSearchFlag & ~DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
+ searchAssemblyDirectory = dllImportSearchFlag & DLLIMPORTSEARCHPATH_ASSEMBLYDIRECTORY;
+
+ }
+ else
+ {
+ Module * pModule = callingAssembly->GetManifestModule();
+
+ if (pModule->HasDefaultDllImportSearchPathsAttribute())
+ {
+ dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
+ searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
+ }
+ }
+
+ NATIVE_LIBRARY_HANDLE hmod =
+ LoadLibraryModuleBySearch(callingAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, &errorTracker, libraryName);
+
+ if (throwOnError && (hmod == nullptr))
+ {
+ SString libraryPathSString(libraryName);
+ errorTracker.Throw(libraryPathSString);
+ }
+
+ return hmod;
+}
+
+// static
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, PCWSTR wszLibName)
+{
+ STANDARD_VM_CONTRACT;
+
+ // First checks if the method has DefaultDllImportSearchPathsAttribute. If so, use that value.
+ // Otherwise checks if the assembly has the attribute. If so, use that value.
+ BOOL searchAssemblyDirectory = TRUE;
+ DWORD dllImportSearchPathFlag = 0;
+
+ if (pMD->HasDefaultDllImportSearchPathsAttribute())
+ {
+ dllImportSearchPathFlag = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
+ searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
+ }
+ else
+ {
+ Module * pModule = pMD->GetModule();
+
+ if (pModule->HasDefaultDllImportSearchPathsAttribute())
+ {
+ dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
+ searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
+ }
+ }
+
+ Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
+ return LoadLibraryModuleBySearch(pAssembly, searchAssemblyDirectory, dllImportSearchPathFlag, pErrorTracker, wszLibName);
+}
+
+// static
+void NDirect::FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle)
+{
+ STANDARD_VM_CONTRACT;
+
+ // FreeLibrary doesn't throw if the input is null.
+ // This avoids further null propagation/check while freeing resources (ex: in finally blocks)
+ if (handle == NULL)
+ return;
+
+#ifndef FEATURE_PAL
+ BOOL retVal = FreeLibrary(handle);
+#else // !FEATURE_PAL
+ BOOL retVal = PAL_FreeLibraryDirect(handle);
+#endif // !FEATURE_PAL
+
+ if (retVal == 0)
+ COMPlusThrow(kInvalidOperationException, W("Arg_InvalidOperationException"));
+}
+
+//static
+INT_PTR NDirect::GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError)
+{
+ STANDARD_VM_CONTRACT;
+
+ if (handle == NULL)
+ COMPlusThrowArgumentNull(W("handle"), W("Arg_InvalidHandle"));
+
+ if (symbolName == NULL)
+ COMPlusThrowArgumentNull(W("symbolName"), W("ArgumentNull_String"));
+
+ MAKE_UTF8PTR_FROMWIDE(lpstr, symbolName);
+
+#ifndef FEATURE_PAL
+ INT_PTR address = reinterpret_cast<INT_PTR>(GetProcAddress((HMODULE)handle, lpstr));
+ if ((address == NULL) && throwOnError)
+ COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_WIN_DLL, symbolName);
+#else // !FEATURE_PAL
+ INT_PTR address = reinterpret_cast<INT_PTR>(PAL_GetProcAddressDirect((NATIVE_LIBRARY_HANDLE)handle, lpstr));
+ if ((address == NULL) && throwOnError)
+ COMPlusThrow(kEntryPointNotFoundException, IDS_EE_NDIRECT_GETPROCADDR_UNIX_SO, symbolName);
+#endif // !FEATURE_PAL
+
+ return address;
+}
+
+// static
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, PCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
//Dynamic Pinvoke Support:
@@ -6376,12 +6507,14 @@ static void DetermineLibNameVariations(const WCHAR** libNameVariations, int* num
#endif // FEATURE_PAL
// Search for the library and variants of its name in probing directories.
-NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName)
+//static
+NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(Assembly *callingAssembly,
+ BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag,
+ LoadLibErrorTracker * pErrorTracker, LPCWSTR wszLibName)
{
STANDARD_VM_CONTRACT;
-
+
NATIVE_LIBRARY_HANDLE hmod = NULL;
- AppDomain* pDomain = GetAppDomain();
#if defined(FEATURE_CORESYSTEM) && !defined(PLATFORM_UNIX)
// Try to go straight to System32 for Windows API sets. This is replicating quick check from
@@ -6396,9 +6529,10 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD
}
#endif // FEATURE_CORESYSTEM && !FEATURE_PAL
+ AppDomain* pDomain = GetAppDomain();
DWORD loadWithAlteredPathFlags = GetLoadWithAlteredSearchPathFlag();
bool libNameIsRelativePath = Path::IsRelative(wszLibName);
- DWORD dllImportSearchPathFlag = 0;
+
// P/Invokes are often declared with variations on the actual library name.
// For example, it's common to leave off the extension/suffix of the library
// even if it has one, or to leave off a prefix like "lib" even if it has one
@@ -6419,31 +6553,6 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD
return hmod;
}
- // First checks if the method has DefaultDllImportSearchPathsAttribute. If method has the attribute
- // then dllImportSearchPathFlag is set to its value.
- // Otherwise checks if the assembly has the attribute.
- // If assembly has the attribute then flag is set to its value.
- BOOL searchAssemblyDirectory = TRUE;
- BOOL attributeIsFound = FALSE;
-
- if (pMD->HasDefaultDllImportSearchPathsAttribute())
- {
- dllImportSearchPathFlag = pMD->DefaultDllImportSearchPathsAttributeCachedValue();
- searchAssemblyDirectory = pMD->DllImportSearchAssemblyDirectory();
- attributeIsFound = TRUE;
- }
- else
- {
- Module * pModule = pMD->GetModule();
-
- if (pModule->HasDefaultDllImportSearchPathsAttribute())
- {
- dllImportSearchPathFlag = pModule->DefaultDllImportSearchPathsAttributeCachedValue();
- searchAssemblyDirectory = pModule->DllImportSearchAssemblyDirectory();
- attributeIsFound = TRUE;
- }
- }
-
if (!libNameIsRelativePath)
{
DWORD flags = loadWithAlteredPathFlags;
@@ -6460,10 +6569,9 @@ NATIVE_LIBRARY_HANDLE NDirect::LoadLibraryModuleBySearch(NDirectMethodDesc * pMD
return hmod;
}
}
- else if (searchAssemblyDirectory)
+ else if ((callingAssembly != nullptr) && searchAssemblyDirectory)
{
- Assembly* pAssembly = pMD->GetMethodTable()->GetAssembly();
- hmod = LoadFromPInvokeAssemblyDirectory(pAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker);
+ hmod = LoadFromPInvokeAssemblyDirectory(callingAssembly, currLibNameVariation, loadWithAlteredPathFlags | dllImportSearchPathFlag, pErrorTracker);
if (hmod != NULL)
{
return hmod;
@@ -6547,8 +6655,6 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke
hmod = pDomain->FindUnmanagedImageInCache(wszLibName);
if (hmod != NULL)
{
- // AppDomain caches the PAL_registered handles
- // So, no need to PAL_Register the handle obtained from the cache
return hmod.Extract();
}
@@ -6574,7 +6680,7 @@ HINSTANCE NDirect::LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracke
hmod = PAL_RegisterLibraryDirect(hmod, wszLibName);
#endif // FEATURE_PAL
}
- }
+ }
if (hmod != NULL)
{
diff --git a/src/vm/dllimport.h b/src/vm/dllimport.h
index 05336b43c6..2f25e59570 100644
--- a/src/vm/dllimport.h
+++ b/src/vm/dllimport.h
@@ -5,9 +5,6 @@
// File: DllImport.h
//
-//
-
-
#ifndef __dllimport_h__
#define __dllimport_h__
@@ -77,9 +74,13 @@ public:
static HRESULT HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken token, DWORD dwMemberAttrs);
static LPVOID NDirectGetEntryPoint(NDirectMethodDesc *pMD, HINSTANCE hMod);
- static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryFromPath(LPCWSTR libraryPath, BOOL throwOnError);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryByName(LPCWSTR name, Assembly *callingAssembly,
+ BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag,
+ BOOL throwOnError);
static HINSTANCE LoadLibraryModule(NDirectMethodDesc * pMD, LoadLibErrorTracker *pErrorTracker);
-
+ static void FreeNativeLibrary(NATIVE_LIBRARY_HANDLE handle);
+ static INT_PTR GetNativeLibraryExport(NATIVE_LIBRARY_HANDLE handle, LPCWSTR symbolName, BOOL throwOnError);
static VOID NDirectLink(NDirectMethodDesc *pMD);
@@ -125,6 +126,7 @@ private:
static NATIVE_LIBRARY_HANDLE LoadFromPInvokeAssemblyDirectory(Assembly *pAssembly, LPCWSTR libName, DWORD flags, LoadLibErrorTracker *pErrorTracker);
static NATIVE_LIBRARY_HANDLE LoadLibraryModuleViaHost(NDirectMethodDesc * pMD, AppDomain* pDomain, const wchar_t* wszLibName);
static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(NDirectMethodDesc * pMD, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
+ static NATIVE_LIBRARY_HANDLE LoadLibraryModuleBySearch(Assembly *callingAssembly, BOOL searchAssemblyDirectory, DWORD dllImportSearchPathFlag, LoadLibErrorTracker * pErrorTracker, const wchar_t* wszLibName);
#if !defined(FEATURE_PAL)
// Indicates if the OS supports the new secure LoadLibraryEx flags introduced in KB2533623
diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h
index 9c8ef0b9b4..f7c5a791c3 100644
--- a/src/vm/ecalllist.h
+++ b/src/vm/ecalllist.h
@@ -863,6 +863,12 @@ FCFuncStart(gInteropMarshalFuncs)
FCFuncElement("GetExceptionForHRInternal", MarshalNative::GetExceptionForHR)
FCFuncElement("GetDelegateForFunctionPointerInternal", MarshalNative::GetDelegateForFunctionPointerInternal)
FCFuncElement("GetFunctionPointerForDelegateInternal", MarshalNative::GetFunctionPointerForDelegateInternal)
+
+ QCFuncElement("LoadLibraryFromPath", MarshalNative::LoadLibraryFromPath)
+ QCFuncElement("LoadLibraryByName", MarshalNative::LoadLibraryByName)
+ QCFuncElement("FreeNativeLibrary", MarshalNative::FreeNativeLibrary)
+ QCFuncElement("GetNativeLibraryExport", MarshalNative::GetNativeLibraryExport)
+
#ifdef FEATURE_COMINTEROP
FCFuncElement("GetHRForException", MarshalNative::GetHRForException)
FCFuncElement("GetHRForException_WinRT", MarshalNative::GetHRForException_WinRT)
diff --git a/src/vm/marshalnative.cpp b/src/vm/marshalnative.cpp
index a356900756..117b66d6cb 100644
--- a/src/vm/marshalnative.cpp
+++ b/src/vm/marshalnative.cpp
@@ -923,6 +923,68 @@ FCIMPL1(int, MarshalNative::GetHRForException_WinRT, Object* eUNSAFE)
}
FCIMPLEND
+// static
+INT_PTR QCALLTYPE MarshalNative::LoadLibraryFromPath(LPCWSTR path, BOOL throwOnError)
+{
+ QCALL_CONTRACT;
+
+ NATIVE_LIBRARY_HANDLE handle = nullptr;
+
+ BEGIN_QCALL;
+
+ handle = NDirect::LoadLibraryFromPath(path, throwOnError);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(handle);
+}
+
+// static
+INT_PTR QCALLTYPE MarshalNative::LoadLibraryByName(LPCWSTR name, QCall::AssemblyHandle callingAssembly,
+ BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag,
+ BOOL throwOnError)
+{
+ QCALL_CONTRACT;
+
+ NATIVE_LIBRARY_HANDLE handle = nullptr;
+ Assembly *pAssembly = (callingAssembly != NULL) ? callingAssembly->GetAssembly() : NULL;
+
+ BEGIN_QCALL;
+
+ handle = NDirect::LoadLibraryByName(name, pAssembly, hasDllImportSearchPathFlag, dllImportSearchPathFlag, throwOnError);
+
+ END_QCALL;
+
+ return reinterpret_cast<INT_PTR>(handle);
+}
+
+// static
+void QCALLTYPE MarshalNative::FreeNativeLibrary(INT_PTR handle)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+
+ NDirect::FreeNativeLibrary((NATIVE_LIBRARY_HANDLE) handle);
+
+ END_QCALL;
+}
+
+//static
+INT_PTR QCALLTYPE MarshalNative::GetNativeLibraryExport(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError)
+{
+ QCALL_CONTRACT;
+
+ INT_PTR address = NULL;
+
+ BEGIN_QCALL;
+
+ address = NDirect::GetNativeLibraryExport((NATIVE_LIBRARY_HANDLE)handle, symbolName, throwOnError);
+
+ END_QCALL;
+
+ return address;
+}
#ifdef FEATURE_COMINTEROP
diff --git a/src/vm/marshalnative.h b/src/vm/marshalnative.h
index 25c1dc3ab9..9b6aa2cf9a 100644
--- a/src/vm/marshalnative.h
+++ b/src/vm/marshalnative.h
@@ -85,6 +85,17 @@ public:
static FCDECL2(Object*, GetDelegateForFunctionPointerInternal, LPVOID FPtr, ReflectClassBaseObject* refTypeUNSAFE);
static FCDECL1(LPVOID, GetFunctionPointerForDelegateInternal, Object* refDelegateUNSAFE);
+
+ //====================================================================
+ // These methods provide the native callbacks for library loading APIs
+ //====================================================================
+ static INT_PTR QCALLTYPE LoadLibraryFromPath(LPCWSTR path, BOOL throwOnError);
+ static INT_PTR QCALLTYPE LoadLibraryByName(LPCWSTR name, QCall::AssemblyHandle callingAssembly,
+ BOOL hasDllImportSearchPathFlag, DWORD dllImportSearchPathFlag,
+ BOOL throwOnError);
+ static void QCALLTYPE FreeNativeLibrary(INT_PTR handle);
+ static INT_PTR QCALLTYPE GetNativeLibraryExport(INT_PTR handle, LPCWSTR symbolName, BOOL throwOnError);
+
#ifdef FEATURE_COMINTEROP
//====================================================================
// map GUID to Type
diff --git a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs
index 5b36bd2d74..d1ef171349 100644
--- a/tests/src/Common/CoreCLRTestLibrary/Utilities.cs
+++ b/tests/src/Common/CoreCLRTestLibrary/Utilities.cs
@@ -57,10 +57,15 @@ namespace TestLibrary
}
}
- public static bool IsWindows => (Path.DirectorySeparatorChar == '\\');
-
+ public static bool IsX86 => (RuntimeInformation.ProcessArchitecture == Architecture.X86);
+ public static bool IsX64 => (RuntimeInformation.ProcessArchitecture == Architecture.X64);
+ public static bool IsArm => (RuntimeInformation.ProcessArchitecture == Architecture.Arm);
+ public static bool IsArm64 => (RuntimeInformation.ProcessArchitecture == Architecture.Arm64);
+
+ public static bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ public static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ public static bool IsMacOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static bool IsWindowsNanoServer => (!IsWindowsIoTCore && GetInstallationType().Equals("Nano Server", StringComparison.OrdinalIgnoreCase));
-
public static bool IsWindowsIoTCore
{
get
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index bfebf738a2..a1a55e77ae 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -54,6 +54,7 @@ add_subdirectory(StringMarshalling/AnsiBSTR)
add_subdirectory(StringMarshalling/VBByRefStr)
add_subdirectory(MarshalAPI/FunctionPointer)
add_subdirectory(MarshalAPI/IUnknown)
+add_subdirectory(MarshalAPI/NativeLibrary)
add_subdirectory(SizeConst)
add_subdirectory(DllImportAttribute/ExeFile)
add_subdirectory(DllImportAttribute/FileNameContainDot)
diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt b/tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt
new file mode 100644
index 0000000000..f7d98ba507
--- /dev/null
+++ b/tests/src/Interop/MarshalAPI/NativeLibrary/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required (VERSION 2.6)
+project (NativeLibrary)
+include_directories(${INC_PLATFORM_DIR})
+set(SOURCES NativeLibrary.cpp)
+
+# add the executable
+add_library (NativeLibrary SHARED ${SOURCES})
+target_link_libraries(NativeLibrary ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS NativeLibrary DESTINATION bin)
+
+
diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp
new file mode 100644
index 0000000000..7c1090f10f
--- /dev/null
+++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibrary.cpp
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+#include <stdio.h>
+#include <xplatform.h>
+
+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);
+}
+
+
diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs
new file mode 100644
index 0000000000..7ef2afbb45
--- /dev/null
+++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.cs
@@ -0,0 +1,379 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+using Console = Internal.Console;
+
+enum TestResult {
+ Success,
+ ReturnFailure,
+ ReturnNull,
+ IncorrectEvaluation,
+ ArgumentNull,
+ ArgumentBad,
+
+ DllNotFound,
+ BadImage,
+ InvalidOperation,
+ EntryPointNotFound,
+ GenericException
+ };
+
+public class NativeLibraryTest
+{
+ static string CurrentTest;
+ static bool Verbose = false;
+
+ public static int Main()
+ {
+ bool success = true;
+
+ Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
+ string testBinDir = Path.GetDirectoryName(assembly.Location);
+ string libName;
+ IntPtr handle;
+
+ // -----------------------------------------------
+ // Simple LoadLibrary() API Tests
+ // -----------------------------------------------
+
+ // Calls on correct full-path to native lib
+ libName = Path.Combine(testBinDir, GetNativeLibraryName());
+ 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 = Path.Combine(testBinDir, GetNativeLibraryName());
+ 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 = GetNativeLibraryPlainName();
+ success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null));
+ success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null));
+
+ // Calls on Native Library name with correct prefix-suffix
+ libName = GetNativeLibraryName();
+ success &= EXPECT(LoadLibraryAdvanced(libName, assembly, null));
+ success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, null));
+
+ // Calls on full path without prefix-siffix
+ libName = Path.Combine(testBinDir, GetNativeLibraryPlainName());
+ // 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);
+
+ if (TestLibrary.Utilities.IsWindows)
+ {
+ // Calls on a valid library from System32 directory
+ libName = GetNativeLibraryPlainName();
+ success &= EXPECT(LoadLibraryAdvanced("mapi32", assembly, DllImportSearchPath.System32));
+ success &= EXPECT(TryLoadLibraryAdvanced("mapi32", assembly, DllImportSearchPath.System32));
+
+ // Calls on a valid library from application directory
+ success &= EXPECT(LoadLibraryAdvanced("mapi32", assembly, DllImportSearchPath.ApplicationDirectory), TestResult.DllNotFound);
+ success &= EXPECT(TryLoadLibraryAdvanced("mapi32", 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 = GetNativeLibraryPlainName();
+ 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", GetNativeLibraryPlainName()));
+ success &= EXPECT(LoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.DllNotFound);
+ success &= EXPECT(TryLoadLibraryAdvanced(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.ReturnFailure);
+
+ // -----------------------------------------------
+ // FreeLibrary Tests
+ // -----------------------------------------------
+
+ libName = Path.Combine(testBinDir, GetNativeLibraryName());
+ handle = Marshal.LoadLibrary(libName);
+
+ // Valid Free
+ success &= EXPECT(FreeLibrary(handle));
+
+ // Double Free
+ success &= EXPECT(FreeLibrary(handle), TestResult.InvalidOperation);
+
+ // Null Free
+ success &= EXPECT(FreeLibrary(IntPtr.Zero));
+
+ // -----------------------------------------------
+ // GetLibraryExport Tests
+ // -----------------------------------------------
+ libName = Path.Combine(testBinDir, GetNativeLibraryName());
+ handle = Marshal.LoadLibrary(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);
+
+ Marshal.FreeLibrary(handle);
+
+ return (success) ? 100 : -100;
+ }
+
+ static string GetNativeLibraryPlainName()
+ {
+ return "NativeLibrary";
+ }
+
+ static string GetNativeLibraryName()
+ {
+ string baseName = GetNativeLibraryPlainName();
+
+ if (TestLibrary.Utilities.IsWindows)
+ {
+ return baseName + ".dll";
+ }
+ if (TestLibrary.Utilities.IsLinux)
+ {
+ return "lib" + baseName + ".so";
+ }
+ if (TestLibrary.Utilities.IsMacOSX)
+ {
+ return "lib" + baseName + ".dylib";
+ }
+
+ return "ERROR";
+ }
+
+ static bool EXPECT(TestResult actualValue, TestResult expectedValue = TestResult.Success)
+ {
+ 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;
+ }
+ }
+
+ static TestResult Run (Func<TestResult> test)
+ {
+
+ TestResult result;
+
+ try
+ {
+ result = test();
+ }
+ catch (ArgumentNullException e)
+ {
+ return TestResult.ArgumentNull;
+ }
+ catch (ArgumentException e)
+ {
+ return TestResult.ArgumentBad;
+ }
+ catch (DllNotFoundException e)
+ {
+ return TestResult.DllNotFound;
+ }
+ catch (BadImageFormatException e)
+ {
+ return TestResult.BadImage;
+ }
+ catch (InvalidOperationException e)
+ {
+ return TestResult.InvalidOperation;
+ }
+ catch (EntryPointNotFoundException e)
+ {
+ return TestResult.EntryPointNotFound;
+ }
+ catch (Exception e)
+ {
+ //Console.WriteLine(e.ToString());
+ return TestResult.GenericException;
+ }
+
+ return result;
+ }
+
+ static TestResult LoadLibrarySimple(string libPath)
+ {
+ CurrentTest = String.Format("LoadLibrary({0})", libPath);
+
+ IntPtr handle = IntPtr.Zero;
+
+ TestResult result = Run(() => {
+ handle = Marshal.LoadLibrary(libPath);
+ if (handle == IntPtr.Zero)
+ return TestResult.ReturnNull;
+ return TestResult.Success;
+ });
+
+ Marshal.FreeLibrary(handle);
+
+ return result;
+ }
+
+ static TestResult TryLoadLibrarySimple(string libPath)
+ {
+ CurrentTest = String.Format("TryLoadLibrary({0})", libPath);
+
+ IntPtr handle = IntPtr.Zero;
+
+ TestResult result = Run(() => {
+ bool success = Marshal.TryLoadLibrary(libPath, out handle);
+ if(!success)
+ return TestResult.ReturnFailure;
+ if (handle == null)
+ return TestResult.ReturnNull;
+ return TestResult.Success;
+ });
+
+ Marshal.FreeLibrary(handle);
+
+ return result;
+ }
+
+
+ static TestResult LoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ CurrentTest = String.Format("LoadLibrary({0}, {1}, {2})", libName, assembly, searchPath);
+
+ IntPtr handle = IntPtr.Zero;
+
+ TestResult result = Run(() => {
+ handle = Marshal.LoadLibrary(libName, assembly, searchPath);
+ if (handle == IntPtr.Zero)
+ return TestResult.ReturnNull;
+ return TestResult.Success;
+ });
+
+ Marshal.FreeLibrary(handle);
+
+ return result;
+ }
+
+ static TestResult TryLoadLibraryAdvanced(string libName, Assembly assembly, DllImportSearchPath? searchPath)
+ {
+ CurrentTest = String.Format("TryLoadLibrary({0}, {1}, {2})", libName, assembly, searchPath);
+
+ IntPtr handle = IntPtr.Zero;
+
+ TestResult result = Run(() => {
+ bool success = Marshal.TryLoadLibrary(libName, assembly, searchPath, out handle);
+ if (!success)
+ return TestResult.ReturnFailure;
+ if (handle == IntPtr.Zero)
+ return TestResult.ReturnNull;
+ return TestResult.Success;
+ });
+
+ Marshal.FreeLibrary(handle);
+
+ return result;
+ }
+
+ static TestResult FreeLibrary(IntPtr handle)
+ {
+ CurrentTest = String.Format("FreeLibrary({0})", handle);
+
+ return Run(() => {
+ Marshal.FreeLibrary(handle);
+ return TestResult.Success;
+ });
+ }
+
+ static TestResult GetLibraryExport(IntPtr handle, string name)
+ {
+ CurrentTest = String.Format("GetLibraryExport({0}, {1})", handle, name);
+
+ return Run(() => {
+ IntPtr address = Marshal.GetLibraryExport(handle, name);
+ if (address == null)
+ 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 = Marshal.TryGetLibraryExport(handle, name, out address);
+ if (!success)
+ return TestResult.ReturnFailure;
+ if (address == null)
+ return TestResult.ReturnNull;
+ if (RunExportedFunction(address, 1, 1) != 2)
+ return TestResult.IncorrectEvaluation;
+ return TestResult.Success;
+ });
+ }
+
+ [DllImport("NativeLibrary")]
+ static extern int RunExportedFunction(IntPtr address, int arg1, int arg2);
+}
diff --git a/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj
new file mode 100644
index 0000000000..d3d7eb2be7
--- /dev/null
+++ b/tests/src/Interop/MarshalAPI/NativeLibrary/NativeLibraryTests.csproj
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>NativeLibraryTests</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{F1E66554-8C8E-4141-85CF-D0CD6A0CD0B0}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="*.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="NativeLibrary.cpp">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj">
+ <Project>{c8c0dc74-fac4-45b1-81fe-70c4808366e0}</Project>
+ <Name>CoreCLRTestLibrary</Name>
+ </ProjectReference>
+ <ProjectReference Include="CMakeLists.txt" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>