Skip to content

Commit 442fcd2

Browse files
committed
darwin-aarch64: add support for MAP_JIT/pthread_jit_write_protect_np
1 parent 9fa9dbb commit 442fcd2

File tree

12 files changed

+273
-152
lines changed

12 files changed

+273
-152
lines changed

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GCImpl.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ private static PinnedObjectImpl removeClosedPinnedObjects(PinnedObjectImpl list)
788788
@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)
789789
private void blackenStackRoots() {
790790
Timer blackenStackRootsTimer = timers.blackenStackRoots.open();
791+
RuntimeCodeInfoAccess.acquireThreadWriteAccess();
791792
try {
792793
Pointer sp = readCallerStackPointer();
793794
CodePointer ip = readReturnAddress();
@@ -818,6 +819,7 @@ private void blackenStackRoots() {
818819
}
819820
} finally {
820821
blackenStackRootsTimer.close();
822+
RuntimeCodeInfoAccess.releaseThreadWriteAccess();
821823
}
822824
}
823825

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

Lines changed: 20 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;
@@ -107,7 +110,7 @@ public UnsignedWord getGranularity() {
107110

108111
@Override
109112
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
110-
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
113+
public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment, boolean executable) {
111114
if (nbytes.equal(0)) {
112115
return WordFactory.nullPointer();
113116
}
@@ -119,7 +122,11 @@ public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) {
119122
mappingSize = mappingSize.add(alignment);
120123
}
121124
mappingSize = UnsignedUtils.roundUp(mappingSize, granularity);
122-
Pointer mappingBegin = mmap(nullPointer(), mappingSize, PROT_NONE(), MAP_ANON() | MAP_PRIVATE() | MAP_NORESERVE(), NO_FD, NO_FD_OFFSET);
125+
int flags = MAP_ANON() | MAP_PRIVATE() | MAP_NORESERVE();
126+
if (Platform.includedIn(Platform.DARWIN_AARCH64.class) && executable) {
127+
flags |= MAP_JIT();
128+
}
129+
Pointer mappingBegin = mmap(nullPointer(), mappingSize, PROT_NONE(), flags, NO_FD, NO_FD_OFFSET);
123130
if (mappingBegin.equal(MAP_FAILED())) {
124131
return nullPointer();
125132
}
@@ -167,6 +174,11 @@ public Pointer commit(PointerBase start, UnsignedWord nbytes, int access) {
167174
if (start.isNonNull()) {
168175
flags |= MAP_FIXED();
169176
}
177+
178+
boolean isWX = (access & Access.WRITE) != 0 && (access & Access.EXECUTE) != 0;
179+
if (Platform.includedIn(Platform.DARWIN_AARCH64.class) && isWX) {
180+
flags |= MAP_JIT();
181+
}
170182
/* The memory returned by mmap is guaranteed to be zeroed. */
171183
final Pointer result = mmap(start, nbytes, accessAsProt(access), flags, NO_FD, NO_FD_OFFSET);
172184
return result.notEqual(MAP_FAILED()) ? result : nullPointer();
@@ -182,6 +194,12 @@ public int protect(PointerBase start, UnsignedWord nbytes, int access) {
182194
return mprotect(start, nbytes, accessAsProt(access));
183195
}
184196

197+
@Override
198+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
199+
public void jitWriteProtect(boolean protect) {
200+
DarwinPthread.pthread_jit_write_protect_np(protect ? 1 : 0);
201+
}
202+
185203
@Override
186204
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
187205
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: 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.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;
@@ -49,4 +51,8 @@ public class DarwinPthread {
4951

5052
@CFunction(transition = Transition.NO_TRANSITION)
5153
public static native Pointer pthread_get_stackaddr_np(Pthread.pthread_t thread);
54+
55+
@Platforms(Platform.DARWIN_AARCH64.class)
56+
@CFunction(transition = Transition.NO_TRANSITION)
57+
public static native void pthread_jit_write_protect_np(int enabled);
5258
}

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: 31 additions & 0 deletions
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;
@@ -248,6 +253,32 @@ public static void makeCodeMemoryWriteableNonExecutable(CodePointer start, Unsig
248253
CommittedMemoryProvider.get().protect(start, size, EnumSet.of(CommittedMemoryProvider.Access.READ, CommittedMemoryProvider.Access.WRITE));
249254
}
250255

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

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -203,18 +203,23 @@ public boolean walkRuntimeMethodsDuringGC(CodeInfoVisitor visitor) {
203203
assert VMOperation.isGCInProgress() : "otherwise, we would need to make sure that the CodeInfo is not freeded by the GC";
204204
if (table.isNonNull()) {
205205
int length = NonmovableArrays.lengthOf(table);
206-
for (int i = 0; i < length;) {
207-
UntetheredCodeInfo info = NonmovableArrays.getWord(table, i);
208-
if (info.isNonNull()) {
209-
visitor.visitCode(CodeInfoAccess.convert(info));
210-
}
206+
RuntimeCodeInfoAccess.acquireThreadWriteAccess();
207+
try {
208+
for (int i = 0; i < length;) {
209+
UntetheredCodeInfo info = NonmovableArrays.getWord(table, i);
210+
if (info.isNonNull()) {
211+
visitor.visitCode(CodeInfoAccess.convert(info));
212+
}
211213

212-
// If the visitor removed the current entry from the table, then it is necessary to
213-
// visit the now updated entry one more time. However, this could have the effect
214-
// that some entries are visited more than once.
215-
if (info == NonmovableArrays.getWord(table, i)) {
216-
i++;
214+
// If the visitor removed the current entry from the table, then it is necessary
215+
// to visit the now updated entry one more time. However, this could have the
216+
// effect that some entries are visited more than once.
217+
if (info == NonmovableArrays.getWord(table, i)) {
218+
i++;
219+
}
217220
}
221+
} finally {
222+
RuntimeCodeInfoAccess.releaseThreadWriteAccess();
218223
}
219224
}
220225
return true;

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

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_END;
2929
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_WRITABLE_BEGIN;
3030
import static com.oracle.svm.core.Isolates.IMAGE_HEAP_WRITABLE_END;
31+
import static org.graalvm.word.WordFactory.nullPointer;
3132

3233
import java.util.EnumSet;
3334

35+
import com.oracle.svm.core.util.UnsignedUtils;
3436
import org.graalvm.compiler.api.replacements.Fold;
3537
import org.graalvm.word.Pointer;
3638
import org.graalvm.word.PointerBase;
@@ -40,6 +42,7 @@
4042
import com.oracle.svm.core.annotate.Uninterruptible;
4143
import com.oracle.svm.core.c.function.CEntryPointErrors;
4244
import com.oracle.svm.core.heap.Heap;
45+
import org.graalvm.word.WordFactory;
4346

4447
public abstract class AbstractCommittedMemoryProvider implements CommittedMemoryProvider {
4548
@Fold
@@ -94,4 +97,116 @@ public boolean protect(PointerBase start, UnsignedWord nbytes, EnumSet<Access> a
9497
int success = VirtualMemoryProvider.get().protect(start, nbytes, vmAccessBits);
9598
return success == 0;
9699
}
100+
101+
@Override
102+
public Pointer allocateAlignedChunk(UnsignedWord nbytes, UnsignedWord alignment) {
103+
return allocate(nbytes, alignment, false);
104+
}
105+
106+
@Override
107+
public Pointer allocateUnalignedChunk(UnsignedWord nbytes) {
108+
return allocate(nbytes, WordFactory.unsigned(1), false);
109+
}
110+
111+
@Override
112+
public Pointer allocateExecutableMemory(UnsignedWord nbytes, UnsignedWord alignment) {
113+
return allocate(nbytes, alignment, true);
114+
}
115+
116+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
117+
private Pointer allocate(UnsignedWord size, UnsignedWord alignment, boolean executable) {
118+
int access = VirtualMemoryProvider.Access.READ | VirtualMemoryProvider.Access.WRITE;
119+
if (executable) {
120+
access |= VirtualMemoryProvider.Access.EXECUTE;
121+
}
122+
Pointer reserved = WordFactory.nullPointer();
123+
if (!UnsignedUtils.isAMultiple(getGranularity(), alignment)) {
124+
reserved = VirtualMemoryProvider.get().reserve(size, alignment, executable);
125+
if (reserved.isNull()) {
126+
return nullPointer();
127+
}
128+
}
129+
Pointer committed = VirtualMemoryProvider.get().commit(reserved, size, access);
130+
if (committed.isNull()) {
131+
if (reserved.isNonNull()) {
132+
VirtualMemoryProvider.get().free(reserved, size);
133+
}
134+
return nullPointer();
135+
}
136+
assert reserved.isNull() || reserved.equal(committed);
137+
track(size);
138+
return committed;
139+
}
140+
141+
@Override
142+
public boolean areUnalignedChunksZeroed() {
143+
return false;
144+
}
145+
146+
@Override
147+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
148+
public void freeAlignedChunk(PointerBase start, UnsignedWord nbytes, UnsignedWord alignment) {
149+
free(start, nbytes);
150+
}
151+
152+
@Override
153+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
154+
public void freeUnalignedChunk(PointerBase start, UnsignedWord nbytes) {
155+
free(start, nbytes);
156+
}
157+
158+
@Override
159+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
160+
public void freeExecutableMemory(PointerBase start, UnsignedWord nbytes, UnsignedWord alignment) {
161+
free(start, nbytes);
162+
}
163+
164+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
165+
private void free(PointerBase start, UnsignedWord nbytes) {
166+
if (VirtualMemoryProvider.get().free(start, nbytes) == 0) {
167+
untrack(nbytes);
168+
}
169+
}
170+
171+
private final VirtualMemoryTracker tracker = new VirtualMemoryTracker();
172+
173+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
174+
public VirtualMemoryTracker getTracker() {
175+
return tracker;
176+
}
177+
178+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
179+
private void track(UnsignedWord nbytes) {
180+
if (getTracker() == null) {
181+
return;
182+
}
183+
getTracker().track(nbytes);
184+
}
185+
186+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
187+
private void untrack(UnsignedWord nbytes) {
188+
if (getTracker() == null) {
189+
return;
190+
}
191+
getTracker().untrack(nbytes);
192+
}
193+
194+
public static class VirtualMemoryTracker {
195+
196+
private UnsignedWord totalAllocated;
197+
198+
public VirtualMemoryTracker() {
199+
this.totalAllocated = WordFactory.zero();
200+
}
201+
202+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
203+
public void track(UnsignedWord size) {
204+
totalAllocated = totalAllocated.add(size);
205+
}
206+
207+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
208+
public void untrack(UnsignedWord size) {
209+
totalAllocated = totalAllocated.subtract(size);
210+
}
211+
}
97212
}

0 commit comments

Comments
 (0)