diff --git a/src/coreclr/inc/clrconfigvalues.h b/src/coreclr/inc/clrconfigvalues.h index cd29d9b9a91b49..201a8ee097b23c 100644 --- a/src/coreclr/inc/clrconfigvalues.h +++ b/src/coreclr/inc/clrconfigvalues.h @@ -323,6 +323,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TailCallLoopOpt, W("TailCallLoopOpt"), 1, "Con RETAIL_CONFIG_DWORD_INFO(EXTERNAL_Jit_NetFx40PInvokeStackResilience, W("NetFx40_PInvokeStackResilience"), (DWORD)-1, "Makes P/Invoke resilient against mismatched signature and calling convention (significant perf penalty).") RETAIL_CONFIG_DWORD_INFO(EXTERNAL_DisableOptimizedThreadStaticAccess, W("DisableOptimizedThreadStaticAccess"), (DWORD)0, "Disable the OptimizedThreadStaticAccess feature.") CONFIG_DWORD_INFO(EXTERNAL_AssertNotStaticTlsResolver, W("AssertNotStaticTlsResolver"), (DWORD)0, "Assert if we attempt to use the static tls resolver path.") +CONFIG_DWORD_INFO(EXTERNAL_AssertStaticTlsResolver, W("AssertStaticTlsResolver"), (DWORD)0, "Assert if the static tls resolver path is not available.") // AltJitAssertOnNYI should be 0 on targets where JIT is under development or bring up stage, so as to facilitate fallback to main JIT on hitting a NYI. #if defined(TARGET_X86) diff --git a/src/coreclr/vm/threadstatics.cpp b/src/coreclr/vm/threadstatics.cpp index d5ae02973ac091..17468cc502a743 100644 --- a/src/coreclr/vm/threadstatics.cpp +++ b/src/coreclr/vm/threadstatics.cpp @@ -1035,6 +1035,12 @@ bool CanJITOptimizeTLSAccess() // Optimization is disabled for OpenBSD, which has no addressable __tls_get_addr. #elif !defined(TARGET_APPLE) && defined(TARGET_UNIX) && (defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)) bool tlsResolverValid = IsValidTLSResolver(); +#ifdef _DEBUG + if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_AssertStaticTlsResolver) != 0) + { + _ASSERTE(tlsResolverValid && "Static TLS resolver is not available"); + } +#endif // _DEBUG if (tlsResolverValid) { optimizeThreadStaticAccess = true; diff --git a/src/tests/JIT/Directed/tls/TestTLSWithLoadedDlls.cs b/src/tests/JIT/Directed/tls/TestTLSWithLoadedDlls.cs index 18385bb14cd612..ae84ef1128ad12 100644 --- a/src/tests/JIT/Directed/tls/TestTLSWithLoadedDlls.cs +++ b/src/tests/JIT/Directed/tls/TestTLSWithLoadedDlls.cs @@ -2,9 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. -// This test is verifying that the runtime properly handles the cases where the TLS infra in the runtime is forced -// to use a dynamic resolver. This is done by means of a private config variable to validate the behavior on Linux Arm64 -// and a set of multithreaded tasks, that has been known to cause the runtime to crash when this is handled incorrectly. +// This test verifies that the runtime uses a static TLS resolver by default on Linux Arm64 and properly handles +// the case where loading additional TLS-using libraries forces it to use a dynamic resolver. using System; using System.Diagnostics; @@ -23,6 +22,15 @@ static class TLSWithLoadedDlls { private const int CountOfLibTlsToLoad = 40; + [ThreadStatic] + private static int t_threadStatic; + + static int AccessThreadStatic() + { + t_threadStatic = 100; + return t_threadStatic; + } + static async Task DoLotsOfAsyncWork(int loopCount) { for (int i = 0; i < loopCount; i++) @@ -48,6 +56,23 @@ static int Main(string[] args) return 100; } + if ((args.Length == 1) && (args[0] == "AccessThreadStatic")) + { + return AccessThreadStatic(); + } + + string testAssemblyPath = Assembly.GetExecutingAssembly().Location; + if (OperatingSystem.IsLinux() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + int exitCode = RunChildProcess( + $"{testAssemblyPath} AccessThreadStatic", + "DOTNET_AssertStaticTlsResolver"); + if (exitCode != 100) + { + return exitCode; + } + } + int CountOfLibTlsToLoad = 60; if (OperatingSystem.IsWindows()) // Windows does not have a really long command line length limit, and doesn't have a problem with many TLS using images used @@ -79,14 +104,19 @@ static int Main(string[] args) } arguments.Append(' '); - arguments.Append(System.Reflection.Assembly.GetExecutingAssembly().Location); + arguments.Append(testAssemblyPath); arguments.Append(" RunLotsOfTasks"); + return RunChildProcess(arguments.ToString(), "DOTNET_AssertNotStaticTlsResolver"); + } + + private static int RunChildProcess(string arguments, string assertionVariable) + { Process process = new Process(); process.StartInfo.FileName = GetCorerunPath(); - process.StartInfo.Arguments = arguments.ToString(); + process.StartInfo.Arguments = arguments; process.StartInfo.UseShellExecute = false; - process.StartInfo.EnvironmentVariables["DOTNET_AssertNotStaticTlsResolver"] = "1"; + process.StartInfo.EnvironmentVariables[assertionVariable] = "1"; Console.WriteLine($"Launching {process.StartInfo.FileName} {process.StartInfo.Arguments}");