Skip to content

Commit 92a634a

Browse files
committed
darwin-aarch64: add support for MAP_JIT/pthread_jit_write_protect_np
1 parent 67e149a commit 92a634a

File tree

9 files changed

+107
-7
lines changed

9 files changed

+107
-7
lines changed

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixVirtualMemoryProvider.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import static com.oracle.svm.core.posix.headers.Mman.MAP_ANON;
2828
import static com.oracle.svm.core.posix.headers.Mman.MAP_FAILED;
2929
import static com.oracle.svm.core.posix.headers.Mman.MAP_FIXED;
30+
import static com.oracle.svm.core.posix.headers.Mman.MAP_JIT;
3031
import static com.oracle.svm.core.posix.headers.Mman.MAP_NORESERVE;
3132
import static com.oracle.svm.core.posix.headers.Mman.MAP_PRIVATE;
3233
import static com.oracle.svm.core.posix.headers.Mman.PROT_EXEC;
@@ -38,8 +39,10 @@
3839
import static com.oracle.svm.core.posix.headers.Mman.NoTransitions.munmap;
3940
import static org.graalvm.word.WordFactory.nullPointer;
4041

42+
import com.oracle.svm.core.posix.headers.darwin.DarwinPthread;
4143
import org.graalvm.compiler.word.Word;
4244
import org.graalvm.nativeimage.ImageSingletons;
45+
import org.graalvm.nativeimage.Platform;
4346
import org.graalvm.nativeimage.c.type.WordPointer;
4447
import org.graalvm.nativeimage.hosted.Feature;
4548
import org.graalvm.word.Pointer;
@@ -99,6 +102,15 @@ protected static int accessAsProt(int access) {
99102
return prot;
100103
}
101104

