Skip to content
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 @@ -27,8 +27,10 @@
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

import org.graalvm.nativeimage.AnnotationAccess;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.graal.pointsto.ClassInclusionPolicy.SharedLayerImageInclusionPolicy;
Expand All @@ -47,6 +49,8 @@
import com.oracle.graal.pointsto.util.Timer;
import com.oracle.graal.pointsto.util.TimerCollection;
import com.oracle.svm.common.meta.MultiMethod;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.util.OriginalClassProvider;

import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
import jdk.graal.compiler.debug.DebugContext;
Expand Down Expand Up @@ -411,6 +415,34 @@ public void tryRegisterFieldForBaseImage(AnalysisField field) {
}
}

@Override
public void tryRegisterNativeMethodsForBaseImage(ResolvedJavaType type) {
/*
* Some modules contain native methods that should not be included in the image because they
* are hosted only, or because they are currently unsupported.
*/
Set<Module> forbiddenModules = hostVM.getForbiddenModules();
if (forbiddenModules.contains(OriginalClassProvider.getJavaClass(type).getModule())) {
return;
}
/*
* Some methods in target classes can be marked as native because the substitution only
* injects an annotation, or provides an alias, without changing the implementation. Those
* methods should not be included in the image.
*/
if (AnnotationAccess.isAnnotationPresent(type, TargetClass.class)) {
return;
}
ResolvedJavaMethod[] methods = tryApply(type, t -> t.getDeclaredMethods(false), NO_METHODS);
for (ResolvedJavaMethod method : methods) {
if (method.isNative()) {
if (getHostVM().isSupportedOriginalMethod(this, method)) {
classInclusionPolicy.includeMethod(method);
}
}
}
}

/**
* Applies {@code function} to {@code type} and returns the result or, if
* {@link NoClassDefFoundError} or {@link IncompatibleClassChangeError} thrown when applying the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,9 @@ default void tryRegisterMethodForBaseImage(AnalysisMethod method) {
default void tryRegisterFieldForBaseImage(AnalysisField field) {

}

@SuppressWarnings("unused")
default void tryRegisterNativeMethodsForBaseImage(ResolvedJavaType analysisType) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
Expand Down Expand Up @@ -456,6 +457,10 @@ public boolean preventConstantFolding(AnalysisField aField) {
return false;
}

public Set<Module> getForbiddenModules() {
return Set.of();
}

/**
* Helpers to determine what analysis actions should be taken for a given Multi-Method version.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
import com.oracle.svm.core.headers.LibC;
import com.oracle.svm.core.heap.PhysicalMemory.PhysicalMemorySupport;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.posix.headers.Sysctl;
import com.oracle.svm.core.posix.headers.darwin.DarwinSysctl;
import com.oracle.svm.core.posix.headers.darwin.Sysctl;
import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks;
import com.oracle.svm.core.traits.BuiltinTraits.RuntimeAccessOnly;
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Disallowed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,27 +77,9 @@ public class PosixLibC {
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native int strcmp(PointerBase s1, PointerBase s2);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native CCharPointer strcpy(CCharPointer dst, CCharPointer src);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native CCharPointer strncpy(CCharPointer dst, CCharPointer src, UnsignedWord len);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native UnsignedWord strlcpy(CCharPointer dst, CCharPointer src, UnsignedWord len);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native CCharPointer strdup(CCharPointer src);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native CCharPointer strtok_r(CCharPointer str, CCharPointer delim, CCharPointerPointer saveptr);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native long strtol(CCharPointer nptr, CCharPointerPointer endptr, int base);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native CCharPointer strstr(CCharPointer str, CCharPointer substr);

@CFunction(transition = CFunction.Transition.NO_TRANSITION)
public static native int isdigit(int c);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025, 2025, 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.posix.headers.darwin;

import org.graalvm.nativeimage.Platform;

import com.oracle.svm.core.posix.headers.PosixDirectives;

public class DarwinDirectives extends PosixDirectives {
@Override
public boolean isInConfiguration() {
return Platform.includedIn(Platform.DARWIN.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.posix.headers;
package com.oracle.svm.core.posix.headers.darwin;

import org.graalvm.nativeimage.c.CContext;
import org.graalvm.nativeimage.c.function.CFunction;
Expand All @@ -35,9 +35,9 @@
/**
* Definitions manually translated from the C header file sys/sysctl.h.
*/
@CContext(PosixDirectives.class)
@CContext(DarwinDirectives.class)
public class Sysctl {

@CFunction
public static native int sysctl(CIntPointer name, long nlen, PointerBase oldval, WordPointer oldlenp, PointerBase newval, long newlen);
public static native int sysctl(CIntPointer name, int nlen, PointerBase oldval, WordPointer oldlenp, PointerBase newval, long newlen);
}
Original file line number Diff line number Diff line change
Expand Up @@ -463,4 +463,8 @@ public void initBuilderModules() {
Module m1 = SVMHost.class.getModule();
builderModules = m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
}

