Skip to content

Commit

Permalink
Add Windows segfault handler
Browse files Browse the repository at this point in the history
  • Loading branch information
pejovica committed Apr 14, 2020
1 parent 9adefb8 commit 79cab7b
Show file tree
Hide file tree
Showing 5 changed files with 345 additions and 1 deletion.
2 changes: 1 addition & 1 deletion substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@
"subDir": "src",
"sourceDirs": ["src"],
"dependencies": [
"com.oracle.svm.core",
"com.oracle.svm.core.graal.amd64",
],
"checkstyle": "com.oracle.svm.core",
"javaCompliance": "8+",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.windows;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.PointerBase;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.RegisterDumper;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.graal.amd64.SubstrateAMD64RegisterConfig;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.core.windows.headers.ErrHandlingAPI.CONTEXT;

import jdk.vm.ci.amd64.AMD64;

@AutomaticFeature
class WindowsRegisterDumperFeature implements Feature {
@Override
public void afterRegistration(AfterRegistrationAccess access) {
VMError.guarantee(AMD64.r14.equals(SubstrateAMD64RegisterConfig.HEAP_BASE_REGISTER_CANDIDATE));
VMError.guarantee(AMD64.r15.equals(SubstrateAMD64RegisterConfig.THREAD_REGISTER_CANDIDATE));
ImageSingletons.add(RegisterDumper.class, new WindowsRegisterDumper());
}
}

