Skip to content

Commit 0450109

Browse files
committed
Make native methods layer aware
1 parent 4cb2f12 commit 0450109

File tree

13 files changed

+241
-29
lines changed

13 files changed

+241
-29
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/AbstractAnalysisEngine.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@
2727
import java.io.PrintWriter;
2828
import java.util.Collections;
2929
import java.util.List;
30+
import java.util.Set;
3031
import java.util.function.Function;
3132

33+
import org.graalvm.nativeimage.AnnotationAccess;
3234
import org.graalvm.nativeimage.hosted.Feature;
3335

3436
import com.oracle.graal.pointsto.ClassInclusionPolicy.SharedLayerImageInclusionPolicy;
3537
import com.oracle.graal.pointsto.api.HostVM;
3638
import com.oracle.graal.pointsto.api.PointstoOptions;
3739
import com.oracle.graal.pointsto.constraints.UnsupportedFeatures;
40+
import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider;
3841
import com.oracle.graal.pointsto.meta.AnalysisField;
3942
import com.oracle.graal.pointsto.meta.AnalysisMetaAccess;
4043
import com.oracle.graal.pointsto.meta.AnalysisMethod;
@@ -47,6 +50,7 @@
4750
import com.oracle.graal.pointsto.util.Timer;
4851
import com.oracle.graal.pointsto.util.TimerCollection;
4952
import com.oracle.svm.common.meta.MultiMethod;
53+
import com.oracle.svm.core.annotate.TargetClass;
5054

5155
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
5256
import jdk.graal.compiler.debug.DebugContext;
@@ -411,6 +415,34 @@ public void tryRegisterFieldForBaseImage(AnalysisField field) {
411415
}
412416
}
413417