public EconomicSet<Class<?>> getApplicationClasses() {
return applicationClasses;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,7 @@ protected void setupNativeImage(OptionValues options, Map<Method, CEntryPointDat

if (ImageLayerBuildingSupport.buildingSharedLayer()) {
HostedImageLayerBuildingSupport.registerBaseLayerTypes(bb, originalMetaAccess, loader.classLoaderSupport);
HostedImageLayerBuildingSupport.registerNativeMethodsForBaseImage(bb, originalMetaAccess, loader);
}

if (loader.classLoaderSupport.isPreserveMode()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
Expand All @@ -49,6 +51,9 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.c.constant.CConstant;
import org.graalvm.nativeimage.c.function.CEntryPoint;
import org.graalvm.nativeimage.c.function.CLibrary;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.WordBase;

Expand Down Expand Up @@ -109,6 +114,7 @@
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.analysis.SVMParsingSupport;
import com.oracle.svm.hosted.c.libc.HostedLibCBase;
import com.oracle.svm.hosted.classinitialization.ClassInitializationFeature;
import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions;
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
Expand Down Expand Up @@ -151,19 +157,23 @@
import jdk.graal.compiler.debug.GraalError;
import jdk.graal.compiler.debug.MethodFilter;
import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.graph.Node.NodeIntrinsic;
import jdk.graal.compiler.hotspot.word.HotSpotOperation;
import jdk.graal.compiler.java.GraphBuilderPhase.Instance;
import jdk.graal.compiler.nodes.StaticDeoptimizingNode;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext;
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
import jdk.graal.compiler.options.Option;
import jdk.graal.compiler.options.OptionKey;
import jdk.graal.compiler.options.OptionValues;
import jdk.graal.compiler.phases.OptimisticOptimizations;
import jdk.graal.compiler.phases.common.BoxNodeIdentityPhase;
import jdk.graal.compiler.phases.common.CanonicalizerPhase;
import jdk.graal.compiler.virtual.phases.ea.PartialEscapePhase;
import jdk.graal.compiler.word.Word.Operation;
import jdk.internal.loader.NativeLibraries;
import jdk.internal.vm.annotation.DontInline;
import jdk.internal.vm.annotation.ForceInline;
Expand All @@ -174,6 +184,7 @@
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.runtime.JVMCI;

public class SVMHost extends HostVM {
private final ConcurrentHashMap<AnalysisType, DynamicHub> typeToHub = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -225,6 +236,9 @@ public enum UsageKind {

private final SymbolEncoder encoder = SymbolEncoder.singleton();

private com.oracle.svm.hosted.c.NativeLibraries nativeLibraries;
private Collection<Path> allStaticLibNames;

private final int layerId;
private final boolean buildingImageLayer = ImageLayerBuildingSupport.buildingImageLayer();
private final boolean buildingInitialLayer = ImageLayerBuildingSupport.buildingInitialLayer();
Expand All @@ -246,6 +260,12 @@ public enum UsageKind {
private final boolean trackDynamicAccess;
private DynamicAccessDetectionSupport dynamicAccessDetectionSupport = null;

/**
* Some modules contain native methods that should never be in the image, as they are either
* hosted only, or currently unsupported in layered images.
*/
private final Set<Module> forbiddenModules = new HashSet<>();

@SuppressWarnings("this-escape")
public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializationSupport classInitializationSupport, AnnotationSubstitutionProcessor annotationSubstitutions,
MissingRegistrationSupport missingRegistrationSupport) {
Expand Down Expand Up @@ -1082,12 +1102,7 @@ public boolean isSupportedAnalysisMethod(BigBang bb, AnalysisMethod method) {
if (!platformSupported(method)) {
return false;
}
/*
* Methods annotated with @Fold should not be included in the base image as they are
* replaced by the invocation plugin with a constant. If reachable in an extension image,
* the plugin will replace it again.
*/
if (AnnotationAccess.isAnnotationPresent(method, Fold.class)) {
if (!isSupportedMethod(bb, method)) {
return false;
}
return super.isSupportedAnalysisMethod(bb, method);
Expand All @@ -1113,12 +1128,70 @@ public boolean isSupportedOriginalMethod(BigBang bb, ResolvedJavaMethod method)

/* If the method is substituted we need to check the substitution layer for @Fold. */
ResolvedJavaMethod substitutionMethod = bb.getUniverse().getSubstitutions().lookup(method);
if (AnnotationAccess.isAnnotationPresent(substitutionMethod, Fold.class)) {
if (!isSupportedMethod(bb, method) || !isSupportedMethod(bb, substitutionMethod)) {
return false;
}
return super.isSupportedOriginalMethod(bb, method);
}

private boolean isSupportedMethod(BigBang bb, ResolvedJavaMethod method) {
/*
* Methods annotated with @Fold should not be included in the base image as they are
* replaced by the invocation plugin with a constant. If reachable in an extension image,
* the plugin will replace it again.
*/
if (AnnotationAccess.isAnnotationPresent(method, Fold.class)) {
return false;
}

/* Deleted methods should not be included in the image. */
if (AnnotationAccess.isAnnotationPresent(method, Delete.class)) {
return false;
}

/*
* Methods whose graph cannot be created should not be in the image. Those methods are
* compiled in a different way and cannot be included in the same way as normal methods.
*/
if (AnnotationAccess.isAnnotationPresent(method, CConstant.class) || AnnotationAccess.isAnnotationPresent(method, Operation.class) ||
AnnotationAccess.isAnnotationPresent(method, NodeIntrinsic.class) || AnnotationAccess.isAnnotationPresent(method, HotSpotOperation.class)) {
return false;
}

/* Methods that are not provided in the current Libc should not be included. */
if (OriginalMethodProvider.getJavaMethod(method) instanceof Method m && !HostedLibCBase.isMethodProvidedInCurrentLibc(m)) {
return false;
}

/* Methods that are not in the native libraries configuration should not be included. */
if (nativeLibraries == null) {
nativeLibraries = com.oracle.svm.hosted.c.NativeLibraries.singleton();
allStaticLibNames = nativeLibraries.getAllStaticLibNames();
}
if (!nativeLibraries.isMethodInConfiguration(method)) {
return false;
}

/*
* Methods from a CLibrary that is not included in the static libraries of the image should
* not be included.
*/
CLibrary cLibrary = nativeLibraries.getCLibrary(method);
if (cLibrary != null && allStaticLibNames.stream().noneMatch(lib -> lib.toString().contains(cLibrary.value()))) {
return false;
}

/* Methods with an invocation plugin should not be included. */
InvocationPlugins invocationPlugins = getProviders(MultiMethod.ORIGINAL_METHOD).getGraphBuilderPlugins().getInvocationPlugins();
if (invocationPlugins.lookupInvocation(method, bb.getOptions()) != null) {
return false;
}

/* CEntryPoint methods should not be included according to their predicate. */
CEntryPoint cEntryPoint = AnnotationAccess.getAnnotation(method, CEntryPoint.class);
return cEntryPoint == null || ReflectionUtil.newInstance(cEntryPoint.include()).getAsBoolean();
}

/**
* Check if an {@link AnalysisField} should be included in the image. For checking its
* annotations we rely on the {@link AnnotationAccess} unwrapping mechanism to include any
Expand Down Expand Up @@ -1474,4 +1547,28 @@ public SimulateClassInitializerSupport createSimulateClassInitializerSupport(Ana
public ConstantExpressionRegistry getConstantExpressionRegistry() {
return constantExpressionRegistry;
}

@Override
public Set<Module> getForbiddenModules() {
if (forbiddenModules.isEmpty()) {
forbiddenModules.add(JVMCI.class.getModule());
Class<?> llvm = ReflectionUtil.lookupClass(true, "com.oracle.svm.shadowed.org.bytedeco.llvm.global.LLVM");
if (llvm != null) {
forbiddenModules.add(llvm.getModule());
}
Class<?> javacpp = ReflectionUtil.lookupClass(true, "com.oracle.svm.shadowed.org.bytedeco.javacpp.presets.javacpp");
if (javacpp != null) {
forbiddenModules.add(javacpp.getModule());
}
Class<?> truffle = ReflectionUtil.lookupClass(true, "com.oracle.truffle.polyglot.JDKSupport");
if (truffle != null) {
forbiddenModules.add(truffle.getModule());
}
Class<?> libGraal = ReflectionUtil.lookupClass(true, "com.oracle.truffle.runtime.hotspot.libgraal.LibGraal");
if (libGraal != null) {
forbiddenModules.add(libGraal.getModule());
}
}
return forbiddenModules;
}
}
Loading