Skip to content

Commit

Permalink
darwin-aarch64: add support for MAP_JIT/pthread_jit_write_protect_np
Browse files Browse the repository at this point in the history
  • Loading branch information
lewurm committed Feb 2, 2022
1 parent 6bc08b4 commit d153c07
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import static com.oracle.svm.core.posix.headers.Mman.MAP_ANON;
import static com.oracle.svm.core.posix.headers.Mman.MAP_FAILED;
import static com.oracle.svm.core.posix.headers.Mman.MAP_FIXED;
import static com.oracle.svm.core.posix.headers.Mman.MAP_JIT;
import static com.oracle.svm.core.posix.headers.Mman.MAP_NORESERVE;
import static com.oracle.svm.core.posix.headers.Mman.MAP_PRIVATE;
import static com.oracle.svm.core.posix.headers.Mman.PROT_EXEC;
Expand All @@ -38,8 +39,10 @@
import static com.oracle.svm.core.posix.headers.Mman.NoTransitions.munmap;
import static org.graalvm.word.WordFactory.nullPointer;

import com.oracle.svm.core.posix.headers.darwin.DarwinPthread;
import org.graalvm.compiler.word.Word;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.Pointer;
Expand Down Expand Up @@ -99,6 +102,15 @@ protected static int accessAsProt(int access) {
return prot;
}

