diff --git a/NuGet.config b/NuGet.config index 3b47eb2e33ec79..a1337574ac916a 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,7 +9,7 @@ - + 7.0.100-rc.1.22402.1 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 2.5.1-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 - 7.0.0-beta.23164.3 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 2.5.1-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 + 7.0.0-beta.23211.2 6.0.0-preview.1.102 @@ -177,7 +177,7 @@ 7.0.100-1.23207.1 $(MicrosoftNETILLinkTasksVersion) - 7.0.0-rtm.23115.1 + 7.0.0-rtm.23211.1 2.1.1 7.0.0-alpha.1.22459.1 diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh index 41a26d802a93f8..f13b74080edf9b 100644 --- a/eng/common/native/init-compiler.sh +++ b/eng/common/native/init-compiler.sh @@ -71,7 +71,7 @@ if [[ -z "$CLR_CC" ]]; then # Set default versions if [[ -z "$majorVersion" ]]; then # note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero. - if [[ "$compiler" == "clang" ]]; then versions=( 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 ) + if [[ "$compiler" == "clang" ]]; then versions=( 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5 ) elif [[ "$compiler" == "gcc" ]]; then versions=( 12 11 10 9 8 7 6 5 4.9 ); fi for version in "${versions[@]}"; do diff --git a/eng/pipelines/runtime-extra-platforms-other.yml b/eng/pipelines/runtime-extra-platforms-other.yml index 7ef2f1bee1e6a5..3f77cd342fc374 100644 --- a/eng/pipelines/runtime-extra-platforms-other.yml +++ b/eng/pipelines/runtime-extra-platforms-other.yml @@ -143,7 +143,7 @@ jobs: jobParameters: testScope: innerloop nameSuffix: AllSubsets_Mono - buildArgs: -s mono+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true + buildArgs: -s mono+mono.mscordbi+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true timeoutInMinutes: 120 condition: >- or( diff --git a/eng/pipelines/runtime-official.yml b/eng/pipelines/runtime-official.yml index caae555af012d9..569e0052ee1f28 100644 --- a/eng/pipelines/runtime-official.yml +++ b/eng/pipelines/runtime-official.yml @@ -144,7 +144,7 @@ stages: # - windows_arm # - windows_arm64 jobParameters: - buildArgs: -s mono+libs+host+packs+mono.mscordbi -c $(_BuildConfig) + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) nameSuffix: AllSubsets_Mono isOfficialBuild: ${{ variables.isOfficialBuild }} extraStepsTemplate: /eng/pipelines/common/upload-intermediate-artifacts-step.yml @@ -159,7 +159,7 @@ stages: platforms: - Browser_wasm jobParameters: - buildArgs: -s mono+libs+host+packs+mono.mscordbi -c $(_BuildConfig) /p:MonoWasmBuildVariant=perftrace + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) /p:MonoWasmBuildVariant=perftrace nameSuffix: AllSubsets_Mono_perftrace isOfficialBuild: ${{ variables.isOfficialBuild }} runtimeVariant: perftrace @@ -175,7 +175,7 @@ stages: platforms: - Browser_wasm jobParameters: - buildArgs: -s mono+libs+host+packs+mono.mscordbi -c $(_BuildConfig) /p:MonoWasmBuildVariant=multithread + buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) /p:MonoWasmBuildVariant=multithread nameSuffix: AllSubsets_Mono_multithread isOfficialBuild: ${{ variables.isOfficialBuild }} runtimeVariant: multithread diff --git a/global.json b/global.json index 4a7f1a9cdb1f6e..b9d7d81f1f2ad2 100644 --- a/global.json +++ b/global.json @@ -1,16 +1,16 @@ { "sdk": { - "version": "7.0.104", + "version": "7.0.105", "allowPrerelease": true, "rollForward": "major" }, "tools": { - "dotnet": "7.0.104" + "dotnet": "7.0.105" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23164.3", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.23164.3", - "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.23164.3", + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.23211.2", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.23211.2", + "Microsoft.DotNet.SharedFramework.Sdk": "7.0.0-beta.23211.2", "Microsoft.Build.NoTargets": "3.5.0", "Microsoft.Build.Traversal": "3.1.6", "Microsoft.NET.Sdk.IL": "7.0.0-rc.1.22414.6" diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs index 8e383b44d85033..0c1a2a520e2cf0 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs @@ -309,8 +309,8 @@ internal static unsafe void FixupModuleCell(ModuleFixupCell* pCell) hModule = NativeLibrary.LoadBySearch( callingAssembly, - searchAssemblyDirectory: false, - dllImportSearchPathFlags: 0, + searchAssemblyDirectory: (dllImportSearchPath & (uint)DllImportSearchPath.AssemblyDirectory) != 0, + dllImportSearchPathFlags: (int)(dllImportSearchPath & ~(uint)DllImportSearchPath.AssemblyDirectory), ref loadLibErrorTracker, moduleName); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs index 3708b256808e88..1c95dbc088b4fd 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.NativeAot.cs @@ -20,7 +20,7 @@ internal static IntPtr LoadLibraryByName(string libraryName, Assembly assembly, bool searchAssemblyDirectory; if (searchPath.HasValue) { - searchPathFlags = (int)(searchPath.Value & ~DllImportSearchPath.AssemblyDirectory); + searchPathFlags = (int)(searchPath!.Value & ~DllImportSearchPath.AssemblyDirectory); searchAssemblyDirectory = (searchPath.Value & DllImportSearchPath.AssemblyDirectory) != 0; } else diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs index 4971ca48763fe9..4be5ad213a1b84 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PInvokeMethodFixupNode.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Reflection.Metadata; using System.Runtime.InteropServices; using Internal.IL.Stubs; @@ -97,8 +98,16 @@ public PInvokeMethodData(PInvokeLazyFixupField pInvokeLazyFixupField) PInvokeMetadata metadata = pInvokeLazyFixupField.PInvokeMetadata; ModuleDesc declaringModule = ((MetadataType)pInvokeLazyFixupField.TargetMethod.OwningType).Module; - DllImportSearchPath? dllImportSearchPath = default; - if (declaringModule.Assembly is EcmaAssembly asm) + CustomAttributeValue? decodedAttr = null; + + // Look for DefaultDllImportSearchPath on the method + if (pInvokeLazyFixupField.TargetMethod is EcmaMethod method) + { + decodedAttr = method.GetDecodedCustomAttribute("System.Runtime.InteropServices", "DefaultDllImportSearchPathsAttribute"); + } + + // If the attribute it wasn't found on the method, look for it on the assembly + if (!decodedAttr.HasValue && declaringModule.Assembly is EcmaAssembly asm) { // We look for [assembly:DefaultDllImportSearchPaths(...)] var attrHandle = asm.MetadataReader.GetCustomAttributeHandle(asm.AssemblyDefinition.GetCustomAttributes(), @@ -106,14 +115,18 @@ public PInvokeMethodData(PInvokeLazyFixupField pInvokeLazyFixupField) if (!attrHandle.IsNil) { var attr = asm.MetadataReader.GetCustomAttribute(attrHandle); - var decoded = attr.DecodeValue(new CustomAttributeTypeProvider(asm)); - if (decoded.FixedArguments.Length == 1 && - decoded.FixedArguments[0].Value is int searchPath) - { - dllImportSearchPath = (DllImportSearchPath)searchPath; - } + decodedAttr = attr.DecodeValue(new CustomAttributeTypeProvider(asm)); } } + + DllImportSearchPath? dllImportSearchPath = default; + if (decodedAttr.HasValue + && decodedAttr.Value.FixedArguments.Length == 1 + && decodedAttr.Value.FixedArguments[0].Value is int searchPath) + { + dllImportSearchPath = (DllImportSearchPath)searchPath; + } + ModuleData = new PInvokeModuleData(metadata.Module, dllImportSearchPath, declaringModule); EntryPointName = metadata.Name; diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs index 95b3f870db5d0b..63b4a827d6790b 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs @@ -64,8 +64,21 @@ private MsQuicApi(QUIC_API_TABLE* apiTable) #pragma warning disable CA1810 // Initialize all static fields in 'MsQuicApi' when those fields are declared and remove the explicit static constructor static MsQuicApi() { - if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MinMsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle) && - !NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle)) + bool loaded = false; + IntPtr msQuicHandle; + if (OperatingSystem.IsWindows()) + { + // Windows ships msquic in the assembly directory. + loaded = NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle); + } + else + { + // Non-Windows relies on the package being installed on the system and may include the version in its name + loaded = NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{MinMsQuicVersion.Major}", typeof(MsQuicApi).Assembly, null, out msQuicHandle) || + NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, null, out msQuicHandle); + } + + if (!loaded) { // MsQuic library not loaded return; diff --git a/src/mono/mono/metadata/native-library.c b/src/mono/mono/metadata/native-library.c index 164a3aef132f22..e89a741752f2af 100644 --- a/src/mono/mono/metadata/native-library.c +++ b/src/mono/mono/metadata/native-library.c @@ -533,10 +533,13 @@ netcore_probe_for_module (MonoImage *image, const char *file_name, int flags, Mo ERROR_DECL (bad_image_error); - // Try without any path additions +#if defined(HOST_ANDROID) + // On Android, try without any path additions first. It is sensitive to probing that will always miss + // and lookup for some libraries is required to use a relative path module = netcore_probe_for_module_variations (NULL, file_name, lflags, error); if (!module && !is_ok (error) && mono_error_get_error_code (error) == MONO_ERROR_BAD_IMAGE) mono_error_move (bad_image_error, error); +#endif // Check the NATIVE_DLL_SEARCH_DIRECTORIES for (int i = 0; i < pinvoke_search_directories_count && module == NULL; ++i) { @@ -560,6 +563,16 @@ netcore_probe_for_module (MonoImage *image, const char *file_name, int flags, Mo g_free (mdirname); } +#if !defined(HOST_ANDROID) + // Try without any path additions + if (module == NULL) + { + module = netcore_probe_for_module_variations (NULL, file_name, lflags, error); + if (!module && !is_ok (error) && mono_error_get_error_code (error) == MONO_ERROR_BAD_IMAGE) + mono_error_move (bad_image_error, error); + } +#endif + // TODO: Pass remaining flags on to LoadLibraryEx on Windows where appropriate, see https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.dllimportsearchpath?view=netcore-3.1 if (!module && !is_ok (bad_image_error)) { diff --git a/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs b/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs index 429d637b28de79..5d15c0825d532f 100644 --- a/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs +++ b/src/tests/Common/CoreCLRTestLibrary/PlatformDetection.cs @@ -10,8 +10,12 @@ public static class PlatformDetection { public static bool Is32BitProcess => IntPtr.Size == 4; public static bool Is64BitProcess => IntPtr.Size == 8; - + public static bool IsX86Process => RuntimeInformation.ProcessArchitecture == Architecture.X86; public static bool IsNotX86Process => !IsX86Process; + + private static string _variant = Environment.GetEnvironmentVariable("DOTNET_RUNTIME_VARIANT"); + + public static bool IsMonoLLVMFULLAOT => _variant == "llvmfullaot"; } } diff --git a/src/tests/Common/testenvironment.proj b/src/tests/Common/testenvironment.proj index afa5bbecf7b684..b601b280a1ed4a 100644 --- a/src/tests/Common/testenvironment.proj +++ b/src/tests/Common/testenvironment.proj @@ -258,6 +258,8 @@ <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'monointerpreter'" Include="set MONO_ENV_OPTIONS=--interpreter" /> + <_TestEnvFileLine Condition="'$(RuntimeVariant)' != ''" Include="set DOTNET_RUNTIME_VARIANT=$(RuntimeVariant)" /> + <_TestEnvFileLine Condition="'$(Scenario)' == 'clrinterpreter'" Include="set COMPlus_Interpret=%2A" /> <_TestEnvFileLine Condition="'$(Scenario)' == 'clrinterpreter'" Include="set COMPlus_InterpreterHWIntrinsicsIsSupportedFalse=1" /> @@ -273,6 +275,8 @@ <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'monointerpreter'" Include="export MONO_ENV_OPTIONS=--interpreter" /> + <_TestEnvFileLine Condition="'$(RuntimeVariant)' != ''" Include="export DOTNET_RUNTIME_VARIANT=$(RuntimeVariant)" /> + <_TestEnvFileLine Condition="'$(RuntimeVariant)' == 'llvmaot'" Include="export MONO_ENV_OPTIONS=--llvm" /> diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 000af600e2a616..7008a3c4e0ce55 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -537,13 +537,17 @@ <_UsingDefaultForHasRuntimeOutput>false + + + + diff --git a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs new file mode 100644 index 00000000000000..31e69aeab90f72 --- /dev/null +++ b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.cs @@ -0,0 +1,66 @@ +// 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.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using Xunit; + +public class DllImportSearchPathsTest +{ + private static string Subdirectory => Path.Combine(NativeLibraryToLoad.GetDirectory(), "subdirectory"); + + [Fact] + public static void AssemblyDirectory_NotFound() + { + // Library should not be found in the assembly directory + Assert.Throws(() => NativeLibraryPInvoke.Sum(1, 2)); + } + + public static bool CanLoadAssemblyInSubdirectory => + !TestLibrary.Utilities.IsNativeAot && !TestLibrary.PlatformDetection.IsMonoLLVMFULLAOT; + + [ConditionalFact(nameof(CanLoadAssemblyInSubdirectory))] + public static void AssemblyDirectory_Found() + { + // Library should be found in the assembly directory + var assembly = Assembly.LoadFile(Path.Combine(Subdirectory, $"{nameof(DllImportSearchPathsTest)}.dll")); + var type = assembly.GetType(nameof(NativeLibraryPInvoke)); + var method = type.GetMethod(nameof(NativeLibraryPInvoke.Sum)); + + int sum = (int)method.Invoke(null, new object[] { 1, 2 }); + Assert.Equal(3, sum); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void AssemblyDirectory_Fallback_Found() + { + string currentDirectory = Environment.CurrentDirectory; + try + { + Environment.CurrentDirectory = Subdirectory; + + // Library should not be found in the assembly directory, but should fall back to the default OS search which includes CWD on Windows + int sum = NativeLibraryPInvoke.Sum(1, 2); + Assert.Equal(3, sum); + } + finally + { + Environment.CurrentDirectory = currentDirectory; + } + } +} + +public class NativeLibraryPInvoke +{ + public static int Sum(int a, int b) + { + return NativeSum(a, b); + } + + [DllImport(NativeLibraryToLoad.Name)] + [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] + static extern int NativeSum(int arg1, int arg2); +} diff --git a/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj new file mode 100644 index 00000000000000..2456564dbb9304 --- /dev/null +++ b/src/tests/Interop/DllImportSearchPaths/DllImportSearchPathsTest.csproj @@ -0,0 +1,24 @@ + + + true + + + + + + + + + + $(OutDir)/subdirectory + -in-subdirectory + + + <_FilesToCopy Include="$(OutDir)/$(TargetName).dll" /> + <_FilesToMove Include="$(OutDir)/libNativeLibrary.*" /> + <_FilesToMove Include="$(OutDir)/NativeLibrary.*" /> + + + + + diff --git a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs index 58ec31f5d60b91..edefe6e32f446a 100644 --- a/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs +++ b/src/tests/Interop/NativeLibrary/API/GetMainProgramHandleTests.cs @@ -45,7 +45,7 @@ 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"))); + IntPtr handle = LoadLibraryGlobally(Path.Combine(NativeLibraryToLoad.GetDirectory(), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary"))); try { @@ -64,7 +64,7 @@ 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"))); + IntPtr handle = LoadLibraryGlobally(Path.Combine(NativeLibraryToLoad.GetDirectory(), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary"))); try { @@ -83,7 +83,7 @@ public static void GloballyLoadedLibrarySymbolsVisibleFromMainProgramHandle_Mang // 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"))); + IntPtr handle = LoadLibraryGlobally(Path.Combine(NativeLibraryToLoad.GetDirectory(), NativeLibraryToLoad.GetLibraryFileName("GloballyLoadedNativeLibrary"))); try { diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs index 2d5f1c3734557c..f3115f5f4e4207 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.cs @@ -17,7 +17,7 @@ public class NativeLibraryTests : IDisposable public NativeLibraryTests() { assembly = System.Reflection.Assembly.GetExecutingAssembly(); - testBinDir = Path.GetDirectoryName(assembly.Location); + testBinDir = NativeLibraryToLoad.GetDirectory(); libFullPath = NativeLibraryToLoad.GetFullPath(); } @@ -133,11 +133,19 @@ public void LoadLibraryFullPathWithoutNativePrefixOrSuffix_WithAssembly_Failure( public void LoadSystemLibrary_WithSearchPath() { string libName = "url.dll"; - // Calls on a valid library from System32 directory + // Library should be found in the system directory EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.System32)); EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.System32)); - // Calls on a valid library from application directory + // Library should not be found in the assembly directory and should be found in the system directory + EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.System32)); + + // Library should not be found in the assembly directory, but should fall back to the default OS search which includes CWD on Windows + EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory)); + + // Library should not be found in application directory EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.DllNotFound); EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.ApplicationDirectory), TestResult.ReturnFailure); } @@ -165,6 +173,40 @@ public void LoadLibrary_UsesFullPath_EvenWhen_AssemblyDirectory_Specified() EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory), TestResult.ReturnFailure); } + [Fact] + public void LoadLibrary_AssemblyDirectory() + { + string suffix = "-in-subdirectory"; + string libName = $"{NativeLibraryToLoad.Name}{suffix}"; + + string subdirectory = Path.Combine(testBinDir, "subdirectory"); + + if (!TestLibrary.Utilities.IsNativeAot && !TestLibrary.PlatformDetection.IsMonoLLVMFULLAOT) + { + // Library should be found in the assembly directory + Assembly assemblyInSubdirectory = Assembly.LoadFile(Path.Combine(subdirectory, $"{Path.GetFileNameWithoutExtension(assembly.Location)}{suffix}.dll")); + EXPECT(LoadLibrary_WithAssembly(libName, assemblyInSubdirectory, DllImportSearchPath.AssemblyDirectory)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assemblyInSubdirectory, DllImportSearchPath.AssemblyDirectory)); + } + + if (OperatingSystem.IsWindows()) + { + string currentDirectory = Environment.CurrentDirectory; + try + { + Environment.CurrentDirectory = subdirectory; + + // Library should not be found in the assembly directory, but should fall back to the default OS search which includes CWD on Windows + EXPECT(LoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory)); + EXPECT(TryLoadLibrary_WithAssembly(libName, assembly, DllImportSearchPath.AssemblyDirectory)); + } + finally + { + Environment.CurrentDirectory = currentDirectory; + } + } + } + [Fact] public void Free() { diff --git a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj index 50c9834e83a168..f6dd733af6d438 100644 --- a/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj +++ b/src/tests/Interop/NativeLibrary/API/NativeLibraryTests.csproj @@ -15,4 +15,17 @@ + + + + $(OutDir)/subdirectory + -in-subdirectory + + + + + + + + diff --git a/src/tests/Interop/NativeLibrary/Callback/CallbackTests.cs b/src/tests/Interop/NativeLibrary/Callback/CallbackTests.cs index c77a187e5c0874..351b8b4d1a5a09 100644 --- a/src/tests/Interop/NativeLibrary/Callback/CallbackTests.cs +++ b/src/tests/Interop/NativeLibrary/Callback/CallbackTests.cs @@ -99,7 +99,7 @@ private IntPtr ResolveDllImport(string libraryName, Assembly asm, DllImportSearc if (string.Equals(libraryName, NativeLibraryToLoad.InvalidName)) { Assert.Equal(DllImportSearchPath.System32, dllImportSearchPath); - return NativeLibrary.Load(NativeLibraryToLoad.Name, asm, null); + return NativeLibrary.Load(NativeLibraryToLoad.GetFullPath(), asm, null); } return IntPtr.Zero; diff --git a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs index 21dc1ca1c9c986..3acc65116eab28 100644 --- a/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs +++ b/src/tests/Interop/NativeLibrary/NativeLibraryToLoad/NativeLibraryToLoad.cs @@ -32,8 +32,23 @@ public static string GetLibraryFileName(string name) public static string GetFullPath() { - Assembly assembly = Assembly.GetExecutingAssembly(); - string directory = Path.GetDirectoryName(assembly.Location); - return Path.Combine(directory, GetFileName()); + return Path.Combine(GetDirectory(), GetFileName()); + } + + public static string GetDirectory() + { + string directory; + if (TestLibrary.Utilities.IsNativeAot) + { + // NativeAOT test is put in a native/ subdirectory, so we want the parent + // directory that contains the native library to load + directory = new DirectoryInfo(AppContext.BaseDirectory).Parent.FullName; + } + else + { + directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + } + + return directory; } } diff --git a/src/tests/issues.targets b/src/tests/issues.targets index cf842bd2967bd8..db7896e9d99325 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1032,9 +1032,6 @@ https://github.com/dotnet/runtimelab/issues/166 - - https://github.com/dotnet/runtimelab/issues/206 - https://github.com/dotnet/runtimelab/issues/176: VARIANT marshalling