View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import io.netty.util.internal.logging.InternalLogger;
19  import io.netty.util.internal.logging.InternalLoggerFactory;
20  import sun.misc.Unsafe;
21  
22  import java.lang.invoke.MethodHandle;
23  import java.lang.invoke.MethodHandles;
24  import java.lang.reflect.Constructor;
25  import java.lang.reflect.Field;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  import java.nio.Buffer;
29  import java.nio.ByteBuffer;
30  import java.security.AccessController;
31  import java.security.PrivilegedAction;
32  import java.util.concurrent.atomic.AtomicLong;
33  
34  import static io.netty.util.internal.ObjectUtil.checkNotNull;
35  import static java.lang.invoke.MethodType.methodType;
36  
37  /**
38   * The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
39   */
40  final class PlatformDependent0 {
41  
42      private static final InternalLogger logger = InternalLoggerFactory.getInstance(PlatformDependent0.class);
43      private static final long ADDRESS_FIELD_OFFSET;
44      private static final long BYTE_ARRAY_BASE_OFFSET;
45      private static final long INT_ARRAY_BASE_OFFSET;
46      private static final long INT_ARRAY_INDEX_SCALE;
47      private static final long LONG_ARRAY_BASE_OFFSET;
48      private static final long LONG_ARRAY_INDEX_SCALE;
49      private static final MethodHandle DIRECT_BUFFER_CONSTRUCTOR;
50      private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
51      private static final MethodHandle ALLOCATE_ARRAY_METHOD;
52      private static final MethodHandle ALIGN_SLICE;
53      private static final int JAVA_VERSION = javaVersion0();
54      private static final boolean IS_ANDROID = isAndroid0();
55  
56      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
57  
58      // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/
59      // ImageInfo.java
60      private static final boolean RUNNING_IN_NATIVE_IMAGE = SystemPropertyUtil.contains(
61              "org.graalvm.nativeimage.imagecode");
62  
63      private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
64  
65      static final Unsafe UNSAFE;
66  
67      // constants borrowed from murmur3
68      static final int HASH_CODE_ASCII_SEED = 0xc2b2ae35;
69      static final int HASH_CODE_C1 = 0xcc9e2d51;
70      static final int HASH_CODE_C2 = 0x1b873593;
71  
72      /**
73       * Limits the number of bytes to copy per {@link Unsafe#copyMemory(long, long, long)} to allow safepoint polling
74       * during a large copy.
75       */
76      private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;
77  
78      private static final boolean UNALIGNED;
79  
80      private static final long BITS_MAX_DIRECT_MEMORY;
81  
82      static {
83          MethodHandles.Lookup lookup = MethodHandles.lookup();
84          final ByteBuffer direct;
85          Field addressField = null;
86          MethodHandle allocateArrayMethod = null;
87          Throwable unsafeUnavailabilityCause;
88          Unsafe unsafe;
89          if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
90              direct = null;
91              addressField = null;
92              unsafe = null;
93          } else {
94              direct = ByteBuffer.allocateDirect(1);
95  
96              // attempt to access field Unsafe#theUnsafe
97              final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
98                  @Override
99                  public Object run() {
100                     try {
101                         final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
102                         // We always want to try using Unsafe as the access still works on java9 as well and
103                         // we need it for out native-transports and many optimizations.
104                         Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
105                         if (cause != null) {
106                             return cause;
107                         }
108                         // the unsafe instance
109                         return unsafeField.get(null);
110                     } catch (NoSuchFieldException e) {
111                         return e;
112                     } catch (SecurityException e) {
113                         return e;
114                     } catch (IllegalAccessException e) {
115                         return e;
116                     } catch (NoClassDefFoundError e) {
117                         // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
118                         // Unsafe unloadable.
119                         return e;
120                     }
121                 }
122             });
123 
124             // the conditional check here can not be replaced with checking that maybeUnsafe
125             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
126             // instanceof check against Unsafe will trigger a class load and we might not have
127             // the runtime permission accessClassInPackage.sun.misc
128             if (maybeUnsafe instanceof Throwable) {
129                 unsafe = null;
130                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
131                 if (logger.isTraceEnabled()) {
132                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable", (Throwable) maybeUnsafe);
133                 } else {
134                     logger.debug("sun.misc.Unsafe.theUnsafe: unavailable: {}", unsafeUnavailabilityCause.getMessage());
135                 }
136             } else {
137                 unsafe = (Unsafe) maybeUnsafe;
138                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
139             }
140 
141             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK,
142             // or that they haven't been removed by JEP 471.
143             // https://github.com/netty/netty/issues/1061
144             // https://www.mail-archive.com/[email protected]/msg00698.html
145             // https://openjdk.org/jeps/471
146             if (unsafe != null) {
147                 final Unsafe finalUnsafe = unsafe;
148                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
149                     @Override
150                     public Object run() {
151                         try {
152                             // Other methods like storeFence() and invokeCleaner() are tested for elsewhere.
153                             Class<? extends Unsafe> cls = finalUnsafe.getClass();
154                             cls.getDeclaredMethod(
155                                     "copyMemory", Object.class, long.class, Object.class, long.class, long.class);
156                             if (javaVersion() > 23) {
157                                 cls.getDeclaredMethod("objectFieldOffset", Field.class);
158                                 cls.getDeclaredMethod("staticFieldOffset", Field.class);
159                                 cls.getDeclaredMethod("staticFieldBase", Field.class);
160                                 cls.getDeclaredMethod("arrayBaseOffset", Class.class);
161                                 cls.getDeclaredMethod("arrayIndexScale", Class.class);
162                                 cls.getDeclaredMethod("allocateMemory", long.class);
163                                 cls.getDeclaredMethod("reallocateMemory", long.class, long.class);
164                                 cls.getDeclaredMethod("freeMemory", long.class);
165                                 cls.getDeclaredMethod("setMemory", long.class, long.class, byte.class);
166                                 cls.getDeclaredMethod("setMemory", Object.class, long.class, long.class, byte.class);
167                                 cls.getDeclaredMethod("getBoolean", Object.class, long.class);
168                                 cls.getDeclaredMethod("getByte", long.class);
169                                 cls.getDeclaredMethod("getByte", Object.class, long.class);
170                                 cls.getDeclaredMethod("getInt", long.class);
171                                 cls.getDeclaredMethod("getInt", Object.class, long.class);
172                                 cls.getDeclaredMethod("getLong", long.class);
173                                 cls.getDeclaredMethod("getLong", Object.class, long.class);
174                                 cls.getDeclaredMethod("putByte", long.class, byte.class);
175                                 cls.getDeclaredMethod("putByte", Object.class, long.class, byte.class);
176                                 cls.getDeclaredMethod("putInt", long.class, int.class);
177                                 cls.getDeclaredMethod("putInt", Object.class, long.class, int.class);
178                                 cls.getDeclaredMethod("putLong", long.class, long.class);
179                                 cls.getDeclaredMethod("putLong", Object.class, long.class, long.class);
180                                 cls.getDeclaredMethod("addressSize");
181                             }
182                             if (javaVersion() >= 23) {
183                                 // The following tests the methods are usable.
184                                 // Will throw UnsupportedOperationException if unsafe memory access is denied:
185                                 long address = finalUnsafe.allocateMemory(8);
186                                 finalUnsafe.putLong(address, 42);
187                                 finalUnsafe.freeMemory(address);
188                             }
189                             return null;
190                         } catch (UnsupportedOperationException e) {
191                             return e;
192                         } catch (NoSuchMethodException e) {
193                             return e;
194                         } catch (SecurityException e) {
195                             return e;
196                         }
197                     }
198                 });
199 
200                 if (maybeException == null) {
201                     logger.debug("sun.misc.Unsafe base methods: all available");
202                 } else {
203                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
204                     unsafe = null;
205                     unsafeUnavailabilityCause = (Throwable) maybeException;
206                     if (logger.isTraceEnabled()) {
207                         logger.debug("sun.misc.Unsafe method unavailable:", unsafeUnavailabilityCause);
208                     } else {
209                         logger.debug("sun.misc.Unsafe method unavailable: {}",
210                                 ((Throwable) maybeException).getMessage());
211                     }
212                 }
213             }
214 
215             if (unsafe != null) {
216                 final Unsafe finalUnsafe = unsafe;
217 
218                 // attempt to access field Buffer#address
219                 final Object maybeAddressField = AccessController.doPrivileged(new PrivilegedAction<Object>() {
220                     @Override
221                     public Object run() {
222                         try {
223                             final Field field = Buffer.class.getDeclaredField("address");
224                             // Use Unsafe to read value of the address field. This way it will not fail on JDK9+ which
225                             // will forbid changing the access level via reflection.
226                             final long offset = finalUnsafe.objectFieldOffset(field);
227                             final long address = finalUnsafe.getLong(direct, offset);
228 
229                             // if direct really is a direct buffer, address will be non-zero
230                             if (address == 0) {
231                                 return null;
232                             }
233                             return field;
234                         } catch (NoSuchFieldException e) {
235                             return e;
236                         } catch (SecurityException e) {
237                             return e;
238                         }
239                     }
240                 });
241 
242                 if (maybeAddressField instanceof Field) {
243                     addressField = (Field) maybeAddressField;
244                     logger.debug("java.nio.Buffer.address: available");
245                 } else {
246                     unsafeUnavailabilityCause = (Throwable) maybeAddressField;
247                     if (logger.isTraceEnabled()) {
248                         logger.debug("java.nio.Buffer.address: unavailable", (Throwable) maybeAddressField);
249                     } else {
250                         logger.debug("java.nio.Buffer.address: unavailable: {}",
251                                 ((Throwable) maybeAddressField).getMessage());
252                     }
253 
254                     // If we cannot access the address of a direct buffer, there's no point of using unsafe.
255                     // Let's just pretend unsafe is unavailable for overall simplicity.
256                     unsafe = null;
257                 }
258             }
259 
260             if (unsafe != null) {
261                 // There are assumptions made where ever BYTE_ARRAY_BASE_OFFSET is used (equals, hashCodeAscii, and
262                 // primitive accessors) that arrayIndexScale == 1, and results are undefined if this is not the case.
263                 long byteArrayIndexScale = unsafe.arrayIndexScale(byte[].class);
264                 if (byteArrayIndexScale != 1) {
265                     logger.debug("unsafe.arrayIndexScale is {} (expected: 1). Not using unsafe.", byteArrayIndexScale);
266                     unsafeUnavailabilityCause = new UnsupportedOperationException("Unexpected unsafe.arrayIndexScale");
267                     unsafe = null;
268                 }
269             }
270         }
271         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
272         UNSAFE = unsafe;
273 
274         if (unsafe == null) {
275             ADDRESS_FIELD_OFFSET = -1;
276             BYTE_ARRAY_BASE_OFFSET = -1;
277             LONG_ARRAY_BASE_OFFSET = -1;
278             LONG_ARRAY_INDEX_SCALE = -1;
279             INT_ARRAY_BASE_OFFSET = -1;
280             INT_ARRAY_INDEX_SCALE = -1;
281             UNALIGNED = false;
282             BITS_MAX_DIRECT_MEMORY = -1;
283             DIRECT_BUFFER_CONSTRUCTOR = null;
284             ALLOCATE_ARRAY_METHOD = null;
285         } else {
286             MethodHandle directBufferConstructor;
287             long address = -1;
288             try {
289                 final Object maybeDirectBufferConstructor =
290                         AccessController.doPrivileged(new PrivilegedAction<Object>() {
291                             @Override
292                             public Object run() {
293                                 try {
294                                     Class<? extends ByteBuffer> directClass = direct.getClass();
295                                     final Constructor<?> constructor = javaVersion() >= 21 ?
296                                             directClass.getDeclaredConstructor(long.class, long.class) :
297                                             directClass.getDeclaredConstructor(long.class, int.class);
298                                     Throwable cause = ReflectionUtil.trySetAccessible(constructor, true);
299                                     if (cause != null) {
300                                         return cause;
301                                     }
302                                     return lookup.unreflectConstructor(constructor)
303                                             .asType(methodType(ByteBuffer.class, long.class, int.class));
304                                 } catch (Throwable e) {
305                                     return e;
306                                 }
307                             }
308                         });
309 
310                 if (maybeDirectBufferConstructor instanceof MethodHandle) {
311                     address = UNSAFE.allocateMemory(1);
312                     // try to use the constructor now
313                     try {
314                         MethodHandle constructor = (MethodHandle) maybeDirectBufferConstructor;
315                         ByteBuffer ignore = (ByteBuffer) constructor.invokeExact(address, 1);
316                         directBufferConstructor = constructor;
317                         logger.debug("direct buffer constructor: available");
318                     } catch (Throwable e) {
319                         directBufferConstructor = null;
320                     }
321                 } else {
322                     if (logger.isTraceEnabled()) {
323                         logger.debug("direct buffer constructor: unavailable",
324                                 (Throwable) maybeDirectBufferConstructor);
325                     } else {
326                         logger.debug("direct buffer constructor: unavailable: {}",
327                                 ((Throwable) maybeDirectBufferConstructor).getMessage());
328                     }
329                     directBufferConstructor = null;
330                 }
331             } finally {
332                 if (address != -1) {
333                     UNSAFE.freeMemory(address);
334                 }
335             }
336             DIRECT_BUFFER_CONSTRUCTOR = directBufferConstructor;
337             ADDRESS_FIELD_OFFSET = objectFieldOffset(addressField);
338             BYTE_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
339             INT_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(int[].class);
340             INT_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(int[].class);
341             LONG_ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
342             LONG_ARRAY_INDEX_SCALE = UNSAFE.arrayIndexScale(long[].class);
343             final boolean unaligned;
344             // using a known type to avoid loading new classes
345             final AtomicLong maybeMaxMemory = new AtomicLong(-1);
346             Object maybeUnaligned = AccessController.doPrivileged(new PrivilegedAction<Object>() {
347                 @Override
348                 public Object run() {
349                     try {
350                         Class<?> bitsClass =
351                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
352                         int version = javaVersion();
353                         if (unsafeStaticFieldOffsetSupported() && version >= 9) {
354                             // Java9/10 use all lowercase and later versions all uppercase.
355                             String fieldName = version >= 11? "MAX_MEMORY" : "maxMemory";
356                             // On Java9 and later we try to directly access the field as we can do this without
357                             // adjust the accessible levels.
358                             try {
359                                 Field maxMemoryField = bitsClass.getDeclaredField(fieldName);
360                                 if (maxMemoryField.getType() == long.class) {
361                                     long offset = UNSAFE.staticFieldOffset(maxMemoryField);
362                                     Object object = UNSAFE.staticFieldBase(maxMemoryField);
363                                     maybeMaxMemory.lazySet(UNSAFE.getLong(object, offset));
364                                 }
365                             } catch (Throwable ignore) {
366                                 // ignore if can't access
367                             }
368                             fieldName = version >= 11? "UNALIGNED" : "unaligned";
369                             try {
370                                 Field unalignedField = bitsClass.getDeclaredField(fieldName);
371                                 if (unalignedField.getType() == boolean.class) {
372                                     long offset = UNSAFE.staticFieldOffset(unalignedField);
373                                     Object object = UNSAFE.staticFieldBase(unalignedField);
374                                     return UNSAFE.getBoolean(object, offset);
375                                 }
376                                 // There is something unexpected stored in the field,
377                                 // let us fall-back and try to use a reflective method call as last resort.
378                             } catch (NoSuchFieldException ignore) {
379                                 // We did not find the field we expected, move on.
380                             }
381                         }
382                         Method unalignedMethod = bitsClass.getDeclaredMethod("unaligned");
383                         Throwable cause = ReflectionUtil.trySetAccessible(unalignedMethod, true);
384                         if (cause != null) {
385                             return cause;
386                         }
387                         return unalignedMethod.invoke(null);
388                     } catch (NoSuchMethodException e) {
389                         return e;
390                     } catch (SecurityException e) {
391                         return e;
392                     } catch (IllegalAccessException e) {
393                         return e;
394                     } catch (ClassNotFoundException e) {
395                         return e;
396                     } catch (InvocationTargetException e) {
397                         return e;
398                     }
399                 }
400             });
401 
402             if (maybeUnaligned instanceof Boolean) {
403                 unaligned = (Boolean) maybeUnaligned;
404                 logger.debug("java.nio.Bits.unaligned: available, {}", unaligned);
405             } else {
406                 String arch = SystemPropertyUtil.get("os.arch", "");
407                 //noinspection DynamicRegexReplaceableByCompiledPattern
408                 unaligned = arch.matches("^(i[3-6]86|x86(_64)?|x64|amd64)$");
409                 Throwable t = (Throwable) maybeUnaligned;
410                 if (logger.isTraceEnabled()) {
411                     logger.debug("java.nio.Bits.unaligned: unavailable, {}", unaligned, t);
412                 } else {
413                     logger.debug("java.nio.Bits.unaligned: unavailable, {}, {}", unaligned, t.getMessage());
414                 }
415             }
416 
417             UNALIGNED = unaligned;
418             BITS_MAX_DIRECT_MEMORY = maybeMaxMemory.get() >= 0? maybeMaxMemory.get() : -1;
419 
420             if (javaVersion() >= 9) {
421                 Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
422                     @Override
423                     public Object run() {
424                         try {
425                             // Java9 has jdk.internal.misc.Unsafe and not all methods are propagated to
426                             // sun.misc.Unsafe
427                             Class<?> cls = getClassLoader(PlatformDependent0.class)
428                                     .loadClass("jdk.internal.misc.Unsafe");
429                             return lookup.findStatic(cls, "getUnsafe", methodType(cls)).invoke();
430                         } catch (Throwable e) {
431                             return e;
432                         }
433                     }
434                 });
435                 if (!(maybeException instanceof Throwable)) {
436                     final Object finalInternalUnsafe = maybeException;
437                     maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
438                         @Override
439                         public Object run() {
440                             try {
441                                 Class<?> finalInternalUnsafeClass = finalInternalUnsafe.getClass();
442                                 return lookup.findVirtual(
443                                         finalInternalUnsafeClass,
444                                         "allocateUninitializedArray",
445                                         methodType(Object.class, Class.class, int.class));
446                             } catch (Throwable e) {
447                                 return e;
448                             }
449                         }
450                     });
451 
452                     if (maybeException instanceof MethodHandle) {
453                         try {
454                             MethodHandle m = (MethodHandle) maybeException;
455                             m = m.bindTo(finalInternalUnsafe);
456                             byte[] bytes = (byte[]) m.invokeExact(byte.class, 8);
457                             assert bytes.length == 8;
458                             allocateArrayMethod = m;
459                         } catch (Throwable e) {
460                             maybeException = e;
461                         }
462                     }
463                 }
464 
465                 if (maybeException instanceof Throwable) {
466                     if (logger.isTraceEnabled()) {
467                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable",
468                                 (Throwable) maybeException);
469                     } else {
470                         logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable: {}",
471                                 ((Throwable) maybeException).getMessage());
472                     }
473                 } else {
474                     logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): available");
475                 }
476             } else {
477                 logger.debug("jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9");
478             }
479             ALLOCATE_ARRAY_METHOD = allocateArrayMethod;
480         }
481 
482         if (javaVersion() > 9) {
483             ALIGN_SLICE = (MethodHandle) AccessController.doPrivileged(new PrivilegedAction<Object>() {
484                 @Override
485                 public Object run() {
486                     try {
487                         return MethodHandles.publicLookup().findVirtual(
488                                 ByteBuffer.class, "alignedSlice", methodType(ByteBuffer.class, int.class));
489                     } catch (Throwable e) {
490                         return null;
491                     }
492                 }
493             });
494         } else {
495             ALIGN_SLICE = null;
496         }
497 
498         logger.debug("java.nio.DirectByteBuffer.<init>(long, {int,long}): {}",
499                 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
500     }
501 
502     private static boolean unsafeStaticFieldOffsetSupported() {
503         return !RUNNING_IN_NATIVE_IMAGE;
504     }
505 
506     static boolean isExplicitNoUnsafe() {
507         return EXPLICIT_NO_UNSAFE_CAUSE != null;
508     }
509 
510     private static Throwable explicitNoUnsafeCause0() {
511         boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
512         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
513 
514         // See JDK 23 JEP 471 https://openjdk.org/jeps/471 and sun.misc.Unsafe.beforeMemoryAccess() on JDK 23+.
515         String unsafeMemoryAccess = SystemPropertyUtil.get("sun.misc.unsafe.memory.access", "<unspecified>");
516         if (!("allow".equals(unsafeMemoryAccess) || "<unspecified>".equals(unsafeMemoryAccess))) {
517             logger.debug("--sun-misc-unsafe-memory-access={}", unsafeMemoryAccess);
518             noUnsafe = true;
519         }
520 
521         if (noUnsafe) {
522             logger.debug("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
523             return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
524         }
525 
526         // Legacy properties
527         String unsafePropName;
528         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
529             unsafePropName = "io.netty.tryUnsafe";
530         } else {
531             unsafePropName = "org.jboss.netty.tryUnsafe";
532         }
533 
534         if (!SystemPropertyUtil.getBoolean(unsafePropName, true)) {
535             String msg = "sun.misc.Unsafe: unavailable (" + unsafePropName + ')';
536             logger.debug(msg);
537             return new UnsupportedOperationException(msg);
538         }
539 
540         return null;
541     }
542 
543     static boolean isUnaligned() {
544         return UNALIGNED;
545     }
546 
547     /**
548      * Any value >= 0 should be considered as a valid max direct memory value.
549      */
550     static long bitsMaxDirectMemory() {
551         return BITS_MAX_DIRECT_MEMORY;
552     }
553 
554     static boolean hasUnsafe() {
555         return UNSAFE != null;
556     }
557 
558     static Throwable getUnsafeUnavailabilityCause() {
559         return UNSAFE_UNAVAILABILITY_CAUSE;
560     }
561 
562     static boolean unalignedAccess() {
563         return UNALIGNED;
564     }
565 
566     static void throwException(Throwable cause) {
567         throwException0(cause);
568     }
569 
570     @SuppressWarnings("unchecked")
571     private static <E extends Throwable> void throwException0(Throwable t) throws E {
572         throw (E) t;
573     }
574 
575     static boolean hasDirectBufferNoCleanerConstructor() {
576         return DIRECT_BUFFER_CONSTRUCTOR != null;
577     }
578 
579     static ByteBuffer reallocateDirectNoCleaner(ByteBuffer buffer, int capacity) {
580         return newDirectBuffer(UNSAFE.reallocateMemory(directBufferAddress(buffer), capacity), capacity);
581     }
582 
583     static ByteBuffer allocateDirectNoCleaner(int capacity) {
584         // Calling malloc with capacity of 0 may return a null ptr or a memory address that can be used.
585         // Just use 1 to make it safe to use in all cases:
586         // See: https://pubs.opengroup.org/onlinepubs/009695399/functions/malloc.html
587         return newDirectBuffer(UNSAFE.allocateMemory(Math.max(1, capacity)), capacity);
588     }
589 
590     static boolean hasAlignSliceMethod() {
591         return ALIGN_SLICE != null;
592     }
593 
594     static ByteBuffer alignSlice(ByteBuffer buffer, int alignment) {
595         try {
596             return (ByteBuffer) ALIGN_SLICE.invokeExact(buffer, alignment);
597         } catch (Throwable e) {
598             rethrowIfPossible(e);
599             throw new LinkageError("ByteBuffer.alignedSlice not available", e);
600         }
601     }
602 
603     static boolean hasAllocateArrayMethod() {
604         return ALLOCATE_ARRAY_METHOD != null;
605     }
606 
607     static byte[] allocateUninitializedArray(int size) {
608         try {
609             return (byte[]) ALLOCATE_ARRAY_METHOD.invokeExact(byte.class, size);
610         } catch (Throwable e) {
611             rethrowIfPossible(e);
612             throw new LinkageError("Unsafe.allocateUninitializedArray not available", e);
613         }
614     }
615 
616     static ByteBuffer newDirectBuffer(long address, int capacity) {
617         ObjectUtil.checkPositiveOrZero(capacity, "capacity");
618 
619         try {
620             return (ByteBuffer) DIRECT_BUFFER_CONSTRUCTOR.invokeExact(address, capacity);
621         } catch (Throwable cause) {
622             rethrowIfPossible(cause);
623             throw new LinkageError("DirectByteBuffer constructor not available", cause);
624         }
625     }
626 
627     private static void rethrowIfPossible(Throwable cause) {
628         if (cause instanceof Error) {
629             throw (Error) cause;
630         }
631         if (cause instanceof RuntimeException) {
632             throw (RuntimeException) cause;
633         }
634     }
635 
636     static long directBufferAddress(ByteBuffer buffer) {
637         return getLong(buffer, ADDRESS_FIELD_OFFSET);
638     }
639 
640     static long byteArrayBaseOffset() {
641         return BYTE_ARRAY_BASE_OFFSET;
642     }
643 
644     static Object getObject(Object object, long fieldOffset) {
645         return UNSAFE.getObject(object, fieldOffset);
646     }
647 
648     static int getInt(Object object, long fieldOffset) {
649         return UNSAFE.getInt(object, fieldOffset);
650     }
651 
652     static void safeConstructPutInt(Object object, long fieldOffset, int value) {
653         UNSAFE.putInt(object, fieldOffset, value);
654         UNSAFE.storeFence();
655     }
656 
657     private static long getLong(Object object, long fieldOffset) {
658         return UNSAFE.getLong(object, fieldOffset);
659     }
660 
661     static long objectFieldOffset(Field field) {
662         return UNSAFE.objectFieldOffset(field);
663     }
664 
665     static byte getByte(long address) {
666         return UNSAFE.getByte(address);
667     }
668 
669     static short getShort(long address) {
670         return UNSAFE.getShort(address);
671     }
672 
673     static int getInt(long address) {
674         return UNSAFE.getInt(address);
675     }
676 
677     static long getLong(long address) {
678         return UNSAFE.getLong(address);
679     }
680 
681     static byte getByte(byte[] data, int index) {
682         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
683     }
684 
685     static byte getByte(byte[] data, long index) {
686         return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
687     }
688 
689     static short getShort(byte[] data, int index) {
690         return UNSAFE.getShort(data, BYTE_ARRAY_BASE_OFFSET + index);
691     }
692 
693     static int getInt(byte[] data, int index) {
694         return UNSAFE.getInt(data, BYTE_ARRAY_BASE_OFFSET + index);
695     }
696 
697     static int getInt(int[] data, long index) {
698         return UNSAFE.getInt(data, INT_ARRAY_BASE_OFFSET + INT_ARRAY_INDEX_SCALE * index);
699     }
700 
701     static int getIntVolatile(long address) {
702         return UNSAFE.getIntVolatile(null, address);
703     }
704 
705     static void putIntOrdered(long adddress, int newValue) {
706         UNSAFE.putOrderedInt(null, adddress, newValue);
707     }
708 
709     static long getLong(byte[] data, int index) {
710         return UNSAFE.getLong(data, BYTE_ARRAY_BASE_OFFSET + index);
711     }
712 
713     static long getLong(long[] data, long index) {
714         return UNSAFE.getLong(data, LONG_ARRAY_BASE_OFFSET + LONG_ARRAY_INDEX_SCALE * index);
715     }
716 
717     static void putByte(long address, byte value) {
718         UNSAFE.putByte(address, value);
719     }
720 
721     static void putShort(long address, short value) {
722         UNSAFE.putShort(address, value);
723     }
724 
725     static void putInt(long address, int value) {
726         UNSAFE.putInt(address, value);
727     }
728 
729     static void putLong(long address, long value) {
730         UNSAFE.putLong(address, value);
731     }
732 
733     static void putByte(byte[] data, int index, byte value) {
734         UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
735     }
736 
737     static void putByte(Object data, long offset, byte value) {
738         UNSAFE.putByte(data, offset, value);
739     }
740 
741     static void putShort(byte[] data, int index, short value) {
742         UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
743     }
744 
745     static void putInt(byte[] data, int index, int value) {
746         UNSAFE.putInt(data, BYTE_ARRAY_BASE_OFFSET + index, value);
747     }
748 
749     static void putLong(byte[] data, int index, long value) {
750         UNSAFE.putLong(data, BYTE_ARRAY_BASE_OFFSET + index, value);
751     }
752 
753     static void putObject(Object o, long offset, Object x) {
754         UNSAFE.putObject(o, offset, x);
755     }
756 
757     static void copyMemory(long srcAddr, long dstAddr, long length) {
758         // Manual safe-point polling is only needed prior Java9:
759         // See https://bugs.openjdk.java.net/browse/JDK-8149596
760         if (javaVersion() <= 8) {
761             copyMemoryWithSafePointPolling(srcAddr, dstAddr, length);
762         } else {
763             UNSAFE.copyMemory(srcAddr, dstAddr, length);
764         }
765     }
766 
767     private static void copyMemoryWithSafePointPolling(long srcAddr, long dstAddr, long length) {
768         while (length > 0) {
769             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
770             UNSAFE.copyMemory(srcAddr, dstAddr, size);
771             length -= size;
772             srcAddr += size;
773             dstAddr += size;
774         }
775     }
776 
777     static void copyMemory(Object src, long srcOffset, Object dst, long dstOffset, long length) {
778         // Manual safe-point polling is only needed prior Java9:
779         // See https://bugs.openjdk.java.net/browse/JDK-8149596
780         if (javaVersion() <= 8) {
781             copyMemoryWithSafePointPolling(src, srcOffset, dst, dstOffset, length);
782         } else {
783             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, length);
784         }
785     }
786 
787     private static void copyMemoryWithSafePointPolling(
788             Object src, long srcOffset, Object dst, long dstOffset, long length) {
789         while (length > 0) {
790             long size = Math.min(length, UNSAFE_COPY_THRESHOLD);
791             UNSAFE.copyMemory(src, srcOffset, dst, dstOffset, size);
792             length -= size;
793             srcOffset += size;
794             dstOffset += size;
795         }
796     }
797 
798     static void setMemory(long address, long bytes, byte value) {
799         UNSAFE.setMemory(address, bytes, value);
800     }
801 
802     static void setMemory(Object o, long offset, long bytes, byte value) {
803         UNSAFE.setMemory(o, offset, bytes, value);
804     }
805 
806     static boolean equals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
807         int remainingBytes = length & 7;
808         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
809         final long diff = startPos2 - startPos1;
810         if (length >= 8) {
811             final long end = baseOffset1 + remainingBytes;
812             for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
813                 if (UNSAFE.getLong(bytes1, i) != UNSAFE.getLong(bytes2, i + diff)) {
814                     return false;
815                 }
816             }
817         }
818         if (remainingBytes >= 4) {
819             remainingBytes -= 4;
820             long pos = baseOffset1 + remainingBytes;
821             if (UNSAFE.getInt(bytes1, pos) != UNSAFE.getInt(bytes2, pos + diff)) {
822                 return false;
823             }
824         }
825         final long baseOffset2 = baseOffset1 + diff;
826         if (remainingBytes >= 2) {
827             return UNSAFE.getChar(bytes1, baseOffset1) == UNSAFE.getChar(bytes2, baseOffset2) &&
828                     (remainingBytes == 2 ||
829                     UNSAFE.getByte(bytes1, baseOffset1 + 2) == UNSAFE.getByte(bytes2, baseOffset2 + 2));
830         }
831         return remainingBytes == 0 ||
832                 UNSAFE.getByte(bytes1, baseOffset1) == UNSAFE.getByte(bytes2, baseOffset2);
833     }
834 
835     static int equalsConstantTime(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) {
836         long result = 0;
837         long remainingBytes = length & 7;
838         final long baseOffset1 = BYTE_ARRAY_BASE_OFFSET + startPos1;
839         final long end = baseOffset1 + remainingBytes;
840         final long diff = startPos2 - startPos1;
841         for (long i = baseOffset1 - 8 + length; i >= end; i -= 8) {
842             result |= UNSAFE.getLong(bytes1, i) ^ UNSAFE.getLong(bytes2, i + diff);
843         }
844         if (remainingBytes >= 4) {
845             result |= UNSAFE.getInt(bytes1, baseOffset1) ^ UNSAFE.getInt(bytes2, baseOffset1 + diff);
846             remainingBytes -= 4;
847         }
848         if (remainingBytes >= 2) {
849             long pos = end - remainingBytes;
850             result |= UNSAFE.getChar(bytes1, pos) ^ UNSAFE.getChar(bytes2, pos + diff);
851             remainingBytes -= 2;
852         }
853         if (remainingBytes == 1) {
854             long pos = end - 1;
855             result |= UNSAFE.getByte(bytes1, pos) ^ UNSAFE.getByte(bytes2, pos + diff);
856         }
857         return ConstantTimeUtils.equalsConstantTime(result, 0);
858     }
859 
860     static boolean isZero(byte[] bytes, int startPos, int length) {
861         if (length <= 0) {
862             return true;
863         }
864         final long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
865         int remainingBytes = length & 7;
866         final long end = baseOffset + remainingBytes;
867         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
868             if (UNSAFE.getLong(bytes, i) != 0) {
869                 return false;
870             }
871         }
872 
873         if (remainingBytes >= 4) {
874             remainingBytes -= 4;
875             if (UNSAFE.getInt(bytes, baseOffset + remainingBytes) != 0) {
876                 return false;
877             }
878         }
879         if (remainingBytes >= 2) {
880             return UNSAFE.getChar(bytes, baseOffset) == 0 &&
881                     (remainingBytes == 2 || bytes[startPos + 2] == 0);
882         }
883         return bytes[startPos] == 0;
884     }
885 
886     static int hashCodeAscii(byte[] bytes, int startPos, int length) {
887         int hash = HASH_CODE_ASCII_SEED;
888         long baseOffset = BYTE_ARRAY_BASE_OFFSET + startPos;
889         final int remainingBytes = length & 7;
890         final long end = baseOffset + remainingBytes;
891         for (long i = baseOffset - 8 + length; i >= end; i -= 8) {
892             hash = hashCodeAsciiCompute(UNSAFE.getLong(bytes, i), hash);
893         }
894         if (remainingBytes == 0) {
895             return hash;
896         }
897         int hcConst = HASH_CODE_C1;
898         if (remainingBytes != 2 & remainingBytes != 4 & remainingBytes != 6) { // 1, 3, 5, 7
899             hash = hash * HASH_CODE_C1 + hashCodeAsciiSanitize(UNSAFE.getByte(bytes, baseOffset));
900             hcConst = HASH_CODE_C2;
901             baseOffset++;
902         }
903         if (remainingBytes != 1 & remainingBytes != 4 & remainingBytes != 5) { // 2, 3, 6, 7
904             hash = hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getShort(bytes, baseOffset));
905             hcConst = hcConst == HASH_CODE_C1 ? HASH_CODE_C2 : HASH_CODE_C1;
906             baseOffset += 2;
907         }
908         if (remainingBytes >= 4) { // 4, 5, 6, 7
909             return hash * hcConst + hashCodeAsciiSanitize(UNSAFE.getInt(bytes, baseOffset));
910         }
911         return hash;
912     }
913 
914     static int hashCodeAsciiCompute(long value, int hash) {
915         // masking with 0x1f reduces the number of overall bits that impact the hash code but makes the hash
916         // code the same regardless of character case (upper case or lower case hash is the same).
917         return hash * HASH_CODE_C1 +
918                 // Low order int
919                 hashCodeAsciiSanitize((int) value) * HASH_CODE_C2 +
920                 // High order int
921                 (int) ((value & 0x1f1f1f1f00000000L) >>> 32);
922     }
923 
924     static int hashCodeAsciiSanitize(int value) {
925         return value & 0x1f1f1f1f;
926     }
927 
928     static int hashCodeAsciiSanitize(short value) {
929         return value & 0x1f1f;
930     }
931 
932     static int hashCodeAsciiSanitize(byte value) {
933         return value & 0x1f;
934     }
935 
936     static ClassLoader getClassLoader(final Class<?> clazz) {
937         if (System.getSecurityManager() == null) {
938             return clazz.getClassLoader();
939         } else {
940             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
941                 @Override
942                 public ClassLoader run() {
943                     return clazz.getClassLoader();
944                 }
945             });
946         }
947     }
948 
949     static ClassLoader getContextClassLoader() {
950         if (System.getSecurityManager() == null) {
951             return Thread.currentThread().getContextClassLoader();
952         } else {
953             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
954                 @Override
955                 public ClassLoader run() {
956                     return Thread.currentThread().getContextClassLoader();
957                 }
958             });
959         }
960     }
961 
962     static ClassLoader getSystemClassLoader() {
963         if (System.getSecurityManager() == null) {
964             return ClassLoader.getSystemClassLoader();
965         } else {
966             return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
967                 @Override
968                 public ClassLoader run() {
969                     return ClassLoader.getSystemClassLoader();
970                 }
971             });
972         }
973     }
974 
975     static int addressSize() {
976         return UNSAFE.addressSize();
977     }
978 
979     static long allocateMemory(long size) {
980         return UNSAFE.allocateMemory(size);
981     }
982 
983     static void freeMemory(long address) {
984         UNSAFE.freeMemory(address);
985     }
986 
987     static long reallocateMemory(long address, long newSize) {
988         return UNSAFE.reallocateMemory(address, newSize);
989     }
990 
991     static boolean isAndroid() {
992         return IS_ANDROID;
993     }
994 
995     private static boolean isAndroid0() {
996         // Idea: Sometimes java binaries include Android classes on the classpath, even if it isn't actually Android.
997         // Rather than check if certain classes are present, just check the VM, which is tied to the JDK.
998 
999         // Optional improvement: check if `android.os.Build.VERSION` is >= 24. On later versions of Android, the
1000         // OpenJDK is used, which means `Unsafe` will actually work as expected.
1001 
1002         // Android sets this property to Dalvik, regardless of whether it actually is.
1003         String vmName = SystemPropertyUtil.get("java.vm.name");
1004         boolean isAndroid = "Dalvik".equals(vmName);
1005         if (isAndroid) {
1006             logger.debug("Platform: Android");
1007         }
1008         return isAndroid;
1009     }
1010 
1011     private static boolean explicitTryReflectionSetAccessible0() {
1012         // we disable reflective access
1013         return SystemPropertyUtil.getBoolean("io.netty.tryReflectionSetAccessible",
1014                 javaVersion() < 9 || RUNNING_IN_NATIVE_IMAGE);
1015     }
1016 
1017     static boolean isExplicitTryReflectionSetAccessible() {
1018         return IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE;
1019     }
1020 
1021     static int javaVersion() {
1022         return JAVA_VERSION;
1023     }
1024 
1025     private static int javaVersion0() {
1026         final int majorVersion;
1027 
1028         if (isAndroid0()) {
1029             majorVersion = 6;
1030         } else {
1031             majorVersion = majorVersionFromJavaSpecificationVersion();
1032         }
1033 
1034         logger.debug("Java version: {}", majorVersion);
1035 
1036         return majorVersion;
1037     }
1038 
1039     // Package-private for testing only
1040     static int majorVersionFromJavaSpecificationVersion() {
1041         return majorVersion(SystemPropertyUtil.get("java.specification.version", "1.6"));
1042     }
1043 
1044     // Package-private for testing only
1045     static int majorVersion(final String javaSpecVersion) {
1046         final String[] components = javaSpecVersion.split("\\.");
1047         final int[] version = new int[components.length];
1048         for (int i = 0; i < components.length; i++) {
1049             version[i] = Integer.parseInt(components[i]);
1050         }
1051 
1052         if (version[0] == 1) {
1053             assert version[1] >= 6;
1054             return version[1];
1055         } else {
1056             return version[0];
1057         }
1058     }
1059 
1060     private PlatformDependent0() {
1061     }
1062 }