From a9f1207d9f14d6bdf6e4be0f206d11df8e032382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksey=20Kliger=20=28=CE=BBgeek=29?= Date: Fri, 23 Apr 2021 09:08:50 -0400 Subject: [PATCH] [mono] Work around arm64 MacCatalyst unavailable JITing API (#51669) * To [JIT on Apple Silicon](https://developer.apple.com/documentation/apple-silicon/porting-just-in-time-compilers-to-apple-silicon) the application must call `pthread_jit_write_protect_np (0)` before writing to a code page and then `pthread_jit_write_protect_np (1)` before executing JITed code. * Catalyst apps are Mac apps that get to use iOS frameworks (as well as some mac frameworks). The API access is guarded by `__API_AVAILABLE` and `__API_UNAVAILABLE` macros in Apple framework headers. If an API is not explicitly marked for catalyst, clang will follow the `ios` availability. Unfortunately, `pthread_jit_write_protect_np` is marked like this: ```c __API_AVAILABLE(macos(11.0)) __API_UNAVAILABLE(ios, tvos, watchos) void pthread_jit_write_protect_np(int enabled); ``` So a Catalyst app running on arm64 cannot call it. It will get a compile-time error. ``` src/mono/mono/utils/mono-codeman.c:669:3: error: 'pthread_jit_write_protect_np' is unavailable: not available on macCatalyst pthread_jit_write_protect_np (0); ^ /Applications/Xcode_12.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/usr/include/pthread.h:561:6: note: 'pthread_jit_write_protect_np' has been explicitly marked unavailable here void pthread_jit_write_protect_np(int enabled); ^ ``` --- Turns out the symbol `pthread_jit_write_protect_np` is actually present in the Catalyst framework libraries, and it does the right thing. This PR works around the unfortunate `pthread.h` declaration by not using it. We create a new .c file that included our own declaration ```c void pthread_jit_write_protect_np(int enabled); ``` And then a new `mono_jit_write_protect` function that calls the pthread function. --- Another option would be to use something like ``` #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpartial-availability" ... #pragma clang diagnostic pop ``` around the offending callsites. But neither ignoring `-Wpartial-availability` nor `-Wunguarded-availability-new` seem to have any effect on the compilation error. --- Caveat: It's not yet clear whether this is ok to do in a retail (App Store) app. --- * [mono] define HOST_MACCAT not HOST_MACCATALYST for Mac Catalyst The C code already has HOST_MACCAT ifdefs. HOST_MACCATALYST isn't used. --- src/mono/CMakeLists.txt | 4 ++-- src/mono/cmake/config.h.in | 3 +++ src/mono/cmake/configure.cmake | 2 +- src/mono/mono/utils/CMakeLists.txt | 2 ++ src/mono/mono/utils/mono-codeman.c | 13 +++++++++++++ src/mono/mono/utils/write-protect.c | 27 +++++++++++++++++++++++++++ src/mono/mono/utils/write-protect.h | 18 ++++++++++++++++++ 7 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/mono/mono/utils/write-protect.c create mode 100644 src/mono/mono/utils/write-protect.h diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index fcb13923813af..43dca9946c6f0 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -185,7 +185,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(PTHREAD_POINTER_ID 1) set(USE_MACH_SEMA 1) if(CMAKE_SYSTEM_VARIANT STREQUAL "MacCatalyst") - set(HOST_MACCATALYST 1) + set(HOST_MACCAT 1) endif() elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "tvOS") # See man cmake-toolchains(7) on which variables @@ -735,7 +735,7 @@ endif() ### End of OS specific checks add_subdirectory(mono) -if (ENABLE_MSCORDBI AND NOT TARGET_ARCH STREQUAL "arm64" AND NOT CMAKE_CROSSCOMPILING AND NOT TARGET_IOS AND NOT TARGET_ANDROID AND NOT TARGET_BROWSER AND NOT HOST_MACCATALYST) +if (ENABLE_MSCORDBI AND NOT TARGET_ARCH STREQUAL "arm64" AND NOT CMAKE_CROSSCOMPILING AND NOT TARGET_IOS AND NOT TARGET_ANDROID AND NOT TARGET_BROWSER AND NOT HOST_MACCAT) add_subdirectory(dlls/mscordbi) add_subdirectory(dlls/dbgshim) endif() diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index af6f661a96a8b..d93777d6c02cf 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -113,6 +113,9 @@ /* Host Platform is tvOS */ #cmakedefine HOST_TVOS 1 +/* Host Platform is Mac Catalyst */ +#cmakedefine HOST_MACCAT 1 + /* Use classic Windows API support */ #cmakedefine HAVE_CLASSIC_WINAPI_SUPPORT 1 diff --git a/src/mono/cmake/configure.cmake b/src/mono/cmake/configure.cmake index 519c1b6ec7666..453b644c63d5c 100644 --- a/src/mono/cmake/configure.cmake +++ b/src/mono/cmake/configure.cmake @@ -157,6 +157,6 @@ if(HOST_IOS) set(HAVE_EXECVE 0) set(HAVE_EXECVP 0) endif() -elseif(HOST_MACCATALYST) +elseif(HOST_MACCAT) set(HAVE_SYSTEM 0) endif() diff --git a/src/mono/mono/utils/CMakeLists.txt b/src/mono/mono/utils/CMakeLists.txt index 115570cf39df4..6b3b3b02d3f2a 100644 --- a/src/mono/mono/utils/CMakeLists.txt +++ b/src/mono/mono/utils/CMakeLists.txt @@ -33,6 +33,8 @@ set(utils_common_sources mono-logger.c mono-logger-internals.h mono-codeman.c + write-protect.c + write-protect.h mono-counters.c mono-compiler.h mono-dl.c diff --git a/src/mono/mono/utils/mono-codeman.c b/src/mono/mono/utils/mono-codeman.c index 24c90a3e6dd46..ec84e08f0de86 100644 --- a/src/mono/mono/utils/mono-codeman.c +++ b/src/mono/mono/utils/mono-codeman.c @@ -28,6 +28,7 @@ static void* mono_code_manager_heap; #include #include +#include static uintptr_t code_memory_used = 0; static size_t dynamic_code_alloc_count; @@ -668,6 +669,11 @@ mono_codeman_enable_write (void) mono_native_tls_set_value (write_level_tls_id, GINT_TO_POINTER (level)); pthread_jit_write_protect_np (0); } +#elif defined(HOST_MACCAT) && defined(__aarch64__) + int level = GPOINTER_TO_INT (mono_native_tls_get_value (write_level_tls_id)); + level ++; + mono_native_tls_set_value (write_level_tls_id, GINT_TO_POINTER (level)); + mono_jit_write_protect (0); #endif } @@ -689,5 +695,12 @@ mono_codeman_disable_write (void) if (level == 0) pthread_jit_write_protect_np (1); } +#elif defined(HOST_MACCAT) && defined(__aarch64__) + int level = GPOINTER_TO_INT (mono_native_tls_get_value (write_level_tls_id)); + g_assert (level); + level --; + mono_native_tls_set_value (write_level_tls_id, GINT_TO_POINTER (level)); + if (level == 0) + mono_jit_write_protect (1); #endif } diff --git a/src/mono/mono/utils/write-protect.c b/src/mono/mono/utils/write-protect.c new file mode 100644 index 0000000000000..a7aeebee63052 --- /dev/null +++ b/src/mono/mono/utils/write-protect.c @@ -0,0 +1,27 @@ +/** + * \file + */ + +#include "config.h" + +#include "mono-compiler.h" +#include "write-protect.h" + +#if defined (HOST_MACCAT) && defined (__aarch64__) + +/* our own declaration of pthread_jit_write_protect_np so that we don't see the __API_UNAVAILABLE__ header */ +void +pthread_jit_write_protect_np (int enabled); + + +void +mono_jit_write_protect (int enabled) +{ + pthread_jit_write_protect_np (enabled); +} + +#else + +MONO_EMPTY_SOURCE_FILE (write_protect); + +#endif diff --git a/src/mono/mono/utils/write-protect.h b/src/mono/mono/utils/write-protect.h new file mode 100644 index 0000000000000..f218bb6a93f30 --- /dev/null +++ b/src/mono/mono/utils/write-protect.h @@ -0,0 +1,18 @@ +/** + * \file + */ + +#ifndef __MONO_WRITE_PROTECT_H__ +#define __MONO_WRITE_PROTECT_H__ + +#include + +#if defined (HOST_MACCAT) && defined (__aarch64__) + +void +mono_jit_write_protect (int enabled); + +#endif /* defined (HOST_MACCAT) && defined (__aarch64__) */ + +#endif /* __MONO_WRITE_PROTECT_H__ */ +