diff --git a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/runtime/LLVMExceptionUnwind.java b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/runtime/LLVMExceptionUnwind.java index b9953d72aa34..d9387cf12693 100644 --- a/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/runtime/LLVMExceptionUnwind.java +++ b/substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/runtime/LLVMExceptionUnwind.java @@ -25,10 +25,10 @@ package com.oracle.svm.core.graal.llvm.runtime; import static com.oracle.svm.core.util.VMError.shouldNotReachHere; +import static org.graalvm.nativeimage.c.function.CFunction.Transition.NO_TRANSITION; import java.util.function.BooleanSupplier; -import jdk.graal.compiler.core.common.NumUtil; import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.c.CContext; @@ -50,6 +50,7 @@ import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.hosted.code.CEntryPointCallStubSupport; +import jdk.graal.compiler.core.common.NumUtil; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -142,6 +143,7 @@ public static ResolvedJavaMethod getRetrieveExceptionMethod(MetaAccessProvider m public static ExceptionUnwind createRaiseExceptionHandler() { return new ExceptionUnwind() { @Override + @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.") protected void customUnwindException(Pointer callerSP) { _Unwind_Exception exceptionStructure = UnsafeStackValue.get(_Unwind_Exception.class); exceptionStructure.set_exception_class(CurrentIsolate.getCurrentThread()); @@ -198,7 +200,7 @@ private interface _Unwind_Exception extends PointerBase { private interface _Unwind_Context extends PointerBase { } - @CFunction(value = "_Unwind_RaiseException") + @CFunction(value = "_Unwind_RaiseException", transition = NO_TRANSITION) public static native int raiseException(_Unwind_Exception exception); @CFunction(value = "_Unwind_GetIP") diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/DeoptimizedFrame.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/DeoptimizedFrame.java index a0ed358bd5a6..9aa48a651b7d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/DeoptimizedFrame.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/DeoptimizedFrame.java @@ -24,14 +24,14 @@ */ package com.oracle.svm.core.deopt; +import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; + import java.lang.ref.WeakReference; -import jdk.graal.compiler.word.Word; import org.graalvm.nativeimage.PinnedObject; import org.graalvm.nativeimage.c.function.CodePointer; import org.graalvm.word.Pointer; -import com.oracle.svm.core.NeverInline; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.code.CodeInfo; import com.oracle.svm.core.code.CodeInfoAccess; @@ -42,17 +42,15 @@ import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.deopt.Deoptimizer.TargetContent; import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue; -import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.log.StringBuilderLog; import com.oracle.svm.core.meta.SubstrateObjectConstant; import com.oracle.svm.core.monitor.MonitorSupport; import jdk.graal.compiler.nodes.FrameState; +import jdk.graal.compiler.word.Word; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.meta.JavaConstant; -import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; - /** * The handle to a deoptimized frame. It contains all stack entries which are written to the frame * of the deopt target method(s). For details see {@link Deoptimizer}. @@ -407,6 +405,7 @@ protected void buildContent(Pointer newSp) { * deoptimization stub return to the exception handler instead of the regular return address of * the deoptimization target. */ + @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public void takeException() { if (rethrowException) { /* @@ -415,6 +414,7 @@ public void takeException() { */ return; } + ReturnAddress firstAddressEntry = topFrame.returnAddress; CodePointer ip = Word.pointer(firstAddressEntry.returnAddress); CodeInfo info = CodeInfoTable.getImageCodeInfo(ip); @@ -427,13 +427,15 @@ public void takeException() { firstAddressEntry.returnAddress += handler; } - @NeverInline("Has more relaxed heap access requirements than caller.") - @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, reason = "Printing out error and then crashing.") + @Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false) private static void throwMissingExceptionHandler(CodeInfo info, ReturnAddress firstAddressEntry) { + throwMissingExceptionHandler0(info, firstAddressEntry); + } + + private static void throwMissingExceptionHandler0(CodeInfo info, ReturnAddress firstAddressEntry) { CodeInfoQueryResult detailedQueryResult = new CodeInfoQueryResult(); CodeInfoAccess.lookupCodeInfo(info, Word.pointer(firstAddressEntry.returnAddress), detailedQueryResult); FrameInfoQueryResult frameInfo = detailedQueryResult.getFrameInfo(); throw Deoptimizer.fatalDeoptimizationError("No exception handler registered for deopt target", frameInfo); } - } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java index 43381fa815a1..4b9087fd8caf 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/Deoptimizer.java @@ -25,6 +25,7 @@ package com.oracle.svm.core.deopt; import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; +import static com.oracle.svm.core.stack.JavaFrameAnchors.verifyTopFrameAnchor; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -813,7 +814,7 @@ private static boolean isNonNullValue(UnsignedWord pointer) { @DeoptStub(stubType = StubType.LazyEntryStub) @Uninterruptible(reason = "Rewriting stack; gpReturnValue holds object reference.") - public static UnsignedWord lazyDeoptStubObjectReturn(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) { + public static UnsignedWord lazyDeoptStubObjectReturn(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) { try { assert PointerUtils.isAMultiple(KnownIntrinsics.readStackPointer(), Word.unsigned(ConfigurationValues.getTarget().stackAlignment)); assert Options.LazyDeoptimization.getValue(); @@ -828,7 +829,7 @@ public static UnsignedWord lazyDeoptStubObjectReturn(Pointer framePointer, Unsig gpReturnValueObject = ((Pointer) gpReturnValue).toObject(); } - lazyDeoptStubCore(framePointer, gpReturnValue, fpReturnValue, hasException, gpReturnValueObject); + lazyDeoptStubCore(originalStackPointer, gpReturnValue, fpReturnValue, hasException, gpReturnValueObject); throw UnreachableNode.unreachable(); } catch (Throwable t) { @@ -838,7 +839,7 @@ public static UnsignedWord lazyDeoptStubObjectReturn(Pointer framePointer, Unsig @DeoptStub(stubType = StubType.LazyEntryStub) @Uninterruptible(reason = "Rewriting stack.") - public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) { + public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) { /* * Note: when we dispatch an exception, we enter lazyDeoptStubObjectReturn instead, since * that involves returning an exception object. @@ -849,7 +850,7 @@ public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer framePointer, Un assert VMThreads.StatusSupport.isStatusJava() : "Deopt stub execution must not be visible to other threads."; assert !ExceptionUnwind.getLazyDeoptStubShouldReturnToExceptionHandler(); - lazyDeoptStubCore(framePointer, gpReturnValue, fpReturnValue, false, null); + lazyDeoptStubCore(originalStackPointer, gpReturnValue, fpReturnValue, false, null); throw UnreachableNode.unreachable(); } catch (Throwable t) { @@ -864,17 +865,17 @@ public static UnsignedWord lazyDeoptStubPrimitiveReturn(Pointer framePointer, Un * the code info, and construct the {@link DeoptimizedFrame}. */ @Uninterruptible(reason = "Rewriting stack.") - private static UnsignedWord lazyDeoptStubCore(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue, boolean hasException, Object gpReturnValueObject) { - CodePointer deoptStubAddress = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), framePointer); + private static UnsignedWord lazyDeoptStubCore(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue, boolean hasException, Object gpReturnValueObject) { + CodePointer deoptStubAddress = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), originalStackPointer); assert isLazyDeoptStub(deoptStubAddress); /* The original return address is at offset 0 from the stack pointer */ - CodePointer originalReturnAddress = framePointer.readWord(0); + CodePointer originalReturnAddress = originalStackPointer.readWord(0); VMError.guarantee(originalReturnAddress.isNonNull()); DeoptimizedFrame deoptFrame; try { - deoptFrame = constructLazilyDeoptimizedFrameInterruptibly(framePointer, originalReturnAddress, hasException); + deoptFrame = constructLazilyDeoptimizedFrameInterruptibly(originalStackPointer, originalReturnAddress, hasException); } catch (OutOfMemoryError ex) { /* * If a OutOfMemoryError occurs during lazy deoptimization, we cannot let the frame @@ -882,14 +883,14 @@ private static UnsignedWord lazyDeoptStubCore(Pointer framePointer, UnsignedWord * incorrect assumptions. Note that since unwindExceptionSkippingCaller does not return, * this try...catch must not have a finally block, as it will not be executed. */ - ExceptionUnwind.unwindExceptionSkippingCaller(ex, framePointer); + ExceptionUnwind.unwindExceptionSkippingCaller(ex, originalStackPointer); throw UnreachableNode.unreachable(); } DeoptimizationCounters.counters().deoptCount.inc(); VMError.guarantee(deoptFrame != null, "was not able to lazily construct a deoptimized frame"); - Pointer newSp = computeNewFramePointer(framePointer, deoptFrame); + Pointer newSp = computeNewStackPointer(originalStackPointer, deoptFrame); /* Build the content of the deopt target stack frames. */ deoptFrame.buildContent(newSp); @@ -900,9 +901,9 @@ private static UnsignedWord lazyDeoptStubCore(Pointer framePointer, UnsignedWord * can only be called from the current thread. Thus, there is no use case for eager * deoptimization to happen if the current thread is executing the lazy deopt stub. */ - VMError.guarantee(framePointer.readWord(0) == originalReturnAddress, "Eager deoptimization should not occur when lazy deoptimization is in progress"); + VMError.guarantee(originalStackPointer.readWord(0) == originalReturnAddress, "Eager deoptimization should not occur when lazy deoptimization is in progress"); - CodePointer returnAddressAfter = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), framePointer); + CodePointer returnAddressAfter = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), originalStackPointer); VMError.guarantee(returnAddressAfter == deoptStubAddress, "Return address must remain unchanged during deoptimization"); recentDeoptimizationEvents.append(deoptFrame.getCompletedMessage()); @@ -962,7 +963,7 @@ private static DeoptimizedFrame constructLazilyDeoptimizedFrameInterruptibly0(Po * When {@link #eagerDeoptStub} is "called", the stack looks like this: * *
- * : :
+ * : : highest stack address
* | |
* | | frame of the
* +--------------------------------+ deoptimized method
@@ -977,9 +978,8 @@ private static DeoptimizedFrame constructLazilyDeoptimizedFrameInterruptibly0(Po
* The instructions to compute the parameters must be generated in this method's prologue by a
* backend-specific FrameContext class.
*
- * @param framePointer This is a pointer to the reference which was written in
- * {@link #deoptimizeInRange} on the stack (the slot above the original return
- * address).
+ * @param originalStackPointer the original stack pointer of the deoptimized method (points to
+ * the {@link DeoptimizedFrame} object).
* @param gpReturnValue This is the value which was stored in the general purpose return
* register when the deopt stub was reached. It must be restored to the register
* before completion of the stub.
@@ -989,18 +989,19 @@ private static DeoptimizedFrame constructLazilyDeoptimizedFrameInterruptibly0(Po
*/
@DeoptStub(stubType = StubType.EagerEntryStub)
@Uninterruptible(reason = "Frame holds Objects in unmanaged storage.")
- public static UnsignedWord eagerDeoptStub(Pointer framePointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
+ public static UnsignedWord eagerDeoptStub(Pointer originalStackPointer, UnsignedWord gpReturnValue, UnsignedWord fpReturnValue) {
try {
assert PointerUtils.isAMultiple(KnownIntrinsics.readStackPointer(), Word.unsigned(ConfigurationValues.getTarget().stackAlignment));
VMError.guarantee(VMThreads.StatusSupport.isStatusJava(), "Deopt stub execution must not be visible to other threads.");
- DeoptimizedFrame frame = (DeoptimizedFrame) ReferenceAccess.singleton().readObjectAt(framePointer, true);
+
+ DeoptimizedFrame frame = (DeoptimizedFrame) ReferenceAccess.singleton().readObjectAt(originalStackPointer, true);
DeoptimizationCounters.counters().deoptCount.inc();
if (DeoptimizationCounters.Options.ProfileDeoptimization.getValue()) {
DeoptimizationCounters.startTime.set(System.nanoTime());
}
- final Pointer newSp = computeNewFramePointer(framePointer, frame);
+ final Pointer newSp = computeNewStackPointer(originalStackPointer, frame);
/* Build the content of the deopt target stack frames. */
frame.buildContent(newSp);
@@ -1023,13 +1024,15 @@ public static UnsignedWord eagerDeoptStub(Pointer framePointer, UnsignedWord gpR
}
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
- private static Pointer computeNewFramePointer(Pointer originalFramePointer, DeoptimizedFrame deoptimizedFrame) {
- /* Computation of the new stack pointer: we start with the stack pointer of this frame. */
- return originalFramePointer
- /* Remove the size of the frame that gets deoptimized. */
- .add(Word.unsigned(deoptimizedFrame.getSourceTotalFrameSize()))
- /* Add the size of the deoptimization target frames. */
- .subtract(deoptimizedFrame.getTargetContent().getSize());
+ private static Pointer computeNewStackPointer(Pointer originalStackPointer, DeoptimizedFrame deoptimizedFrame) {
+ /* Remove the size of the frame that gets deoptimized. */
+ Pointer callerStackPointer = originalStackPointer.add(Word.unsigned(deoptimizedFrame.getSourceTotalFrameSize()));
+
+ /* Verify that the top frame anchor is in a part of the stack that is not rewritten. */
+ verifyTopFrameAnchor(callerStackPointer);
+
+ /* Add the size of the deoptimization target frames. */
+ return callerStackPointer.subtract(deoptimizedFrame.getTargetContent().getSize());
}
/**
@@ -1054,7 +1057,6 @@ private static UnsignedWord rewriteStackStub(Pointer newSp, UnsignedWord gpRetur
if (DeoptimizationCounters.Options.ProfileDeoptimization.getValue()) {
DeoptimizationCounters.counters().timeSpentInDeopt.add(System.nanoTime() - DeoptimizationCounters.startTime.get());
}
-
return gpReturnValue;
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CFunctionSnippets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CFunctionSnippets.java
index 5b8962aa2641..9ac3c69c6076 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CFunctionSnippets.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CFunctionSnippets.java
@@ -276,14 +276,20 @@ private static void matchCallStructure(CFunctionPrologueNode prologueNode) {
}
}
+/**
+ * {@link CFunctionSnippets} may only be used for code that cannot be deoptimized. Otherwise,
+ * deoptimization could destroy stack allocated {@link JavaFrameAnchor} structs when rewriting the
+ * stack.
+ */
@AutomaticallyRegisteredFeature
@Platforms(InternalPlatform.NATIVE_ONLY.class)
class CFunctionSnippetsFeature implements InternalFeature {
-
@Override
@SuppressWarnings("unused")
public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues options, Providers providers,
Map, NodeLoweringProvider>> lowerings, boolean hosted) {
- new CFunctionSnippets(options, providers, lowerings);
+ if (hosted) {
+ new CFunctionSnippets(options, providers, lowerings);
+ }
}
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java
index 695326c9d102..b751a56a2a03 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java
@@ -25,6 +25,7 @@
package com.oracle.svm.core.snippets;
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
+import static com.oracle.svm.core.stack.JavaFrameAnchors.verifyTopFrameAnchor;
import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.NO_SIDE_EFFECT;
import org.graalvm.nativeimage.CurrentIsolate;
@@ -44,14 +45,12 @@
import com.oracle.svm.core.deopt.DeoptimizationSupport;
import com.oracle.svm.core.deopt.DeoptimizedFrame;
import com.oracle.svm.core.deopt.Deoptimizer;
-import com.oracle.svm.core.heap.RestrictHeapAccess;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
import com.oracle.svm.core.stack.JavaFrame;
import com.oracle.svm.core.stack.JavaFrames;
import com.oracle.svm.core.stack.JavaStackWalk;
import com.oracle.svm.core.stack.JavaStackWalker;
-import com.oracle.svm.core.stack.StackOverflowCheck;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.core.threadlocal.FastThreadLocalBytes;
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
@@ -99,43 +98,25 @@ static boolean exceptionsAreFatal() {
/** Foreign call: {@link #UNWIND_EXCEPTION_WITHOUT_CALLEE_SAVED_REGISTERS}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
- @Uninterruptible(reason = "Must not execute recurring callbacks or a stack overflow check.", calleeMustBe = false)
- @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
+ @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
private static void unwindExceptionWithoutCalleeSavedRegisters(Throwable exception, Pointer callerSP) {
- /*
- * Make the yellow zone available and pause recurring callbacks to avoid that unexpected
- * exceptions are thrown. This is reverted before execution continues in the exception
- * handler (see ExceptionStackFrameVisitor.visitFrame).
- */
- StackOverflowCheck.singleton().makeYellowZoneAvailable();
-
- unwindExceptionInterruptible(exception, callerSP, false, false);
+ unwindException(exception, callerSP, false, false);
}
/** Foreign call: {@link #UNWIND_EXCEPTION_WITH_CALLEE_SAVED_REGISTERS}. */
@SubstrateForeignCallTarget(stubCallingConvention = true)
- @Uninterruptible(reason = "Must not execute recurring callbacks or a stack overflow check.", calleeMustBe = false)
- @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
+ @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
private static void unwindExceptionWithCalleeSavedRegisters(Throwable exception, Pointer callerSP) {
- StackOverflowCheck.singleton().makeYellowZoneAvailable();
-
- unwindExceptionInterruptible(exception, callerSP, true, false);
+ unwindException(exception, callerSP, true, false);
}
- @Uninterruptible(reason = "Must not execute recurring callbacks or a stack overflow check.", calleeMustBe = false)
- @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
+ @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
public static void unwindExceptionSkippingCaller(Throwable exception, Pointer callerSP) {
- StackOverflowCheck.singleton().makeYellowZoneAvailable();
-
- unwindExceptionInterruptible(exception, callerSP, true, true);
+ unwindException(exception, callerSP, true, true);
}
- /*
- * The stack walking objects must be stateless (no instance fields), because multiple threads
- * can use them simultaneously. All state must be in separate VMThreadLocals.
- */
- @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
- private static void unwindExceptionInterruptible(Throwable exception, Pointer callerSP, boolean fromMethodWithCalleeSavedRegisters, boolean skipCaller) {
+ @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
+ private static void unwindException(Throwable exception, Pointer callerSP, boolean fromMethodWithCalleeSavedRegisters, boolean skipCaller) {
if (currentException.get() != null) {
reportRecursiveUnwind(exception);
return; /* Unreachable code. */
@@ -167,6 +148,7 @@ private static void unwindExceptionInterruptible(Throwable exception, Pointer ca
* Exception unwinding cannot be called recursively. The most likely reason to end up here is an
* exception being thrown while walking the stack to find an exception handler.
*/
+ @Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
private static void reportRecursiveUnwind(Throwable exception) {
Log.log().string("Fatal error: recursion in exception handling: ").string(exception.getClass().getName());
Log.log().string(" thrown while unwinding ").string(currentException.get().getClass().getName()).newline().newline();
@@ -181,6 +163,7 @@ private static void reportRecursiveUnwind(Throwable exception) {
* exception checks such as null pointer or array bounds checks. In such cases, exceptions are
* treated as fatal errors.
*/
+ @Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
private static void reportFatalUnwind(Throwable exception) {
Log.log().string("Fatal error: exception unwind while thread is not in Java state: ");
Log.log().exception(exception).newline().newline();
@@ -193,6 +176,7 @@ private static void reportFatalUnwind(Throwable exception) {
* proper exception handling and reporting of "unhandled" user exceptions is at a higher level
* using a normal Java catch-all exception handler.
*/
+ @Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
private static void reportUnhandledException(Throwable exception) {
Log.log().string("Fatal error: unhandled exception in isolate ").hex(CurrentIsolate.getIsolate()).string(": ");
Log.log().exception(exception).newline().newline();
@@ -200,6 +184,7 @@ private static void reportUnhandledException(Throwable exception) {
}
/** Hook to allow a {@link Feature} to install custom exception unwind code. */
+ @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
protected abstract void customUnwindException(Pointer callerSP);
/**
@@ -211,7 +196,7 @@ private static void reportUnhandledException(Throwable exception) {
* @param skipCaller Whether the first (caller) frame should be skipped. If this is true, then
* the value of fromMethodWithCalleeSavedRegisters will be ignored.
*/
- @Uninterruptible(reason = "Prevent deoptimization apart from the few places explicitly considered safe for deoptimization")
+ @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
private static void defaultUnwindException(Pointer startSP, boolean fromMethodWithCalleeSavedRegisters, boolean skipCaller) {
IsolateThread thread = CurrentIsolate.getCurrentThread();
boolean hasCalleeSavedRegisters = fromMethodWithCalleeSavedRegisters;
@@ -235,7 +220,7 @@ private static void defaultUnwindException(Pointer startSP, boolean fromMethodWi
DeoptimizedFrame deoptFrame = Deoptimizer.checkEagerDeoptimized(frame);
if (deoptFrame != null) {
/* Deoptimization entry points always have an exception handler. */
- deoptTakeExceptionInterruptible(deoptFrame);
+ deoptFrame.takeException();
jumpToHandler(sp, DeoptimizationSupport.getEagerDeoptStubPointer(), hasCalleeSavedRegisters);
UnreachableNode.unreachable();
return; /* Unreachable */
@@ -272,13 +257,13 @@ private static void defaultUnwindException(Pointer startSP, boolean fromMethodWi
}
}
- @Uninterruptible(reason = "Prevent deoptimization while dispatching to exception handler")
+ @Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
private static void jumpToHandler(Pointer sp, CodePointer handlerIP, boolean hasCalleeSavedRegisters) {
+ verifyTopFrameAnchor(sp);
+
Throwable exception = currentException.get();
currentException.set(null);
- StackOverflowCheck.singleton().protectYellowZone();
-
if (hasCalleeSavedRegisters) {
/*
* The fromMethodWithCalleeSavedRegisters parameter of farReturn must be a compile-time
@@ -291,10 +276,4 @@ private static void jumpToHandler(Pointer sp, CodePointer handlerIP, boolean has
}
/* Unreachable code: the intrinsic performs a jump to the specified instruction pointer. */
}
-
- @Uninterruptible(reason = "Wrap call to interruptible code.", calleeMustBe = false)
- private static void deoptTakeExceptionInterruptible(DeoptimizedFrame deoptFrame) {
- deoptFrame.takeException();
- }
-
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java
index f53e995ed585..99a90fb38008 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/JavaFrameAnchors.java
@@ -27,7 +27,6 @@
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
import static jdk.graal.compiler.core.common.spi.ForeignCallDescriptor.CallSideEffect.NO_SIDE_EFFECT;
-import jdk.graal.compiler.word.Word;
import org.graalvm.nativeimage.CurrentIsolate;
import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.word.Pointer;
@@ -38,6 +37,7 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider;
+import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.snippets.SnippetRuntime;
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
@@ -52,6 +52,7 @@
import jdk.graal.compiler.core.common.spi.ForeignCallDescriptor;
import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.nodes.extended.ForeignCallNode;
+import jdk.graal.compiler.word.Word;
/**
* Maintains the linked list of {@link JavaFrameAnchor} for stack walking. Note that a thread may
@@ -140,14 +141,46 @@ private static void verifyFrameAnchorStub(boolean newAnchor) {
}
}
+ /**
+ * Verifies that the top {@link JavaFrameAnchor} is in a part of the stack that is safe to
+ * access. This method should be called whenever code manipulates the Java stack significantly.
+ */
+ @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
+ public static void verifyTopFrameAnchor(Pointer safeStackBegin) {
+ Pointer anchor = (Pointer) JavaFrameAnchors.getFrameAnchor();
+ if (anchor.isNonNull()) {
+ VMError.guarantee(anchor.aboveOrEqual(safeStackBegin), "The frame anchor struct is outside of the stack that is safe to use.");
+ }
+ }
+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
private static void verifyFrameAnchor(JavaFrameAnchor cur, boolean newAnchor) {
VMError.guarantee(StatusSupport.getStatusVolatile() == StatusSupport.STATUS_IN_JAVA, "Invalid thread status.");
- VMError.guarantee(((Pointer) cur).aboveOrEqual(KnownIntrinsics.readStackPointer()), "The frame anchor struct is outside of the used stack.");
- VMError.guarantee(cur.getMagicBefore() == JavaFrameAnchor.MAGIC, "Corrupt frame anchor: magic before");
- VMError.guarantee(cur.getMagicAfter() == JavaFrameAnchor.MAGIC, "Corrupt frame anchor: magic after");
- VMError.guarantee(newAnchor == cur.getLastJavaIP().isNull(), "Corrupt frame anchor: invalid IP");
- VMError.guarantee(newAnchor == cur.getLastJavaSP().isNull(), "Corrupt frame anchor: invalid SP");
+ if (((Pointer) cur).belowThan(KnownIntrinsics.readStackPointer())) {
+ throw verificationFailed(cur, "The frame anchor struct is outside of the used stack.");
+ }
+ if (cur.getMagicBefore() != JavaFrameAnchor.MAGIC) {
+ throw verificationFailed(cur, "Magic number at the start of the frame anchor is corrupt.");
+ }
+ if (cur.getMagicAfter() != JavaFrameAnchor.MAGIC) {
+ throw verificationFailed(cur, "Magic number at the end of the frame anchor is corrupt.");
+ }
+ if (newAnchor != cur.getLastJavaIP().isNull()) {
+ throw verificationFailed(cur, "Invalid IP");
+ }
+ if (newAnchor != cur.getLastJavaSP().isNull()) {
+ throw verificationFailed(cur, "Invalid SP");
+ }
+ }
+
+ @Uninterruptible(reason = "No need to be uninterruptible because we are terminating with a fatal error.", calleeMustBe = false)
+ private static RuntimeException verificationFailed(JavaFrameAnchor cur, String msg) {
+ Log log = Log.log();
+ log.string("Frame anchor verification failed: ").string(msg).newline();
+ log.string("Anchor ").zhex(cur).string(" MagicBefore ").zhex(cur.getMagicBefore()).string(" LastJavaSP ").zhex(cur.getLastJavaSP()).string(" LastJavaIP ").zhex(cur.getLastJavaIP())
+ .string(" Previous ").zhex(cur.getPreviousAnchor()).string(" MagicAfter ").zhex(cur.getMagicAfter()).newline();
+
+ throw VMError.shouldNotReachHere(msg);
}
@Node.NodeIntrinsic(value = ForeignCallNode.class)