@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
protected static boolean needsMapJit(int access) {
if (!Platform.includedIn(Platform.DARWIN_AARCH64.class)) {
return false;
}

return (access & Access.WRITE) != 0 && (access & Access.EXECUTE) != 0;
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public UnsignedWord getGranularity() {
Expand All @@ -107,7 +119,7 @@ public UnsignedWord getGranularity() {

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment, boolean executable) {
if (nbytes.equal(0)) {
return WordFactory.nullPointer();
}
Expand All @@ -119,7 +131,11 @@ public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
mappingSize = mappingSize.add(alignment);
}
mappingSize = UnsignedUtils.roundUp(mappingSize, granularity);
Pointer mappingBegin = mmap(nullPointer(), mappingSize, PROT_NONE(), MAP_ANON() | MAP_PRIVATE() | MAP_NORESERVE(), NO_FD, NO_FD_OFFSET);
int flags = MAP_ANON() | MAP_PRIVATE() | MAP_NORESERVE();
if (Platform.includedIn(Platform.DARWIN_AARCH64.class) && executable) {
flags |= MAP_JIT();
}
Pointer mappingBegin = mmap(nullPointer(), mappingSize, PROT_NONE(), flags, NO_FD, NO_FD_OFFSET);
if (mappingBegin.equal(MAP_FAILED())) {
return nullPointer();
}
Expand Down Expand Up @@ -167,6 +183,9 @@ public Pointer commit(PointerBase start, UnsignedWord nbytes, int access) {
if (start.isNonNull()) {
flags |= MAP_FIXED();
}
if (needsMapJit(access)) {
flags |= MAP_JIT();
}
/* The memory returned by mmap is guaranteed to be zeroed. */
final Pointer result = mmap(start, nbytes, accessAsProt(access), flags, NO_FD, NO_FD_OFFSET);
return result.notEqual(MAP_FAILED()) ? result : nullPointer();
Expand All @@ -182,6 +201,11 @@ public int protect(PointerBase start, UnsignedWord nbytes, int access) {
return mprotect(start, nbytes, accessAsProt(access));
}

@Override
public void jitWriteProtect(boolean protect) {
DarwinPthread.pthread_jit_write_protect_np(protect ? 1 : 0);
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public int uncommit(PointerBase start, UnsignedWord nbytes) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.core.posix.headers;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.constant.CConstant;
import org.graalvm.nativeimage.c.function.CFunction;
Expand Down Expand Up @@ -67,6 +69,10 @@ public class Mman {
@CConstant
public static native int MAP_NORESERVE();

@CConstant
@Platforms(Platform.DARWIN_AARCH64.class)
public static native int MAP_JIT();

@CConstant
public static native PointerBase MAP_FAILED();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@
*/
package com.oracle.svm.core.posix.headers.darwin;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.function.CFunction;
import org.graalvm.nativeimage.c.function.CFunction.Transition;
import org.graalvm.nativeimage.c.function.CLibrary;
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;

Expand All @@ -49,4 +52,8 @@ public class DarwinPthread {

@CFunction(transition = Transition.NO_TRANSITION)
public static native Pointer pthread_get_stackaddr_np(Pthread.pthread_t thread);

@Platforms(Platform.DARWIN_AARCH64.class)
@CFunction(transition = Transition.NO_TRANSITION)
public static native void pthread_jit_write_protect_np(int enabled);
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private static int accessAsProt(int access) {

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment, boolean executable) {
if (nbytes.equal(0)) {
return WordFactory.nullPointer();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@

import java.util.EnumSet;

import com.oracle.svm.core.os.VirtualMemoryProvider;
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
import com.oracle.svm.core.threadlocal.FastThreadLocalInt;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.UnmanagedMemory;
import org.graalvm.nativeimage.c.function.CodePointer;
import org.graalvm.nativeimage.c.struct.SizeOf;
Expand Down Expand Up @@ -154,14 +159,19 @@ public static boolean areAllObjectsOnImageHeap(CodeInfo info) {
* Walks all strong references in a {@link CodeInfo} object.
*/
public static boolean walkStrongReferences(CodeInfo info, ObjectReferenceVisitor visitor) {
return NonmovableArrays.walkUnmanagedObjectArray(cast(info).getObjectFields(), visitor, CodeInfoImpl.FIRST_STRONGLY_REFERENCED_OBJFIELD, CodeInfoImpl.STRONGLY_REFERENCED_OBJFIELD_COUNT);
enableJitWriteProtect(false);
boolean ret = NonmovableArrays.walkUnmanagedObjectArray(cast(info).getObjectFields(), visitor, CodeInfoImpl.FIRST_STRONGLY_REFERENCED_OBJFIELD, CodeInfoImpl.STRONGLY_REFERENCED_OBJFIELD_COUNT);
enableJitWriteProtect(true);
return ret;
}

/**
* Walks all weak references in a {@link CodeInfo} object.
*/
@DuplicatedInNativeCode
public static boolean walkWeakReferences(CodeInfo info, ObjectReferenceVisitor visitor) {
enableJitWriteProtect(false);

CodeInfoImpl impl = cast(info);
boolean continueVisiting = true;
continueVisiting = continueVisiting &&
Expand All @@ -174,6 +184,8 @@ public static boolean walkWeakReferences(CodeInfo info, ObjectReferenceVisitor v
continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoSourceClasses(), visitor);
continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoSourceMethodNames(), visitor);
continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getDeoptimizationObjectConstants(), visitor);

enableJitWriteProtect(true);
return continueVisiting;
}

Expand Down Expand Up @@ -248,6 +260,29 @@ public static void makeCodeMemoryWriteableNonExecutable(CodePointer start, Unsig
CommittedMemoryProvider.get().protect(start, size, EnumSet.of(CommittedMemoryProvider.Access.READ, CommittedMemoryProvider.Access.WRITE));
}

@Platforms(Platform.DARWIN_AARCH64.class)
private static final FastThreadLocalInt jitProtectDepth = FastThreadLocalFactory.createInt("jitProtectDepth");

public static void enableJitWriteProtect(boolean protect) {
if (Platform.includedIn(Platform.DARWIN_AARCH64.class)) {
// Disabling write protection can be nested, for example a GC can be triggered during code installation
// which in turn causes walk of references in code. Both need to disable write protection, but only the
// outer one should enable it again.
if (!protect) {
if (jitProtectDepth.get() == 0) {
VirtualMemoryProvider.get().jitWriteProtect(protect);
}
jitProtectDepth.set(jitProtectDepth.get() + 1);
} else {
VMError.guarantee(jitProtectDepth.get() >= 1);
jitProtectDepth.set(jitProtectDepth.get() - 1);
if (jitProtectDepth.get() == 0) {
VirtualMemoryProvider.get().jitWriteProtect(protect);
}
}
}
}

@Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true)
static void releaseMethodInfoOnTearDown(CodeInfo info) {
InstalledCodeObserverSupport.removeObserversOnTearDown(getCodeObserverHandles(info));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ private Pointer allocate(UnsignedWord size, UnsignedWord alignment, boolean exec
}
Pointer reserved = WordFactory.nullPointer();
if (!UnsignedUtils.isAMultiple(getGranularity(), alignment)) {
reserved = VirtualMemoryProvider.get().reserve(size, alignment);
reserved = VirtualMemoryProvider.get().reserve(size, alignment, executable);
if (reserved.isNull()) {
return nullPointer();
}
Expand Down Expand Up @@ -170,8 +170,12 @@ public void untrack(UnsignedWord size) {
class OSCommittedMemoryProviderFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
OSCommittedMemoryProvider memoryProvider = new OSCommittedMemoryProvider();
/* TODO: Remove this once GR-34673 is implemented */
ImageSingletons.add(OSCommittedMemoryProvider.class, memoryProvider);

if (!ImageSingletons.contains(CommittedMemoryProvider.class)) {
ImageSingletons.add(CommittedMemoryProvider.class, new OSCommittedMemoryProvider());
ImageSingletons.add(CommittedMemoryProvider.class, memoryProvider);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.core.os;

import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.util.VMError;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.word.Pointer;
Expand Down Expand Up @@ -83,10 +85,16 @@ default UnsignedWord getAlignment() {
* to a multiple of the {@linkplain #getGranularity() granularity}. This value must
* not be 0.
* @param alignment The alignment in bytes of the start of the address range to be reserved.
* @param executable Indicates if memory for code is requested.
* @return An {@linkplain #getAlignment aligned} pointer to the beginning of the reserved
* address range, or {@link WordFactory#nullPointer()} in case of an error.
*/
Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment);
Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment, boolean executable);

@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
default Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
return reserve(nbytes, alignment, false);
}

/**
* Map a region of an open file to the specified address range. When {@linkplain Access#WRITE
Expand Down Expand Up @@ -183,4 +191,15 @@ default UnsignedWord getAlignment() {
* @return 0 when successful, or a non-zero implementation-specific error code.
*/
int free(PointerBase start, UnsignedWord nbytes);

/**
* Toggle a thread-local flag that tells the OS our intention about the usage of pages that are
* mapped with MAP_JIT. Only applicable on macOS. Only enforced on AArch64.
* @param protect If write protection is enabled, pages mapped with MAP_JIT have effectively
* READ|EXEC permissions for the calling thread. If disabled, the permissions
* for the same pages turn to READ|WRITE.
*/
default void jitWriteProtect(boolean protect) {
VMError.shouldNotReachHere();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ private CodeInstallInfo doPrepareInstall() {
private static void installPrepared(SharedMethod method, CodeInstallInfo installInfo, SubstrateInstalledCode installedCode) {
IsolatedRuntimeMethodInfoAccess.startTrackingInCurrentIsolate(installInfo.getCodeInfo());

RuntimeCodeInfoAccess.enableJitWriteProtect(false);
IsolatedReferenceAdjuster.adjustAndDispose(installInfo.getAdjusterData(), IsolatedCompileClient.get().getHandleSet());
installInfo.setAdjusterData(WordFactory.nullPointer());
RuntimeCodeInfoAccess.enableJitWriteProtect(true);

doInstallPrepared(method, installInfo.getCodeInfo(), installedCode);
UnmanagedMemory.free(installInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ protected void doPrepareInstall(ReferenceAdjuster adjuster, CodeInfo codeInfo) {
"No direct calls permitted: patching of runtime-compiled code intentionally not supported");
}

RuntimeCodeInfoAccess.enableJitWriteProtect(false);
prepareCodeMemory();

/*
Expand Down Expand Up @@ -229,6 +230,8 @@ protected void doPrepareInstall(ReferenceAdjuster adjuster, CodeInfo codeInfo) {
patchDirectObjectConstants(objectConstants, codeInfo, adjuster);

createCodeChunkInfos(codeInfo, adjuster);

RuntimeCodeInfoAccess.enableJitWriteProtect(true);
compilation = null;
}

Expand Down

0 comments on commit d153c07

Please sign in to comment.