418+
@Override
419+
public void tryRegisterNativeMethodsForBaseImage(ResolvedJavaType type) {
420+
/*
421+
* Some modules contain native methods that should not be included in the image because they
422+
* are hosted only, or because they are currently unsupported.
423+
*/
424+
Set<Module> forbiddenModules = hostVM.getForbiddenModules();
425+
if (forbiddenModules.contains(OriginalClassProvider.getJavaClass(type).getModule())) {
426+
return;
427+
}
428+
/*
429+
* Some methods in target classes can be marked as native because the substitution only
430+
* injects an annotation, or provides an alias, without changing the implementation. Those
431+
* methods should not be included in the image.
432+
*/
433+
if (AnnotationAccess.isAnnotationPresent(type, TargetClass.class)) {
434+
return;
435+
}
436+
ResolvedJavaMethod[] methods = tryApply(type, t -> t.getDeclaredMethods(false), NO_METHODS);
437+
for (ResolvedJavaMethod method : methods) {
438+
if (method.isNative()) {
439+
if (getHostVM().isSupportedOriginalMethod(this, method)) {
440+
classInclusionPolicy.includeMethod(method);
441+
}
442+
}
443+
}
444+
}
445+
414446
/**
415447
* Applies {@code function} to {@code type} and returns the result or, if
416448
* {@link NoClassDefFoundError} or {@link IncompatibleClassChangeError} thrown when applying the

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,9 @@ default void tryRegisterMethodForBaseImage(AnalysisMethod method) {
159159
default void tryRegisterFieldForBaseImage(AnalysisField field) {
160160

161161
}
162+
163+
@SuppressWarnings("unused")
164+
default void tryRegisterNativeMethodsForBaseImage(ResolvedJavaType analysisType) {
165+
166+
}
162167
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Comparator;
3333
import java.util.List;
3434
import java.util.Optional;
35+
import java.util.Set;
3536
import java.util.concurrent.CopyOnWriteArrayList;
3637
import java.util.function.BiConsumer;
3738
import java.util.function.Predicate;
@@ -456,6 +457,10 @@ public boolean preventConstantFolding(AnalysisField aField) {
456457
return false;
457458
}
458459

460+
public Set<Module> getForbiddenModules() {
461+
return Set.of();
462+
}
463+
459464
/**
460465
* Helpers to determine what analysis actions should be taken for a given Multi-Method version.
461466
*/

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPhysicalMemorySupportImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
import com.oracle.svm.core.headers.LibC;
3535
import com.oracle.svm.core.heap.PhysicalMemory.PhysicalMemorySupport;
3636
import com.oracle.svm.core.log.Log;
37-
import com.oracle.svm.core.posix.headers.Sysctl;
3837
import com.oracle.svm.core.posix.headers.darwin.DarwinSysctl;
38+
import com.oracle.svm.core.posix.headers.darwin.Sysctl;
3939
import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks;
4040
import com.oracle.svm.core.traits.BuiltinTraits.RuntimeAccessOnly;
4141
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Disallowed;

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

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -77,27 +77,9 @@ public class PosixLibC {
7777
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
7878
public static native int strcmp(PointerBase s1, PointerBase s2);
7979

80-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
81-
public static native CCharPointer strcpy(CCharPointer dst, CCharPointer src);
82-
83-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
84-
public static native CCharPointer strncpy(CCharPointer dst, CCharPointer src, UnsignedWord len);
85-
86-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
87-
public static native UnsignedWord strlcpy(CCharPointer dst, CCharPointer src, UnsignedWord len);
88-
8980
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
9081
public static native CCharPointer strdup(CCharPointer src);
9182

92-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
93-
public static native CCharPointer strtok_r(CCharPointer str, CCharPointer delim, CCharPointerPointer saveptr);
94-
95-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
96-
public static native long strtol(CCharPointer nptr, CCharPointerPointer endptr, int base);
97-
98-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
99-
public static native CCharPointer strstr(CCharPointer str, CCharPointer substr);
100-
10183
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
10284
public static native int isdigit(int c);
10385

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.posix.headers.darwin;
26+
27+
import org.graalvm.nativeimage.Platform;
28+
29+
import com.oracle.svm.core.posix.headers.PosixDirectives;
30+
31+
public class DarwinDirectives extends PosixDirectives {
32+
@Override
33+
public boolean isInConfiguration() {
34+
return Platform.includedIn(Platform.DARWIN.class);
35+
}
36+
}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Sysctl.java renamed to substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/Sysctl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* or visit www.oracle.com if you need additional information or have any
2323
* questions.
2424
*/
25-
package com.oracle.svm.core.posix.headers;
25+
package com.oracle.svm.core.posix.headers.darwin;
2626

2727
import org.graalvm.nativeimage.c.CContext;
2828
import org.graalvm.nativeimage.c.function.CFunction;
@@ -35,9 +35,9 @@
3535
/**
3636
* Definitions manually translated from the C header file sys/sysctl.h.
3737
*/
38-
@CContext(PosixDirectives.class)
38+
@CContext(DarwinDirectives.class)
3939
public class Sysctl {
4040

4141
@CFunction
42-
public static native int sysctl(CIntPointer name, long nlen, PointerBase oldval, WordPointer oldlenp, PointerBase newval, long newlen);
42+
public static native int sysctl(CIntPointer name, int nlen, PointerBase oldval, WordPointer oldlenp, PointerBase newval, long newlen);
4343
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,4 +463,8 @@ public void initBuilderModules() {
463463
Module m1 = SVMHost.class.getModule();
464464
builderModules = m0.equals(m1) ? Set.of(m0) : Set.of(m0, m1);
465465
}
466+
467+
public EconomicSet<Class<?>> getApplicationClasses() {
468+
return applicationClasses;
469+
}
466470
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,7 @@ protected void setupNativeImage(OptionValues options, Map<Method, CEntryPointDat
11091109

11101110
if (ImageLayerBuildingSupport.buildingSharedLayer()) {
11111111
HostedImageLayerBuildingSupport.registerBaseLayerTypes(bb, originalMetaAccess, loader.classLoaderSupport);
1112+
HostedImageLayerBuildingSupport.registerNativeMethodsForBaseImage(bb, originalMetaAccess, loader);
11121113
}
11131114

11141115
if (loader.classLoaderSupport.isPreserveMode()) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import java.lang.reflect.Method;
3131
import java.lang.reflect.Modifier;
3232
import java.lang.reflect.Proxy;
33+
import java.nio.file.Path;
34+
import java.util.Collection;
3335
import java.util.Comparator;
3436
import java.util.EnumSet;
3537
import java.util.HashMap;
@@ -49,6 +51,9 @@
4951
import org.graalvm.nativeimage.ImageSingletons;
5052
import org.graalvm.nativeimage.Platform;
5153
import org.graalvm.nativeimage.Platforms;
54+
import org.graalvm.nativeimage.c.constant.CConstant;
55+
import org.graalvm.nativeimage.c.function.CEntryPoint;
56+
import org.graalvm.nativeimage.c.function.CLibrary;
5257
import org.graalvm.nativeimage.hosted.Feature;
5358
import org.graalvm.word.WordBase;
5459

@@ -113,6 +118,7 @@
113118
import com.oracle.svm.core.util.UserError;
114119
import com.oracle.svm.core.util.VMError;
115120
import com.oracle.svm.hosted.analysis.SVMParsingSupport;
121+
import com.oracle.svm.hosted.c.libc.HostedLibCBase;
116122
import com.oracle.svm.hosted.classinitialization.ClassInitializationFeature;
117123
import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions;
118124
import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport;
@@ -151,19 +157,23 @@
151157
import jdk.graal.compiler.debug.GraalError;
152158
import jdk.graal.compiler.debug.MethodFilter;
153159
import jdk.graal.compiler.graph.Node;
160+
import jdk.graal.compiler.graph.Node.NodeIntrinsic;
161+
import jdk.graal.compiler.hotspot.word.HotSpotOperation;
154162
import jdk.graal.compiler.java.GraphBuilderPhase.Instance;
155163
import jdk.graal.compiler.nodes.StaticDeoptimizingNode;
156164
import jdk.graal.compiler.nodes.StructuredGraph;
157165
import jdk.graal.compiler.nodes.ValueNode;
158166
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
159167
import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext;
168+
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
160169
import jdk.graal.compiler.options.Option;
161170
import jdk.graal.compiler.options.OptionKey;
162171
import jdk.graal.compiler.options.OptionValues;
163172
import jdk.graal.compiler.phases.OptimisticOptimizations;
164173
import jdk.graal.compiler.phases.common.BoxNodeIdentityPhase;
165174
import jdk.graal.compiler.phases.common.CanonicalizerPhase;
166175
import jdk.graal.compiler.virtual.phases.ea.PartialEscapePhase;
176+
import jdk.graal.compiler.word.Word.Operation;
167177
import jdk.internal.loader.NativeLibraries;
168178
import jdk.internal.vm.annotation.DontInline;
169179
import jdk.internal.vm.annotation.ForceInline;
@@ -174,6 +184,7 @@
174184
import jdk.vm.ci.meta.ResolvedJavaField;
175185
import jdk.vm.ci.meta.ResolvedJavaMethod;
176186
import jdk.vm.ci.meta.ResolvedJavaType;
187+
import jdk.vm.ci.runtime.JVMCI;
177188

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

226237
private final SymbolEncoder encoder = SymbolEncoder.singleton();
227238

239+
private com.oracle.svm.hosted.c.NativeLibraries nativeLibraries;
240+
private Collection<Path> allStaticLibNames;
241+
228242
private final int layerId;
229243
private final boolean buildingImageLayer = ImageLayerBuildingSupport.buildingImageLayer();
230244
private final boolean buildingInitialLayer = ImageLayerBuildingSupport.buildingInitialLayer();
@@ -246,6 +260,12 @@ public enum UsageKind {
246260
private final boolean trackDynamicAccess;
247261
private DynamicAccessDetectionSupport dynamicAccessDetectionSupport = null;
248262

263+
/**
264+
* Some modules contain native methods that should never be in the image, as they are either
265+
* hosted only, or currently unsupported in layered images.
266+
*/
267+
private final Set<Module> forbiddenModules = new HashSet<>();
268+
249269
@SuppressWarnings("this-escape")
250270
public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializationSupport classInitializationSupport, AnnotationSubstitutionProcessor annotationSubstitutions,
251271
MissingRegistrationSupport missingRegistrationSupport) {
@@ -1082,12 +1102,7 @@ public boolean isSupportedAnalysisMethod(BigBang bb, AnalysisMethod method) {
10821102
if (!platformSupported(method)) {
10831103
return false;
10841104
}
1085-
/*
1086-
* Methods annotated with @Fold should not be included in the base image as they are
1087-
* replaced by the invocation plugin with a constant. If reachable in an extension image,
1088-
* the plugin will replace it again.
1089-
*/
1090-
if (AnnotationAccess.isAnnotationPresent(method, Fold.class)) {
1105+
if (!isSupportedMethod(bb, method)) {
10911106
return false;
10921107
}
10931108
return super.isSupportedAnalysisMethod(bb, method);
@@ -1113,12 +1128,70 @@ public boolean isSupportedOriginalMethod(BigBang bb, ResolvedJavaMethod method)
11131128

11141129
/* If the method is substituted we need to check the substitution layer for @Fold. */
11151130
ResolvedJavaMethod substitutionMethod = bb.getUniverse().getSubstitutions().lookup(method);
1116-
if (AnnotationAccess.isAnnotationPresent(substitutionMethod, Fold.class)) {
1131+
if (!isSupportedMethod(bb, method) || !isSupportedMethod(bb, substitutionMethod)) {
11171132
return false;
11181133
}
11191134
return super.isSupportedOriginalMethod(bb, method);
11201135
}
11211136

1137+
private boolean isSupportedMethod(BigBang bb, ResolvedJavaMethod method) {
1138+
/*
1139+
* Methods annotated with @Fold should not be included in the base image as they are
1140+
* replaced by the invocation plugin with a constant. If reachable in an extension image,
1141+
* the plugin will replace it again.
1142+
*/
1143+
if (AnnotationAccess.isAnnotationPresent(method, Fold.class)) {
1144+
return false;
1145+
}
1146+
1147+
/* Deleted methods should not be included in the image. */
1148+
if (AnnotationAccess.isAnnotationPresent(method, Delete.class)) {
1149+
return false;
1150+
}
1151+
1152+
/*
1153+
* Methods whose graph cannot be created should not be in the image. Those methods are
1154+
* compiled in a different way and cannot be included in the same way as normal methods.
1155+
*/
1156+
if (AnnotationAccess.isAnnotationPresent(method, CConstant.class) || AnnotationAccess.isAnnotationPresent(method, Operation.class) ||
1157+
AnnotationAccess.isAnnotationPresent(method, NodeIntrinsic.class) || AnnotationAccess.isAnnotationPresent(method, HotSpotOperation.class)) {
1158+
return false;
1159+
}
1160+
1161+
/* Methods that are not provided in the current Libc should not be included. */
1162+
if (OriginalMethodProvider.getJavaMethod(method) instanceof Method m && !HostedLibCBase.isMethodProvidedInCurrentLibc(m)) {
1163+
return false;
1164+
}
1165+
1166+
/* Methods that are not in the native libraries configuration should not be included. */
1167+
if (nativeLibraries == null) {
1168+
nativeLibraries = com.oracle.svm.hosted.c.NativeLibraries.singleton();
1169+
allStaticLibNames = nativeLibraries.getAllStaticLibNames();
1170+
}
1171+
if (!nativeLibraries.isMethodInConfiguration(method)) {
1172+
return false;
1173+
}
1174+
1175+
/*
1176+
* Methods from a CLibrary that is not included in the static libraries of the image should
1177+
* not be included.
1178+
*/
1179+
CLibrary cLibrary = nativeLibraries.getCLibrary(method);
1180+
if (cLibrary != null && allStaticLibNames.stream().noneMatch(lib -> lib.toString().contains(cLibrary.value()))) {
1181+
return false;
1182+
}
1183+
1184+
/* Methods with an invocation plugin should not be included. */
1185+
InvocationPlugins invocationPlugins = getProviders(MultiMethod.ORIGINAL_METHOD).getGraphBuilderPlugins().getInvocationPlugins();
1186+
if (invocationPlugins.lookupInvocation(method, bb.getOptions()) != null) {
1187+
return false;
1188+
}
1189+
1190+
/* CEntryPoint methods should not be included according to their predicate. */
1191+
CEntryPoint cEntryPoint = AnnotationAccess.getAnnotation(method, CEntryPoint.class);
1192+
return cEntryPoint == null || ReflectionUtil.newInstance(cEntryPoint.include()).getAsBoolean();
1193+
}
1194+
11221195
/**
11231196
* Check if an {@link AnalysisField} should be included in the image. For checking its
11241197
* annotations we rely on the {@link AnnotationAccess} unwrapping mechanism to include any
@@ -1474,4 +1547,28 @@ public SimulateClassInitializerSupport createSimulateClassInitializerSupport(Ana
14741547
public ConstantExpressionRegistry getConstantExpressionRegistry() {
14751548
return constantExpressionRegistry;
14761549
}
1550+
1551+
@Override
1552+
public Set<Module> getForbiddenModules() {
1553+
if (forbiddenModules.isEmpty()) {
1554+
forbiddenModules.add(JVMCI.class.getModule());
1555+
Class<?> llvm = ReflectionUtil.lookupClass(true, "com.oracle.svm.shadowed.org.bytedeco.llvm.global.LLVM");
1556+
if (llvm != null) {
1557+
forbiddenModules.add(llvm.getModule());
1558+
}
1559+
Class<?> javacpp = ReflectionUtil.lookupClass(true, "com.oracle.svm.shadowed.org.bytedeco.javacpp.presets.javacpp");
1560+
if (javacpp != null) {
1561+
forbiddenModules.add(javacpp.getModule());
1562+
}
1563+
Class<?> truffle = ReflectionUtil.lookupClass(true, "com.oracle.truffle.polyglot.JDKSupport");
1564+
if (truffle != null) {
1565+
forbiddenModules.add(truffle.getModule());
1566+
}
1567+
Class<?> libGraal = ReflectionUtil.lookupClass(true, "com.oracle.truffle.runtime.hotspot.libgraal.LibGraal");
1568+
if (libGraal != null) {
1569+
forbiddenModules.add(libGraal.getModule());
1570+
}
1571+
}
1572+
return forbiddenModules;
1573+
}
14771574
}

0 commit comments

Comments
 (0)