001/** 002 * Copyright (C) 2006-2025 Talend Inc. - www.talend.com 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.talend.sdk.component.runtime.manager.asm; 017 018import static java.util.Objects.requireNonNull; 019import static lombok.AccessLevel.PRIVATE; 020 021import java.lang.reflect.AccessibleObject; 022import java.lang.reflect.Field; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.security.AccessController; 026import java.security.PrivilegedAction; 027import java.security.ProtectionDomain; 028import java.util.stream.Stream; 029 030import org.talend.sdk.component.classloader.ConfigurableClassLoader; 031import org.talend.sdk.component.runtime.reflect.JavaVersion; 032 033import lombok.NoArgsConstructor; 034 035@NoArgsConstructor(access = PRIVATE) 036public final class Unsafes { 037 038 private static final Object UNSAFE; 039 040 private static final Object INTERNAL_UNSAFE; 041 042 private static final Method UNSAFE_DEFINE_CLASS; 043 044 static { 045 Class<?> unsafeClass; 046 final int javaVersion = JavaVersion.major(); 047 if (javaVersion > 8 && javaVersion < 17) { 048 try { 049 /** 050 * Disable Access Warnings: 051 * 052 * <pre> 053 * {@code 054 * WARNING: An illegal reflective access operation has occurred 055 * WARNING: Illegal reflective access by org.talend.sdk.component.runtime.manager.asm.Unsafes \ 056 * (file:/xxxx/component-runtime-manager-x.xx.x.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int) 057 * WARNING: Please consider reporting this to the maintainers of org.talend.sdk.component.runtime.manager.asm.Unsafes 058 * WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations 059 * WARNING: All illegal access operations will be denied in a future release 060 } 061 * </pre> 062 */ 063 Class unsafeClazz = Class.forName("sun.misc.Unsafe"); 064 Field field = unsafeClazz.getDeclaredField("theUnsafe"); 065 field.setAccessible(true); 066 Object unsafe = field.get(null); 067 Method putObjectVolatile = 068 unsafeClazz.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class); 069 Method staticFieldOffset = unsafeClazz.getDeclaredMethod("staticFieldOffset", Field.class); 070 Class loggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger"); 071 Field loggerField = loggerClass.getDeclaredField("logger"); 072 Long offset = (Long) staticFieldOffset.invoke(unsafe, loggerField); 073 putObjectVolatile.invoke(unsafe, loggerClass, offset, null); 074 } catch (Exception e) { 075 System.err.println("Disabling unsafe warnings failed: " + e.getMessage()); 076 } 077 } 078 try { 079 unsafeClass = AccessController 080 .doPrivileged((PrivilegedAction<Class<?>>) () -> Stream 081 .of(Thread.currentThread().getContextClassLoader(), ClassLoader.getSystemClassLoader()) 082 .flatMap(classloader -> Stream 083 .of("sun.misc.Unsafe", "jdk.internal.misc.Unsafe") 084 .flatMap(name -> { 085 try { 086 return Stream.of(classloader.loadClass(name)); 087 } catch (final ClassNotFoundException e) { 088 return Stream.empty(); 089 } 090 })) 091 .findFirst() 092 .orElseThrow(() -> new IllegalStateException("Cannot get Unsafe"))); 093 } catch (final Exception e) { 094 throw new IllegalStateException("Cannot get Unsafe class", e); 095 } 096 097 UNSAFE = AccessController.doPrivileged((PrivilegedAction<Object>) () -> { 098 try { 099 final Field field = unsafeClass.getDeclaredField("theUnsafe"); 100 field.setAccessible(true); 101 return field.get(null); 102 } catch (final Exception e) { 103 throw new IllegalStateException(e); 104 } 105 }); 106 INTERNAL_UNSAFE = AccessController.doPrivileged((PrivilegedAction<Object>) () -> { 107 try { // j11, unwrap unsafe, it owns defineClass now and no more theUnsafe 108 final Field theInternalUnsafe = unsafeClass.getDeclaredField("theInternalUnsafe"); 109 theInternalUnsafe.setAccessible(true); 110 return theInternalUnsafe.get(null).getClass(); 111 } catch (final Exception notJ11OrMore) { 112 return UNSAFE; 113 } 114 }); 115 116 if (UNSAFE != null) { 117 UNSAFE_DEFINE_CLASS = AccessController.doPrivileged((PrivilegedAction<Method>) () -> { 118 try { 119 return INTERNAL_UNSAFE 120 .getClass() 121 .getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, 122 ClassLoader.class, ProtectionDomain.class); 123 } catch (final Exception e) { 124 return null; 125 } 126 }); 127 128 try { 129 final Class<?> rootLoaderClass = Class.forName("java.lang.ClassLoader"); 130 rootLoaderClass 131 .getDeclaredMethod("defineClass", 132 new Class[] { String.class, byte[].class, int.class, int.class }) 133 .setAccessible(true); 134 rootLoaderClass 135 .getDeclaredMethod("defineClass", 136 new Class[] { String.class, byte[].class, int.class, int.class, 137 ProtectionDomain.class }) 138 .setAccessible(true); 139 } catch (final Exception e) { 140 try { // some j>8, since we have unsafe let's use it 141 final Class<?> rootLoaderClass = Class.forName("java.lang.ClassLoader"); 142 final Method objectFieldOffset = 143 UNSAFE.getClass().getDeclaredMethod("objectFieldOffset", Field.class); 144 final Method putBoolean = 145 UNSAFE.getClass().getDeclaredMethod("putBoolean", Object.class, long.class, boolean.class); 146 objectFieldOffset.setAccessible(true); 147 final long accOffset = Long.class 148 .cast(objectFieldOffset 149 .invoke(UNSAFE, AccessibleObject.class.getDeclaredField("override"))); 150 putBoolean 151 .invoke(UNSAFE, 152 rootLoaderClass 153 .getDeclaredMethod("defineClass", 154 new Class[] { String.class, byte[].class, int.class, int.class }), 155 accOffset, true); 156 putBoolean 157 .invoke(UNSAFE, 158 rootLoaderClass 159 .getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, 160 int.class, int.class, ProtectionDomain.class }), 161 accOffset, true); 162 } catch (final Exception ex) { 163 // no-op: no more supported by the JVM 164 } 165 } 166 } else { 167 UNSAFE_DEFINE_CLASS = null; 168 } 169 } 170 171 /** 172 * The 'defineClass' method on the ClassLoader is private, thus we need to 173 * invoke it via reflection. 174 * 175 * @param classLoader the classloader to use to define the proxy. 176 * @param proxyName the class name to define. 177 * @param proxyBytes the bytes of the class to define. 178 * @param <T> the Class type 179 * 180 * @return the Class which got loaded in the classloader 181 */ 182 public static <T> Class<T> defineAndLoadClass(final ClassLoader classLoader, final String proxyName, 183 final byte[] proxyBytes) { 184 if (ConfigurableClassLoader.class.isInstance(classLoader)) { 185 return (Class<T>) ConfigurableClassLoader.class 186 .cast(classLoader) 187 .registerBytecode(proxyName.replace('/', '.'), proxyBytes); 188 } 189 Class<?> clazz = classLoader.getClass(); 190 191 Method defineClassMethod = null; 192 do { 193 try { 194 defineClassMethod = 195 clazz.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); 196 } catch (NoSuchMethodException e) { 197 // do nothing, we need to search the superclass 198 } 199 200 clazz = clazz.getSuperclass(); 201 } while (defineClassMethod == null && clazz != Object.class); 202 203 if (defineClassMethod != null && !defineClassMethod.isAccessible()) { 204 try { 205 defineClassMethod.setAccessible(true); 206 } catch (final RuntimeException re) { // likely j9, let's use unsafe 207 defineClassMethod = null; 208 } 209 } 210 211 try { 212 Class<T> definedClass; 213 214 if (defineClassMethod != null) { 215 definedClass = 216 (Class<T>) defineClassMethod.invoke(classLoader, proxyName, proxyBytes, 0, proxyBytes.length); 217 } else { 218 requireNonNull(UNSAFE_DEFINE_CLASS, "No Unsafe.defineClass available"); 219 definedClass = (Class<T>) UNSAFE_DEFINE_CLASS 220 .invoke(UNSAFE, proxyName, proxyBytes, 0, proxyBytes.length, classLoader, null); 221 } 222 223 return (Class<T>) Class.forName(definedClass.getName(), true, classLoader); 224 } catch (final InvocationTargetException le) { 225 if (LinkageError.class.isInstance(le.getCause())) { 226 try { 227 return (Class<T>) Class.forName(proxyName.replace('/', '.'), true, classLoader); 228 } catch (ClassNotFoundException e) { 229 // default error handling 230 } 231 } 232 throw new IllegalStateException(le.getCause()); 233 } catch (final Throwable e) { 234 throw new IllegalStateException(e); 235 } 236 } 237}