diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.IsQemuDetected.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.IsQemuDetected.cs
new file mode 100644
index 0000000000000..08a717736f735
--- /dev/null
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.IsQemuDetected.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+using System.Threading;
+
+internal static partial class Interop
+{
+ internal static partial class Sys
+ {
+ [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_IsQemuDetected")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static partial bool IsQemuDetectedImpl();
+
+ private static int s_isQemuDetected;
+
+ internal static bool IsQemuDetected()
+ {
+ int isQemuDetected = Interlocked.CompareExchange(ref s_isQemuDetected, 0, 0);
+ if (isQemuDetected == 0)
+ {
+ isQemuDetected = IsQemuDetectedImpl() ? 1 : 2;
+ int oldValue = Interlocked.CompareExchange(ref s_isQemuDetected, isQemuDetected, 0);
+ if (oldValue != 0) // a different thread has managed to update the value
+ {
+ isQemuDetected = oldValue;
+ }
+ }
+
+ return isQemuDetected == 1;
+ }
+ }
+}
diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs
index 1b4e6e3e0d184..b391cc88b51c9 100644
--- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs
+++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Unix.cs
@@ -31,6 +31,13 @@ public static partial class PlatformDetection
public static bool IsMonoLinuxArm64 => IsMonoRuntime && IsLinux && IsArm64Process;
public static bool IsNotMonoLinuxArm64 => !IsMonoLinuxArm64;
+ public static bool IsQemuDetected =>
+#if TARGET_WINDOWS || TARGET_BROWSER || TARGET_WASI
+ false;
+#else
+ Interop.Sys.IsQemuDetected();
+#endif
+
// OSX family
public static bool IsApplePlatform => IsOSX || IsiOS || IstvOS || IsMacCatalyst;
public static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
diff --git a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
index 1566f163697bc..58e97718c79ba 100644
--- a/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
+++ b/src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
@@ -95,6 +95,8 @@
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.OpenSslGetProtocolSupport.cs" />
+
diff --git a/src/native/libs/System.Native/CMakeLists.txt b/src/native/libs/System.Native/CMakeLists.txt
index 4cac1051f6739..fd716df561991 100644
--- a/src/native/libs/System.Native/CMakeLists.txt
+++ b/src/native/libs/System.Native/CMakeLists.txt
@@ -47,6 +47,7 @@ if (NOT CLR_CMAKE_TARGET_WASI)
pal_signal.c
pal_threading.c
pal_uid.c
+ "${CLR_SRC_NATIVE_DIR}/minipal/cpufeatures.c"
)
else()
list (APPEND NATIVE_SOURCES
diff --git a/src/native/libs/System.Native/entrypoints.c b/src/native/libs/System.Native/entrypoints.c
index 51c761109159b..0a5b9437082e3 100644
--- a/src/native/libs/System.Native/entrypoints.c
+++ b/src/native/libs/System.Native/entrypoints.c
@@ -224,6 +224,7 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_GetUnixRelease)
DllImportEntry(SystemNative_GetUnixVersion)
DllImportEntry(SystemNative_GetOSArchitecture)
+ DllImportEntry(SystemNative_IsQemuDetected)
DllImportEntry(SystemNative_SearchPath)
DllImportEntry(SystemNative_SearchPath_TempDirectory)
DllImportEntry(SystemNative_RegisterForSigChld)
diff --git a/src/native/libs/System.Native/pal_runtimeinformation.c b/src/native/libs/System.Native/pal_runtimeinformation.c
index aa18e7000d148..e5b4d35638141 100644
--- a/src/native/libs/System.Native/pal_runtimeinformation.c
+++ b/src/native/libs/System.Native/pal_runtimeinformation.c
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#if defined(TARGET_ANDROID)
#include
#elif defined(TARGET_OSX)
@@ -151,3 +152,12 @@ int32_t SystemNative_GetOSArchitecture(void)
return result;
#endif
}
+
+int32_t SystemNative_IsQemuDetected(void)
+{
+#ifdef TARGET_WASI
+ return 0;
+#else
+ return minipal_detect_qemu() ? 1 : 0;
+#endif
+}
diff --git a/src/native/libs/System.Native/pal_runtimeinformation.h b/src/native/libs/System.Native/pal_runtimeinformation.h
index 5f6dc7ab8e2d9..e0105046d701a 100644
--- a/src/native/libs/System.Native/pal_runtimeinformation.h
+++ b/src/native/libs/System.Native/pal_runtimeinformation.h
@@ -11,3 +11,5 @@ PALEXPORT char* SystemNative_GetUnixRelease(void);
PALEXPORT int32_t SystemNative_GetUnixVersion(char* version, int* capacity);
PALEXPORT int32_t SystemNative_GetOSArchitecture(void);
+
+PALEXPORT int32_t SystemNative_IsQemuDetected(void);
diff --git a/src/native/minipal/cpufeatures.c b/src/native/minipal/cpufeatures.c
index 47327255e40e2..e55e3f8a51da6 100644
--- a/src/native/minipal/cpufeatures.c
+++ b/src/native/minipal/cpufeatures.c
@@ -11,7 +11,7 @@
#include "cpufeatures.h"
#include "cpuid.h"
-#if HOST_WINDOWS
+#ifdef HOST_WINDOWS
#include
@@ -28,6 +28,11 @@
#include
#include
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-macros"
+#endif
+
// Light-up for hardware capabilities that are not present in older headers used by the portable build.
#ifndef HWCAP_ASIMDRDM
#define HWCAP_ASIMDRDM (1 << 12)
@@ -45,6 +50,14 @@
#define HWCAP_SVE (1 << 22)
#endif
+#ifndef XSTATE_MASK_AVX512
+#define XSTATE_MASK_AVX512 (0xE0) /* 0b1110_0000 */
+#endif // XSTATE_MASK_AVX512
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
#endif
#if HAVE_SYSCTLBYNAME
@@ -56,7 +69,7 @@
#if defined(HOST_UNIX)
#if defined(HOST_X86) || defined(HOST_AMD64)
-static uint32_t xmmYmmStateSupport()
+static uint32_t xmmYmmStateSupport(void)
{
uint32_t eax;
__asm(" xgetbv\n" \
@@ -68,11 +81,7 @@ static uint32_t xmmYmmStateSupport()
return ((eax & 0x06) == 0x06) ? 1 : 0;
}
-#ifndef XSTATE_MASK_AVX512
-#define XSTATE_MASK_AVX512 (0xE0) /* 0b1110_0000 */
-#endif // XSTATE_MASK_AVX512
-
-static uint32_t avx512StateSupport()
+static uint32_t avx512StateSupport(void)
{
#if defined(HOST_APPLE)
// MacOS has specialized behavior where it reports AVX512 support but doesnt
@@ -99,12 +108,12 @@ static uint32_t avx512StateSupport()
#endif
}
-static bool IsAvxEnabled()
+static bool IsAvxEnabled(void)
{
return true;
}
-static bool IsAvx512Enabled()
+static bool IsAvx512Enabled(void)
{
return true;
}
@@ -222,11 +231,11 @@ int minipal_getcpufeatures(void)
if (IsAvx512Enabled() && (avx512StateSupport() == 1)) // XGETBV XRC0[7:5] == 111
{
- if (((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0) && // AVX512F
- ((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0) && // AVX512BW
- ((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0) && // AVX512CD
- ((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0) && // AVX512DQ
- ((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0)) // AVX512VL
+ if ((((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 16)) != 0) && // AVX512F
+ (((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 30)) != 0) && // AVX512BW
+ (((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 28)) != 0) && // AVX512CD
+ (((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 17)) != 0) && // AVX512DQ
+ (((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 31)) != 0)) // AVX512VL
{
// While the AVX-512 ISAs can be individually lit-up, they really
// need F, BW, CD, DQ, and VL to be fully functional without adding
@@ -301,12 +310,12 @@ int minipal_getcpufeatures(void)
}
}
- __cpuid(cpuidInfo, 0x80000000);
+ __cpuid(cpuidInfo, (int)0x80000000);
uint32_t maxCpuIdEx = (uint32_t)cpuidInfo[CPUID_EAX];
if (maxCpuIdEx >= 0x80000001)
{
- __cpuid(cpuidInfo, 0x80000001);
+ __cpuid(cpuidInfo, (int)0x80000001);
if ((cpuidInfo[CPUID_ECX] & (1 << 5)) != 0) // LZCNT
{
@@ -441,35 +450,90 @@ int minipal_getcpufeatures(void)
return result;
}
-// Detect if the current process is running under the Apple Rosetta x64 emulator
-bool minipal_detect_rosetta(void)
+static bool GetCpuBrand(char* brand, size_t bufferSize)
{
#if defined(HOST_AMD64) || defined(HOST_X86)
// Check for CPU brand indicating emulation
int regs[4];
- char brand[49];
// Get the maximum value for extended function CPUID info
__cpuid(regs, (int)0x80000000);
if ((unsigned int)regs[0] < 0x80000004)
{
- return false; // Extended CPUID not supported
+ brand[0] = '\0'; // Extended CPUID not supported, return empty string or handle error
+ return false;
}
- // Retrieve the CPU brand string
+ // Retrieve the CPU brand string directly into the caller-provided buffer
for (unsigned int i = 0x80000002; i <= 0x80000004; ++i)
{
__cpuid(regs, (int)i);
memcpy(brand + (i - 0x80000002) * sizeof(regs), regs, sizeof(regs));
}
- brand[sizeof(brand) - 1] = '\0';
+
+ brand[bufferSize - 1] = '\0';
+
+ return true;
+#else
+ (void)brand;
+ (void)bufferSize;
+ return false;
+#endif // HOST_AMD64 || HOST_X86
+}
+
+// Detect if the current process is running under the Apple Rosetta x64 emulator
+bool minipal_detect_rosetta(void)
+{
+ char brand[49];
+
+ // Check if CPU brand indicates emulation
+ return GetCpuBrand(brand, sizeof(brand)) && (strstr(brand, "VirtualApple") != NULL);
+}
+
+#if !defined(HOST_WINDOWS)
+
+// Detect if the current process is running under QEMU
+bool minipal_detect_qemu(void)
+{
+ char brand[49];
// Check if CPU brand indicates emulation
- if (strstr(brand, "VirtualApple") != NULL)
+ if (GetCpuBrand(brand, sizeof(brand)) && strstr(brand, "QEMU") != NULL)
{
return true;
}
-#endif // HOST_AMD64 || HOST_X86
+
+ // Check for process name of PID 1 indicating emulation
+ char cmdline[256];
+ FILE *cmdline_file = fopen("/proc/1/cmdline", "r");
+ if (cmdline_file != NULL)
+ {
+ fgets(cmdline, sizeof(cmdline), cmdline_file);
+ fclose(cmdline_file);
+
+ if (strstr(cmdline, "qemu-") != NULL)
+ {
+ return true;
+ }
+ }
+
+ // Check for process-level emulation using ps command
+ FILE *ps_output = popen("ps -e -o comm=", "r");
+ if (ps_output != NULL)
+ {
+ char process_name[256];
+ while (fgets(process_name, sizeof(process_name), ps_output) != NULL)
+ {
+ if (strstr(process_name, "qemu") != NULL)
+ {
+ pclose(ps_output);
+ return true;
+ }
+ }
+ pclose(ps_output);
+ }
return false;
}
+
+#endif // !HOST_WINDOWS
diff --git a/src/native/minipal/cpufeatures.h b/src/native/minipal/cpufeatures.h
index 52527a4565036..f0222e4a98fd0 100644
--- a/src/native/minipal/cpufeatures.h
+++ b/src/native/minipal/cpufeatures.h
@@ -66,6 +66,7 @@ extern "C"
int minipal_getcpufeatures(void);
bool minipal_detect_rosetta(void);
+bool minipal_detect_qemu(void);
#ifdef __cplusplus
}