Skip to content

Commit

Permalink
recompiler: allow jitprotect() calls to be nested (#1306)
Browse files Browse the repository at this point in the history
The following applies only to macOS when JIT page protection is enforced
(namely on Apple silicon).

A sequence of jitprotect(false) calls must now be followed by an equal
number of jitprotect(true) calls to restore executable protection. A
further assumption is that nesting always starts with (false), which is
only logical because you have to write something before you have
anything to execute. This is explicitly checked in debug builds.

The N64 and PS1 recompilers have needed this since commit 8ed4af8 in
order to handle the path taken on recompiler cache flushes. It's subtle,
but emit() will evict all pools and so the second call to pool() from
block() will reallocate the pool and unexpectedly toggle the protection
out from under it.

Technically the SH2 recompiler has needed this since commit 241f0e9
(from 8 months ago!) but that same commit made cache flushes practically
nonexistent, so it's likely not impacting the retail title library.
  • Loading branch information
invertego authored Nov 30, 2023
1 parent 1c9d388 commit 9a8e7f8
Showing 1 changed file with 9 additions and 1 deletion.
10 changes: 9 additions & 1 deletion nall/memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,15 @@ template<u32 size, typename T> auto writem(void* target, T data) -> void {
inline auto jitprotect(bool executable) -> void {
#if defined(PLATFORM_MACOS)
if(__builtin_available(macOS 11.0, *)) {
pthread_jit_write_protect_np(executable);
static thread_local s32 depth = 0;
if(!executable && depth++ == 0
|| executable && --depth == 0) {
pthread_jit_write_protect_np(executable);
}
#if defined(DEBUG)
struct unmatched_jitprotect {};
if(depth < 0 || depth > 10) throw unmatched_jitprotect{};
#endif
}
#endif
}
Expand Down

0 comments on commit 9a8e7f8

Please sign in to comment.