Skip to content

Commit

Permalink
[mono] Work around arm64 MacCatalyst unavailable JITing API (#51669)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
lambdageek authored Apr 23, 2021
1 parent df61348 commit a9f1207
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/mono/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions src/mono/cmake/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/mono/cmake/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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()
2 changes: 2 additions & 0 deletions src/mono/mono/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions src/mono/mono/utils/mono-codeman.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ static void* mono_code_manager_heap;

#include <mono/utils/mono-os-mutex.h>
#include <mono/utils/mono-tls.h>
#include <mono/utils/write-protect.h>

static uintptr_t code_memory_used = 0;
static size_t dynamic_code_alloc_count;
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}
27 changes: 27 additions & 0 deletions src/mono/mono/utils/write-protect.c
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions src/mono/mono/utils/write-protect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* \file
*/

#ifndef __MONO_WRITE_PROTECT_H__
#define __MONO_WRITE_PROTECT_H__

#include <mono/utils/mono-publib.h>

#if defined (HOST_MACCAT) && defined (__aarch64__)

void
mono_jit_write_protect (int enabled);

#endif /* defined (HOST_MACCAT) && defined (__aarch64__) */

#endif /* __MONO_WRITE_PROTECT_H__ */

0 comments on commit a9f1207

Please sign in to comment.