public class WindowsRegisterDumper implements RegisterDumper {
@Override
public void dumpRegisters(Log log, Context context) {
dumpRegisters(log, (CONTEXT) context);
}

private static void dumpRegisters(Log log, CONTEXT context) {
log.string("RAX ").zhex(context.Rax()).newline();
log.string("RBX ").zhex(context.Rbx()).newline();
log.string("RCX ").zhex(context.Rcx()).newline();
log.string("RDX ").zhex(context.Rdx()).newline();
log.string("RBP ").zhex(context.Rbp()).newline();
log.string("RSI ").zhex(context.Rsi()).newline();
log.string("RDI ").zhex(context.Rdi()).newline();
log.string("RSP ").zhex(context.Rsp()).newline();
log.string("R8 ").zhex(context.R8()).newline();
log.string("R9 ").zhex(context.R9()).newline();
log.string("R10 ").zhex(context.R10()).newline();
log.string("R11 ").zhex(context.R11()).newline();
log.string("R12 ").zhex(context.R12()).newline();
log.string("R13 ").zhex(context.R13()).newline();
log.string("R14 ").zhex(context.R14()).newline();
log.string("R15 ").zhex(context.R15()).newline();
log.string("EFL ").zhex(context.EFlags()).newline();
log.string("RIP ").zhex(context.Rip()).newline();
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public PointerBase getHeapBase(Context context) {
return WordFactory.pointer(((CONTEXT) context).R14());
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public PointerBase getThreadPointer(Context context) {
return WordFactory.pointer(((CONTEXT) context).R15());
}

@Override
public PointerBase getSP(Context context) {
return WordFactory.pointer(((CONTEXT) context).Rsp());
}

@Override
public PointerBase getIP(Context context) {
return WordFactory.pointer(((CONTEXT) context).Rip());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.windows;

import static com.oracle.svm.core.annotate.RestrictHeapAccess.Access.NO_ALLOCATION;
import static com.oracle.svm.core.annotate.RestrictHeapAccess.Access.NO_HEAP_ACCESS;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.c.function.CEntryPoint;
import org.graalvm.nativeimage.c.function.CEntryPointLiteral;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.svm.core.SubstrateSegfaultHandler;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.RestrictHeapAccess;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.c.function.CEntryPointOptions;
import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue;
import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue;
import com.oracle.svm.core.c.function.CEntryPointOptions.NotIncludedAutomatically;
import com.oracle.svm.core.c.function.CEntryPointOptions.Publish;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.core.windows.headers.ErrHandlingAPI;

@AutomaticFeature
class WindowsSubstrateSegfaultHandlerFeature implements Feature {
@Override
public void afterRegistration(AfterRegistrationAccess access) {
ImageSingletons.add(SubstrateSegfaultHandler.class, new WindowsSubstrateSegfaultHandler());
}
}

class WindowsSubstrateSegfaultHandler extends SubstrateSegfaultHandler {
@Override
protected void install() {
/*
* Normally we would use SEH (Structured Exception Handling) for this. However, in order for
* SEH to work, the OS must be able to perform stack walking. On x64, this requires the
* presence of unwinding info for all methods in the PE image, and we do not currently
* support its generation.
*
* This leaves us with two options to choose from: VEH (Vectored Exception Handler) and VCH
* (Vectored Continue Handler). The problem with VEHs is that they are called too early,
* before any SEH processing, so we can't know if an exception will be handled, and we don't
* want to interfere with the native code.
*
* On the other hand, VCHs are called after SEH processing, but only under certain
* conditions. In fact, this implementation actually relies on the OS to call them
* unconditionally due to the lack of stack-walking support. While this is obviously far
* from ideal, it should be good enough for now.
*/
if (ErrHandlingAPI.AddVectoredContinueHandler(0, HANDLER_LITERAL.getFunctionPointer()).isNull()) {
VMError.shouldNotReachHere("SubstrateSegfaultHandler installation failed.");
}
}

private static final CEntryPointLiteral<CFunctionPointer> HANDLER_LITERAL = CEntryPointLiteral.create(WindowsSubstrateSegfaultHandler.class,
"handler", ErrHandlingAPI.EXCEPTION_POINTERS.class);

@CEntryPoint
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, publishAs = Publish.SymbolOnly, include = NotIncludedAutomatically.class)
@Uninterruptible(reason = "Must be uninterruptible until we get immune to safepoints.")
@RestrictHeapAccess(access = NO_HEAP_ACCESS, reason = "We have yet to enter the isolate.")
private static int handler(ErrHandlingAPI.EXCEPTION_POINTERS exceptionInfo) {
ErrHandlingAPI.EXCEPTION_RECORD exceptionRecord = exceptionInfo.ExceptionRecord();
if (exceptionRecord.ExceptionCode() != ErrHandlingAPI.EXCEPTION_ACCESS_VIOLATION()) {
/* Not a segfault. */
return ErrHandlingAPI.EXCEPTION_CONTINUE_SEARCH();
}

ErrHandlingAPI.CONTEXT context = exceptionInfo.ContextRecord();
if (tryEnterIsolate(context)) {
dump(context);
throw shouldNotReachHere();
}
/* Nothing we can do. */
return ErrHandlingAPI.EXCEPTION_CONTINUE_SEARCH();
}

@Uninterruptible(reason = "Called from uninterruptible code.")
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate in segfault handler.", overridesCallers = true)
private static RuntimeException shouldNotReachHere() {
return VMError.shouldNotReachHere();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.windows.headers;

import static org.graalvm.nativeimage.c.function.CFunction.Transition.NO_TRANSITION;

import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.constant.CConstant;
import org.graalvm.nativeimage.c.function.CFunction;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
import org.graalvm.nativeimage.c.struct.CField;
import org.graalvm.nativeimage.c.struct.CStruct;
import org.graalvm.word.PointerBase;

import com.oracle.svm.core.RegisterDumper;

// Checkstyle: stop

/**
* Definitions for Windows errhandlingapi.h
*/
@CContext(WindowsDirectives.class)
public class ErrHandlingAPI {

/** Registers a vectored continue handler. */
@CFunction(transition = NO_TRANSITION)
public static native PointerBase AddVectoredContinueHandler(int first, CFunctionPointer handler);

@CConstant
public static native int EXCEPTION_CONTINUE_SEARCH();

/** Contains pointers to exception and context records. */
@CStruct
public interface EXCEPTION_POINTERS extends PointerBase {
@CField
EXCEPTION_RECORD ExceptionRecord();

@CField
CONTEXT ContextRecord();
}

/** Contains a description of the exception. */
@CStruct
public interface EXCEPTION_RECORD extends PointerBase {
@CField
int ExceptionCode();
}

@CConstant
public static native int EXCEPTION_ACCESS_VIOLATION();

/** Contains processor-specific register data. */
@CStruct
public interface CONTEXT extends RegisterDumper.Context {
@CField
int EFlags();

@CField
long Rax();

@CField
long Rcx();

@CField
long Rdx();

@CField
long Rbx();

@CField
long Rsp();

@CField
long Rbp();

@CField
long Rsi();

@CField
long Rdi();

@CField
long R8();

@CField
long R9();

@CField
long R10();

@CField
long R11();

@CField
long R12();

@CField
long R13();

@CField
long R14();

@CField
long R15();

@CField
long Rip();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.core;

import static com.oracle.svm.core.annotate.RestrictHeapAccess.Access.NO_ALLOCATION;

import org.graalvm.compiler.options.Option;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.nativeimage.ImageSingletons;
Expand All @@ -36,6 +38,7 @@
import org.graalvm.word.PointerBase;

import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.RestrictHeapAccess;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.c.function.CEntryPointErrors;
import com.oracle.svm.core.graal.nodes.WriteCurrentVMThreadNode;
Expand Down Expand Up @@ -78,6 +81,7 @@ static void startupHook() {

/** Called from the platform dependent segfault handler to enter the isolate. */
@Uninterruptible(reason = "Called from uninterruptible code.")
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate in segfault handler.", overridesCallers = true)
protected static boolean tryEnterIsolate(RegisterDumper.Context context) {
if (SubstrateOptions.SpawnIsolates.getValue()) {
PointerBase heapBase = RegisterDumper.singleton().getHeapBase(context);
Expand All @@ -99,6 +103,7 @@ protected static boolean tryEnterIsolate(RegisterDumper.Context context) {

/** Called from the platform dependent segfault handler to print diagnostics. */
@Uninterruptible(reason = "Must be uninterruptible until we get immune to safepoints.", calleeMustBe = false)
@RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate in segfault handler.", overridesCallers = true)
protected static void dump(RegisterDumper.Context context) {
VMThreads.StatusSupport.setStatusIgnoreSafepoints();
dumpInterruptibly(context);
Expand Down

0 comments on commit 79cab7b

Please sign in to comment.