105+
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
106+
protected static boolean needsMapJit(int access) {
107+
if (!Platform.includedIn(Platform.DARWIN_AARCH64.class)) {
108+
return false;
109+
}
110+
111+
return (access & Access.WRITE) != 0 && (access & Access.EXECUTE) != 0;
112+
}
113+
102114
@Override
103115
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
104116
public UnsignedWord getGranularity() {
@@ -107,7 +119,7 @@ public UnsignedWord getGranularity() {
107119

108120
@Override
109121
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
110-
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
122+
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment, boolean executable) {
111123
if (nbytes.equal(0)) {
112124
return WordFactory.nullPointer();
113125
}
@@ -119,7 +131,11 @@ public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
119131
mappingSize = mappingSize.add(alignment);
120132
}
121133
mappingSize = UnsignedUtils.roundUp(mappingSize, granularity);
122-
Pointer mappingBegin = mmap(nullPointer(), mappingSize, PROT_NONE(), MAP_ANON() | MAP_PRIVATE() | MAP_NORESERVE(), NO_FD, NO_FD_OFFSET);
134+
int flags = MAP_ANON() | MAP_PRIVATE() | MAP_NORESERVE();
135+
if (Platform.includedIn(Platform.DARWIN_AARCH64.class) && executable) {
136+
flags |= MAP_JIT();
137+
}
138+
Pointer mappingBegin = mmap(nullPointer(), mappingSize, PROT_NONE(), flags, NO_FD, NO_FD_OFFSET);
123139
if (mappingBegin.equal(MAP_FAILED())) {
124140
return nullPointer();
125141
}
@@ -167,6 +183,9 @@ public Pointer commit(PointerBase start, UnsignedWord nbytes, int access) {
167183
if (start.isNonNull()) {
168184
flags |= MAP_FIXED();
169185
}
186+
if (needsMapJit(access)) {
187+
flags |= MAP_JIT();
188+
}
170189
/* The memory returned by mmap is guaranteed to be zeroed. */
171190
final Pointer result = mmap(start, nbytes, accessAsProt(access), flags, NO_FD, NO_FD_OFFSET);
172191
return result.notEqual(MAP_FAILED()) ? result : nullPointer();
@@ -182,6 +201,11 @@ public int protect(PointerBase start, UnsignedWord nbytes, int access) {
182201
return mprotect(start, nbytes, accessAsProt(access));
183202
}
184203

204+
@Override
205+
public void jitWriteProtect(boolean protect) {
206+
DarwinPthread.pthread_jit_write_protect_np(protect ? 1 : 0);
207+
}
208+
185209
@Override
186210
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
187211
public int uncommit(PointerBase start, UnsignedWord nbytes) {

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Mman.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.posix.headers;
2626

27+
import org.graalvm.nativeimage.Platform;
28+
import org.graalvm.nativeimage.Platforms;
2729
import org.graalvm.nativeimage.c.CContext;
2830
import org.graalvm.nativeimage.c.constant.CConstant;
2931
import org.graalvm.nativeimage.c.function.CFunction;
@@ -67,6 +69,10 @@ public class Mman {
6769
@CConstant
6870
public static native int MAP_NORESERVE();
6971

72+
@CConstant
73+
@Platforms(Platform.DARWIN_AARCH64.class)
74+
public static native int MAP_JIT();
75+
7076
@CConstant
7177
public static native PointerBase MAP_FAILED();
7278

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinPthread.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
*/
2525
package com.oracle.svm.core.posix.headers.darwin;
2626

27+
import org.graalvm.nativeimage.Platform;
28+
import org.graalvm.nativeimage.Platforms;
2729
import org.graalvm.nativeimage.c.CContext;
2830
import org.graalvm.nativeimage.c.function.CFunction;
2931
import org.graalvm.nativeimage.c.function.CFunction.Transition;
3032
import org.graalvm.nativeimage.c.function.CLibrary;
3133
import org.graalvm.nativeimage.c.type.CCharPointer;
34+
import org.graalvm.nativeimage.c.type.CIntPointer;
3235
import org.graalvm.word.Pointer;
3336
import org.graalvm.word.UnsignedWord;
3437

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

5053
@CFunction(transition = Transition.NO_TRANSITION)
5154
public static native Pointer pthread_get_stackaddr_np(Pthread.pthread_t thread);
55+
56+
@Platforms(Platform.DARWIN_AARCH64.class)
57+
@CFunction(transition = Transition.NO_TRANSITION)
58+
public static native void pthread_jit_write_protect_np(int enabled);
5259
}

substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVirtualMemoryProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private static int accessAsProt(int access) {
134134

135135
@Override
136136
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
137-
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
137+
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment, boolean executable) {
138138
if (nbytes.equal(0)) {
139139
return WordFactory.nullPointer();
140140
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeCodeInfoAccess.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,13 @@
2626

2727
import java.util.EnumSet;
2828

29+
import com.oracle.svm.core.os.VirtualMemoryProvider;
30+
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
31+
import com.oracle.svm.core.threadlocal.FastThreadLocalInt;
2932
import org.graalvm.compiler.api.replacements.Fold;
3033
import org.graalvm.nativeimage.ImageSingletons;
34+
import org.graalvm.nativeimage.Platform;
35+
import org.graalvm.nativeimage.Platforms;
3136
import org.graalvm.nativeimage.UnmanagedMemory;
3237
import org.graalvm.nativeimage.c.function.CodePointer;
3338
import org.graalvm.nativeimage.c.struct.SizeOf;
@@ -154,14 +159,19 @@ public static boolean areAllObjectsOnImageHeap(CodeInfo info) {
154159
* Walks all strong references in a {@link CodeInfo} object.
155160
*/
156161
public static boolean walkStrongReferences(CodeInfo info, ObjectReferenceVisitor visitor) {
157-
return NonmovableArrays.walkUnmanagedObjectArray(cast(info).getObjectFields(), visitor, CodeInfoImpl.FIRST_STRONGLY_REFERENCED_OBJFIELD, CodeInfoImpl.STRONGLY_REFERENCED_OBJFIELD_COUNT);
162+
enableJitWriteProtect(false);
163+
boolean ret = NonmovableArrays.walkUnmanagedObjectArray(cast(info).getObjectFields(), visitor, CodeInfoImpl.FIRST_STRONGLY_REFERENCED_OBJFIELD, CodeInfoImpl.STRONGLY_REFERENCED_OBJFIELD_COUNT);
164+
enableJitWriteProtect(true);
165+
return ret;
158166
}
159167

160168
/**
161169
* Walks all weak references in a {@link CodeInfo} object.
162170
*/
163171
@DuplicatedInNativeCode
164172
public static boolean walkWeakReferences(CodeInfo info, ObjectReferenceVisitor visitor) {
173+
enableJitWriteProtect(false);
174+
165175
CodeInfoImpl impl = cast(info);
166176
boolean continueVisiting = true;
167177
continueVisiting = continueVisiting &&
@@ -174,6 +184,8 @@ public static boolean walkWeakReferences(CodeInfo info, ObjectReferenceVisitor v
174184
continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoSourceClasses(), visitor);
175185
continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getFrameInfoSourceMethodNames(), visitor);
176186
continueVisiting = continueVisiting && NonmovableArrays.walkUnmanagedObjectArray(impl.getDeoptimizationObjectConstants(), visitor);
187+
188+
enableJitWriteProtect(true);
177189
return continueVisiting;
178190
}
179191

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

263+
@Platforms(Platform.DARWIN_AARCH64.class)
264+
private static final FastThreadLocalInt jitProtectDepth = FastThreadLocalFactory.createInt("jitProtectDepth");
265+
266+
public static void enableJitWriteProtect(boolean protect) {
267+
if (Platform.includedIn(Platform.DARWIN_AARCH64.class)) {
268+
// Disabling write protection can be nested, for example a GC can be triggered during code installation
269+
// which in turn causes walk of references in code. Both need to disable write protection, but only the
270+
// outer one should enable it again.
271+
if (!protect) {
272+
if (jitProtectDepth.get() == 0) {
273+
VirtualMemoryProvider.get().jitWriteProtect(protect);
274+
}
275+
jitProtectDepth.set(jitProtectDepth.get() + 1);
276+
} else {
277+
VMError.guarantee(jitProtectDepth.get() >= 1);
278+
jitProtectDepth.set(jitProtectDepth.get() - 1);
279+
if (jitProtectDepth.get() == 0) {
280+
VirtualMemoryProvider.get().jitWriteProtect(protect);
281+
}
282+
}
283+
}
284+
}
285+
251286
@Uninterruptible(reason = "Called from uninterruptible code", mayBeInlined = true)
252287
static void releaseMethodInfoOnTearDown(CodeInfo info) {
253288
InstalledCodeObserverSupport.removeObserversOnTearDown(getCodeObserverHandles(info));

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/OSCommittedMemoryProvider.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private Pointer allocate(UnsignedWord size, UnsignedWord alignment, boolean exec
9898
}
9999
Pointer reserved = WordFactory.nullPointer();
100100
if (!UnsignedUtils.isAMultiple(getGranularity(), alignment)) {
101-
reserved = VirtualMemoryProvider.get().reserve(size, alignment);
101+
reserved = VirtualMemoryProvider.get().reserve(size, alignment, executable);
102102
if (reserved.isNull()) {
103103
return nullPointer();
104104
}
@@ -170,8 +170,12 @@ public void untrack(UnsignedWord size) {
170170
class OSCommittedMemoryProviderFeature implements Feature {
171171
@Override
172172
public void beforeAnalysis(BeforeAnalysisAccess access) {
173+
OSCommittedMemoryProvider memoryProvider = new OSCommittedMemoryProvider();
174+
/* TODO: Remove this once GR-34673 is implemented */
175+
ImageSingletons.add(OSCommittedMemoryProvider.class, memoryProvider);
176+
173177
if (!ImageSingletons.contains(CommittedMemoryProvider.class)) {
174-
ImageSingletons.add(CommittedMemoryProvider.class, new OSCommittedMemoryProvider());
178+
ImageSingletons.add(CommittedMemoryProvider.class, memoryProvider);
175179
}
176180
}
177181
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/VirtualMemoryProvider.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core.os;
2626

27+
import com.oracle.svm.core.annotate.Uninterruptible;
28+
import com.oracle.svm.core.util.VMError;
2729
import org.graalvm.compiler.api.replacements.Fold;
2830
import org.graalvm.nativeimage.ImageSingletons;
2931
import org.graalvm.word.Pointer;
@@ -83,10 +85,16 @@ default UnsignedWord getAlignment() {
8385
* to a multiple of the {@linkplain #getGranularity() granularity}. This value must
8486
* not be 0.
8587
* @param alignment The alignment in bytes of the start of the address range to be reserved.
88+
* @param executable Indicates if memory for code is requested.
8689
* @return An {@linkplain #getAlignment aligned} pointer to the beginning of the reserved
8790
* address range, or {@link WordFactory#nullPointer()} in case of an error.
8891
*/
89-
Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment);
92+
Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment, boolean executable);
93+
94+
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
95+
default Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
96+
return reserve(nbytes, alignment, false);
97+
}
9098

9199
/**
92100
* Map a region of an open file to the specified address range. When {@linkplain Access#WRITE
@@ -183,4 +191,15 @@ default UnsignedWord getAlignment() {
183191
* @return 0 when successful, or a non-zero implementation-specific error code.
184192
*/
185193
int free(PointerBase start, UnsignedWord nbytes);
194+
195+
/**
196+
* Toggle a thread-local flag that tells the OS our intention about the usage of pages that are
197+
* mapped with MAP_JIT. Only applicable on macOS. Only enforced on AArch64.
198+
* @param protect If write protection is enabled, pages mapped with MAP_JIT have effectively
199+
* READ|EXEC permissions for the calling thread. If disabled, the permissions
200+
* for the same pages turn to READ|WRITE.
201+
*/
202+
default void jitWriteProtect(boolean protect) {
203+
VMError.shouldNotReachHere();
204+
}
186205
}

substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/isolated/IsolatedRuntimeCodeInstaller.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,10 @@ private CodeInstallInfo doPrepareInstall() {
129129
private static void installPrepared(SharedMethod method, CodeInstallInfo installInfo, SubstrateInstalledCode installedCode) {
130130
IsolatedRuntimeMethodInfoAccess.startTrackingInCurrentIsolate(installInfo.getCodeInfo());
131131

132+
RuntimeCodeInfoAccess.enableJitWriteProtect(false);
132133
IsolatedReferenceAdjuster.adjustAndDispose(installInfo.getAdjusterData(), IsolatedCompileClient.get().getHandleSet());
133134
installInfo.setAdjusterData(WordFactory.nullPointer());
135+
RuntimeCodeInfoAccess.enableJitWriteProtect(true);
134136

135137
doInstallPrepared(method, installInfo.getCodeInfo(), installedCode);
136138
UnmanagedMemory.free(installInfo);

substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ protected void doPrepareInstall(ReferenceAdjuster adjuster, CodeInfo codeInfo) {
182182
"No direct calls permitted: patching of runtime-compiled code intentionally not supported");
183183
}
184184

185+
RuntimeCodeInfoAccess.enableJitWriteProtect(false);
185186
prepareCodeMemory();
186187

187188
/*
@@ -229,6 +230,8 @@ protected void doPrepareInstall(ReferenceAdjuster adjuster, CodeInfo codeInfo) {
229230
patchDirectObjectConstants(objectConstants, codeInfo, adjuster);
230231

231232
createCodeChunkInfos(codeInfo, adjuster);
233+
234+
RuntimeCodeInfoAccess.enableJitWriteProtect(true);
232235
compilation = null;
233236
}
234237

0 commit comments

Comments
 (0)