Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-36766] Darwin-aarch64: add support for MAP_JIT/pthread_jit_write_protect_np #4286

Merged
merged 2 commits into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -788,33 +788,40 @@ private static PinnedObjectImpl removeClosedPinnedObjects(PinnedObjectImpl list)
@Uninterruptible(reason = "Required by called JavaStackWalker methods. We are at a safepoint during GC, so it does not change anything for this method.", calleeMustBe = false)
private void blackenStackRoots() {
Timer blackenStackRootsTimer = timers.blackenStackRoots.open();
RuntimeCodeInfoAccess.acquireThreadWriteAccess();
try {
Pointer sp = readCallerStackPointer();
CodePointer ip = readReturnAddress();
try {
Pointer sp = readCallerStackPointer();
CodePointer ip = readReturnAddress();

JavaStackWalk walk = StackValue.get(JavaStackWalk.class);
JavaStackWalker.initWalk(walk, sp, ip);
walkStack(walk);
JavaStackWalk walk = StackValue.get(JavaStackWalk.class);
JavaStackWalker.initWalk(walk, sp, ip);
walkStack(walk);

if (SubstrateOptions.MultiThreaded.getValue()) {
/*
* Scan the stacks of all the threads. Other threads will be blocked at a safepoint
* (or in native code) so they will each have a JavaFrameAnchor in their VMThread.
*/
for (IsolateThread vmThread = VMThreads.firstThread(); vmThread.isNonNull(); vmThread = VMThreads.nextThread(vmThread)) {
if (vmThread == CurrentIsolate.getCurrentThread()) {
/*
* The current thread is already scanned by code above, so we do not have to
* do anything for it here. It might have a JavaFrameAnchor from earlier
* Java-to-C transitions, but certainly not at the top of the stack since it
* is running this code, so just this scan would be incomplete.
*/
continue;
}
if (JavaStackWalker.initWalk(walk, vmThread)) {
walkStack(walk);
if (SubstrateOptions.MultiThreaded.getValue()) {
/*
* Scan the stacks of all the threads. Other threads will be blocked at a
* safepoint (or in native code) so they will each have a JavaFrameAnchor in
* their VMThread.
*/
for (IsolateThread vmThread = VMThreads.firstThread(); vmThread.isNonNull(); vmThread = VMThreads.nextThread(vmThread)) {
if (vmThread == CurrentIsolate.getCurrentThread()) {
/*
* The current thread is already scanned by code above, so we do not
* have to do anything for it here. It might have a JavaFrameAnchor from
* earlier Java-to-C transitions, but certainly not at the top of the
* stack since it is running this code, so just this scan would be
* incomplete.
*/
continue;
}
if (JavaStackWalker.initWalk(walk, vmThread)) {
walkStack(walk);
}
}
}
} finally {
RuntimeCodeInfoAccess.releaseThreadWriteAccess();
}
} finally {
blackenStackRootsTimer.close();
Expand Down
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 @@ -107,7 +110,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 +122,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 +174,11 @@ public Pointer commit(PointerBase start, UnsignedWord nbytes, int access) {
if (start.isNonNull()) {
flags |= MAP_FIXED();
}

boolean isWX = (access & Access.WRITE) != 0 && (access & Access.EXECUTE) != 0;
if (Platform.includedIn(Platform.DARWIN_AARCH64.class) && isWX) {
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 +194,12 @@ public int protect(PointerBase start, UnsignedWord nbytes, int access) {
return mprotect(start, nbytes, accessAsProt(access));
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
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 @@ -91,8 +91,8 @@ public void dumpRegisters(Log log, Signal.ucontext_t uContext, boolean printLoca
dumpReg(log, "R28 ", regs.read(28), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations);
dumpReg(log, "R29 ", sigcontext.fp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations);
dumpReg(log, "R30 ", sigcontext.lr(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations);
dumpReg(log, "SP ", sigcontext.sp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations);
dumpReg(log, "PC ", sigcontext.pc(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations);
dumpReg(log, "SP ", sigcontext.sp(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations);
dumpReg(log, "PC ", sigcontext.pc(), printLocationInfo, allowJavaHeapAccess, allowUnsafeOperations);
}

@Override
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,6 +24,8 @@
*/
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;
Expand All @@ -49,4 +51,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 @@ -248,6 +253,32 @@ 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");

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void acquireThreadWriteAccess() {
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 (jitProtectDepth.get() == 0) {
VirtualMemoryProvider.get().jitWriteProtect(false);
}
jitProtectDepth.set(jitProtectDepth.get() + 1);
}
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void releaseThreadWriteAccess() {
if (Platform.includedIn(Platform.DARWIN_AARCH64.class)) {
VMError.guarantee(jitProtectDepth.get() >= 1);
jitProtectDepth.set(jitProtectDepth.get() - 1);
if (jitProtectDepth.get() == 0) {
VirtualMemoryProvider.get().jitWriteProtect(true);
}
}
}

@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 @@ -203,18 +203,23 @@ public boolean walkRuntimeMethodsDuringGC(CodeInfoVisitor visitor) {
assert VMOperation.isGCInProgress() : "otherwise, we would need to make sure that the CodeInfo is not freeded by the GC";
if (table.isNonNull()) {
int length = NonmovableArrays.lengthOf(table);
for (int i = 0; i < length;) {
UntetheredCodeInfo info = NonmovableArrays.getWord(table, i);
if (info.isNonNull()) {
visitor.visitCode(CodeInfoAccess.convert(info));
}
RuntimeCodeInfoAccess.acquireThreadWriteAccess();
try {
for (int i = 0; i < length;) {
UntetheredCodeInfo info = NonmovableArrays.getWord(table, i);
if (info.isNonNull()) {
visitor.visitCode(CodeInfoAccess.convert(info));
}

// If the visitor removed the current entry from the table, then it is necessary to
// visit the now updated entry one more time. However, this could have the effect
// that some entries are visited more than once.
if (info == NonmovableArrays.getWord(table, i)) {
i++;
// If the visitor removed the current entry from the table, then it is necessary
// to visit the now updated entry one more time. However, this could have the
// effect that some entries are visited more than once.
if (info == NonmovableArrays.getWord(table, i)) {
i++;
}
}
} finally {
RuntimeCodeInfoAccess.releaseThreadWriteAccess();
}
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_END;
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_WRITABLE_BEGIN;
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_WRITABLE_END;
import static org.graalvm.word.WordFactory.nullPointer;

import java.util.EnumSet;

import com.oracle.svm.core.util.UnsignedUtils;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
Expand All @@ -40,6 +42,7 @@
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.c.function.CEntryPointErrors;
import com.oracle.svm.core.heap.Heap;
import org.graalvm.word.WordFactory;

public abstract class AbstractCommittedMemoryProvider implements CommittedMemoryProvider {
@Fold
Expand Down Expand Up @@ -94,4 +97,95 @@ public boolean protect(PointerBase start, UnsignedWord nbytes, EnumSet<Access> a
int success = VirtualMemoryProvider.get().protect(start, nbytes, vmAccessBits);
return success == 0;
}

@Override
public Pointer allocateAlignedChunk(UnsignedWord nbytes, UnsignedWord alignment) {
return allocate(nbytes, alignment, false);
}

@Override
public Pointer allocateUnalignedChunk(UnsignedWord nbytes) {
return allocate(nbytes, WordFactory.unsigned(1), false);
}

@Override
public Pointer allocateExecutableMemory(UnsignedWord nbytes, UnsignedWord alignment) {
return allocate(nbytes, alignment, true);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private Pointer allocate(UnsignedWord size, UnsignedWord alignment, boolean executable) {
int access = VirtualMemoryProvider.Access.READ | VirtualMemoryProvider.Access.WRITE;
if (executable) {
access |= VirtualMemoryProvider.Access.EXECUTE;
}
Pointer reserved = WordFactory.nullPointer();
if (!UnsignedUtils.isAMultiple(getGranularity(), alignment)) {
reserved = VirtualMemoryProvider.get().reserve(size, alignment, executable);
if (reserved.isNull()) {
return nullPointer();
}
}
Pointer committed = VirtualMemoryProvider.get().commit(reserved, size, access);
if (committed.isNull()) {
if (reserved.isNonNull()) {
VirtualMemoryProvider.get().free(reserved, size);
}
return nullPointer();
}
assert reserved.isNull() || reserved.equal(committed);
tracker.track(size);
return committed;
}

@Override
public boolean areUnalignedChunksZeroed() {
return false;
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public void freeAlignedChunk(PointerBase start, UnsignedWord nbytes, UnsignedWord alignment) {
free(start, nbytes);
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public void freeUnalignedChunk(PointerBase start, UnsignedWord nbytes) {
free(start, nbytes);
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public void freeExecutableMemory(PointerBase start, UnsignedWord nbytes, UnsignedWord alignment) {
free(start, nbytes);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private void free(PointerBase start, UnsignedWord nbytes) {
if (VirtualMemoryProvider.get().free(start, nbytes) == 0) {
tracker.untrack(nbytes);
}
}

private final VirtualMemoryTracker tracker = new VirtualMemoryTracker();

public static class VirtualMemoryTracker {

private UnsignedWord totalAllocated;

public VirtualMemoryTracker() {
this.totalAllocated = WordFactory.zero();
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public void track(UnsignedWord size) {
totalAllocated = totalAllocated.add(size);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public void untrack(UnsignedWord size) {
totalAllocated = totalAllocated.subtract(size);
}
}
}
Loading