Skip to content

Commit d06d8b7

Browse files
committed
[GR-29096] Add workaround for injecting classes in the host boot class loader in a Java 16 host.
PullRequest: graal/9483
2 parents 8ac3679 + 8b59173 commit d06d8b7

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/FinalizationSupport.java

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
*/
2323
package com.oracle.truffle.espresso;
2424

25+
import java.lang.invoke.MethodHandle;
26+
import java.lang.invoke.MethodHandles;
2527
import java.lang.ref.PublicFinalReference;
2628
import java.lang.reflect.AccessibleObject;
2729
import java.lang.reflect.Field;
@@ -93,42 +95,57 @@ public final class FinalizationSupport {
9395
Class<?> publicFinalReference = injectClassInBootClassLoader("java/lang/ref/PublicFinalReference", PUBLIC_FINAL_REFERENCE_BYTES);
9496
EspressoError.guarantee("java.lang.ref.FinalReference".equals(publicFinalReference.getSuperclass().getName()),
9597
"Injected class does not subclass FinalReference");
96-
} catch (Exception e) {
97-
throw EspressoError.shouldNotReachHere("Error injecting PublicFinalReference in the host (version " + HostJavaVersionUtil.JAVA_SPEC + ")", e);
98+
} catch (Throwable t) {
99+
throw EspressoError.shouldNotReachHere("Error injecting PublicFinalReference in the host (version " + HostJavaVersionUtil.JAVA_SPEC + ")", t);
98100
}
99101
}
100102

101103
public static void ensureInitialized() {
102104
/* nop */
103105
}
104106

105-
private static Class<?> injectClassInBootClassLoader(String className, byte[] classBytes) throws Exception {
107+
private static Class<?> injectClassInBootClassLoader(String className, byte[] classBytes) throws Throwable {
106108
if (HostJavaVersionUtil.JAVA_SPEC == 8) {
107109
// Inject class via sun.misc.Unsafe#defineClass.
108110
// The use of reflection here is deliberate, so the code compiles with both Java 8/11.
109111
Method defineClass = Unsafe.class.getDeclaredMethod("defineClass",
110112
String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
111113
defineClass.setAccessible(true);
112114
return (Class<?>) defineClass.invoke(UnsafeAccess.get(), className, classBytes, 0, classBytes.length, null, null);
113-
} else if (HostJavaVersionUtil.JAVA_SPEC == 11) {
114-
// Inject class via j.l.ClassLoader#defineClass1.
115+
} else if (HostJavaVersionUtil.JAVA_SPEC >= 11) {
116+
// Inject class via j.l.ClassLoader#defineClass1 (private native method).
115117
Method defineClass1 = ClassLoader.class.getDeclaredMethod("defineClass1",
116118
ClassLoader.class, String.class, byte[].class, int.class, int.class, ProtectionDomain.class, String.class);
117119

118120
if (UnsafeOverride) {
119121
/*
120-
* Overwrites the AccessibleObject.override field via Unsafe to force-enable
121-
* reflection access and get rid of illegal access warnings.
122+
* Overwrite the AccessibleObject.override field to enable reflection access and get
123+
* rid of illegal access warnings.
122124
*/
123-
Object unsafeInstance = UnsafeAccess.get();
124-
Method putBoolean = unsafeInstance.getClass().getMethod("putBoolean", Object.class, long.class, boolean.class);
125-
Method objectFieldOffset = unsafeInstance.getClass().getMethod("objectFieldOffset", Field.class);
126-
127-
Field overrideField = AccessibleObject.class.getDeclaredField("override");
128-
long overrideFieldOffset = (long) objectFieldOffset.invoke(unsafeInstance, overrideField);
129-
130-
// Force-enable access to j.l.ClassLoader#defineClass1.
131-
putBoolean.invoke(unsafeInstance, defineClass1, overrideFieldOffset, true);
125+
if (HostJavaVersionUtil.JAVA_SPEC == 11) {
126+
Object unsafeInstance = UnsafeAccess.get();
127+
Method putBoolean = unsafeInstance.getClass().getMethod("putBoolean", Object.class, long.class, boolean.class);
128+
Method objectFieldOffset = unsafeInstance.getClass().getMethod("objectFieldOffset", Field.class);
129+
Field overrideField = AccessibleObject.class.getDeclaredField("override");
130+
long overrideFieldOffset = (long) objectFieldOffset.invoke(unsafeInstance, overrideField);
131+
132+
// Force-enable access to j.l.ClassLoader#defineClass1.
133+
putBoolean.invoke(unsafeInstance, defineClass1, overrideFieldOffset, true);
134+
} else if (HostJavaVersionUtil.JAVA_SPEC >= 12) {
135+
// Tested on Java 16.
136+
// In Java 12+, AccessibleObject.override was added to the reflection blocklist.
137+
// Illegal access checks can be circumvented by getting the implementation
138+
// lookup from MethodHandles with Unsafe.
139+
Field implLookupField = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
140+
Unsafe unsafe = UnsafeAccess.get();
141+
long implLookupFieldOffset = unsafe.staticFieldOffset(implLookupField);
142+
Object lookupStaticFieldBase = unsafe.staticFieldBase(implLookupField);
143+
MethodHandles.Lookup implLookup = (MethodHandles.Lookup) unsafe.getObject(lookupStaticFieldBase, implLookupFieldOffset);
144+
final MethodHandle overrideSetter = implLookup.findSetter(AccessibleObject.class, "override", boolean.class);
145+
146+
// Force-enable access to j.l.ClassLoader#defineClass1.
147+
overrideSetter.invokeWithArguments(defineClass1, true);
148+
}
132149
}
133150

134151
return (Class<?>) defineClass1.invoke(null, null, className, classBytes, 0, classBytes.length, null, null);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/Target_java_lang_ref_Reference.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,22 @@ static boolean waitForReferenceProcessing() throws InterruptedException {
197197
static void reachabilityFence(Object ref) {
198198
GraalDirectives.blackhole(ref);
199199
}
200+
201+
@Substitute //
202+
@TargetElement(onlyWith = JDK16OrLater.class) //
203+
T getFromInactiveFinalReference() {
204+
// assert this instanceof FinalReference;
205+
assert next != null; // I.e. FinalReference is inactive
206+
return get();
207+
}
208+
209+
@Substitute //
210+
@TargetElement(onlyWith = JDK16OrLater.class) //
211+
void clearInactiveFinalReference() {
212+
// assert this instanceof FinalReference;
213+
assert next != null; // I.e. FinalReference is inactive
214+
clear();
215+
}
200216
}
201217

202218
/** We provide our own {@link com.oracle.svm.core.heap.ReferenceHandler}. */

0 commit comments

Comments
 (0)