From 2f16613b2b3f239e452dd56fecd9f40ed6b47368 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 9 Sep 2022 23:20:34 -0700 Subject: [PATCH 1/2] Flush instruction cache after thunk pool allocation Fixes #74710 --- src/coreclr/nativeaot/Runtime/PalRedhawk.h | 1 + .../nativeaot/Runtime/ThunksMapping.cpp | 2 ++ .../nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 28 +++++++++++++++++++ .../Runtime/windows/PalRedhawkMinWin.cpp | 5 ++++ 4 files changed, 36 insertions(+) diff --git a/src/coreclr/nativeaot/Runtime/PalRedhawk.h b/src/coreclr/nativeaot/Runtime/PalRedhawk.h index 5e8751f694544e..007b3914415f01 100644 --- a/src/coreclr/nativeaot/Runtime/PalRedhawk.h +++ b/src/coreclr/nativeaot/Runtime/PalRedhawk.h @@ -674,6 +674,7 @@ EXTERN_C void * __cdecl _alloca(size_t); REDHAWK_PALIMPORT _Ret_maybenull_ _Post_writable_byte_size_(size) void* REDHAWK_PALAPI PalVirtualAlloc(_In_opt_ void* pAddress, uintptr_t size, uint32_t allocationType, uint32_t protect); REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualFree(_In_ void* pAddress, uintptr_t size, uint32_t freeType); REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddress, uintptr_t size, uint32_t protect); +REDHAWK_PALIMPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size); REDHAWK_PALIMPORT void REDHAWK_PALAPI PalSleep(uint32_t milliseconds); REDHAWK_PALIMPORT UInt32_BOOL REDHAWK_PALAPI PalSwitchToThread(); REDHAWK_PALIMPORT HANDLE REDHAWK_PALAPI PalCreateEventW(_In_opt_ LPSECURITY_ATTRIBUTES pEventAttributes, UInt32_BOOL manualReset, UInt32_BOOL initialState, _In_opt_z_ LPCWSTR pName); diff --git a/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp index 6fe0e768f15fa1..760dbc3b0ad8e7 100644 --- a/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp +++ b/src/coreclr/nativeaot/Runtime/ThunksMapping.cpp @@ -229,6 +229,8 @@ EXTERN_C NATIVEAOT_API void* __cdecl RhAllocateThunksMapping() return NULL; } + PalFlushInstructionCache(pThunksSection, THUNKS_MAP_SIZE); + return pThunksSection; } diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index f8be025c062aef..f1d4fff1d0ff0b 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -832,6 +832,34 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddre return mprotect(pPageStart, memSize, unixProtect) == 0; } +REDHAWK_PALEXPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size) +{ +#ifndef HOST_ARM + // Intrinsic should do the right thing across all platforms (except Linux arm) + __builtin___clear_cache((char *)pAddress, (char *)pAddress + size); +#else // HOST_ARM + // On Linux/arm (at least on 3.10) we found that there is a problem with __do_cache_op (arch/arm/kernel/traps.c) + // implementing cacheflush syscall. cacheflush flushes only the first page in range [pAddress, pAddress + size) + // and leaves other pages in undefined state which causes random tests failures (often due to SIGSEGV) with no particular pattern. + // + // As a workaround, we call __builtin___clear_cache on each page separately. + + const size_t pageSize = getpagesize(); + uint8_t* begin = (uint8_t*)pAddress; + uint8_t* end = begin + size; + + while (begin < end) + { + uint8_t* endOrNextPageBegin = ALIGN_UP(begin + 1, pageSize); + if (endOrNextPageBegin > end) + endOrNextPageBegin = end; + + __builtin___clear_cache((char *)begin, (char *)endOrNextPageBegin); + begin = endOrNextPageBegin; + } +#endif // HOST_ARM +} + REDHAWK_PALEXPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer) { static void* pBuffer; diff --git a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp index 39f8ffd00a0cbb..20238e87501d5e 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalRedhawkMinWin.cpp @@ -599,6 +599,11 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddre return VirtualProtect(pAddress, size, protect, &oldProtect); } +REDHAWK_PALEXPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size) +{ + FlushInstructionCache(GetCurrentProcess(), pAddress, size); +} + REDHAWK_PALEXPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer) { static void* pBuffer; From 666ccf54dd0a6c159842bcd7f93e64801e137874 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 10 Sep 2022 11:59:40 -0700 Subject: [PATCH 2/2] More precise ifdef --- src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp | 9 ++++----- src/coreclr/pal/src/thread/context.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index f1d4fff1d0ff0b..55ad31c82d886a 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -834,10 +834,7 @@ REDHAWK_PALEXPORT UInt32_BOOL REDHAWK_PALAPI PalVirtualProtect(_In_ void* pAddre REDHAWK_PALEXPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size) { -#ifndef HOST_ARM - // Intrinsic should do the right thing across all platforms (except Linux arm) - __builtin___clear_cache((char *)pAddress, (char *)pAddress + size); -#else // HOST_ARM +#if defined(__linux__) && defined(HOST_ARM) // On Linux/arm (at least on 3.10) we found that there is a problem with __do_cache_op (arch/arm/kernel/traps.c) // implementing cacheflush syscall. cacheflush flushes only the first page in range [pAddress, pAddress + size) // and leaves other pages in undefined state which causes random tests failures (often due to SIGSEGV) with no particular pattern. @@ -857,7 +854,9 @@ REDHAWK_PALEXPORT void PalFlushInstructionCache(_In_ void* pAddress, size_t size __builtin___clear_cache((char *)begin, (char *)endOrNextPageBegin); begin = endOrNextPageBegin; } -#endif // HOST_ARM +#else + __builtin___clear_cache((char *)pAddress, (char *)pAddress + size); +#endif } REDHAWK_PALEXPORT _Ret_maybenull_ void* REDHAWK_PALAPI PalSetWerDataBuffer(_In_ void* pNewBuffer) diff --git a/src/coreclr/pal/src/thread/context.cpp b/src/coreclr/pal/src/thread/context.cpp index 184116239ca736..a9a00bb04a0f5d 100644 --- a/src/coreclr/pal/src/thread/context.cpp +++ b/src/coreclr/pal/src/thread/context.cpp @@ -1637,10 +1637,7 @@ DBG_FlushInstructionCache( IN LPCVOID lpBaseAddress, IN SIZE_T dwSize) { -#ifndef HOST_ARM - // Intrinsic should do the right thing across all platforms (except Linux arm) - __builtin___clear_cache((char *)lpBaseAddress, (char *)((INT_PTR)lpBaseAddress + dwSize)); -#else // HOST_ARM +#if defined(__linux__) && defined(HOST_ARM) // On Linux/arm (at least on 3.10) we found that there is a problem with __do_cache_op (arch/arm/kernel/traps.c) // implementing cacheflush syscall. cacheflush flushes only the first page in range [lpBaseAddress, lpBaseAddress + dwSize) // and leaves other pages in undefined state which causes random tests failures (often due to SIGSEGV) with no particular pattern. @@ -1660,6 +1657,8 @@ DBG_FlushInstructionCache( __builtin___clear_cache((char *)begin, (char *)endOrNextPageBegin); begin = endOrNextPageBegin; } -#endif // HOST_ARM +#else + __builtin___clear_cache((char *)lpBaseAddress, (char *)((INT_PTR)lpBaseAddress + dwSize)); +#endif return TRUE; }