View Javadoc
1   /*
2    * Copyright 2024 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  
21  import java.lang.invoke.MethodHandle;
22  import java.lang.invoke.MethodHandles;
23  import java.lang.reflect.Field;
24  import java.security.AccessController;
25  import java.security.PrivilegedAction;
26  import java.util.ArrayList;
27  import java.util.List;
28  
29  import static java.lang.invoke.MethodType.methodType;
30  
31  /**
32   * Helper class which delegates to {@link sun.misc.Unsafe} via {@link MethodHandle}s.
33   */
34  final class SunMiscUnsafeAccess {
35  
36      private static final InternalLogger logger = InternalLoggerFactory.getInstance(SunMiscUnsafeAccess.class);
37      private static final String SUN_MISC_UNSAFE_CLASS_NAME = "sun.misc.Unsafe";
38      private static final Throwable EXPLICIT_NO_UNSAFE_CAUSE = explicitNoUnsafeCause0();
39      private static final Throwable UNSAFE_UNAVAILABILITY_CAUSE;
40      private static final MethodHandle UNSAFE_ALLOCATE_MEMORY_HANDLE;
41      private static final MethodHandle UNSAFE_COPY_MEMORY_WITH_OBJECT_HANDLE;
42      private static final MethodHandle UNSAFE_COPY_MEMORY_WITH_LONG_HANDLE;
43      private static final MethodHandle UNSAFE_OBJECT_FIELD_OFFSET_HANDLE;
44      private static final MethodHandle UNSAFE_REALLOCATE_MEMORY_HANDLE;
45      private static final MethodHandle UNSAFE_FREE_MEMORY_HANDLE;
46      private static final MethodHandle UNSAFE_SET_MEMORY_WITH_LONG_HANDLE;
47      private static final MethodHandle UNSAFE_SET_MEMORY_WITH_OBJECT_HANDLE;
48      private static final MethodHandle UNSAFE_GET_BYTE_WITH_LONG_HANDLE;
49      private static final MethodHandle UNSAFE_GET_BYTE_WITH_OBJECT_HANDLE;
50      private static final MethodHandle UNSAFE_GET_CHAR_WITH_OBJECT_HANDLE;
51      private static final MethodHandle UNSAFE_GET_SHORT_WITH_LONG_HANDLE;
52      private static final MethodHandle UNSAFE_GET_SHORT_WITH_OBJECT_HANDLE;
53      private static final MethodHandle UNSAFE_GET_INT_WITH_LONG_HANDLE;
54      private static final MethodHandle UNSAFE_GET_INT_WITH_OBJECT_HANDLE;
55      private static final MethodHandle UNSAFE_GET_INT_VOLATILE_WITH_OBJECT_HANDLE;
56      private static final MethodHandle UNSAFE_GET_LONG_WITH_LONG_HANDLE;
57      private static final MethodHandle UNSAFE_GET_LONG_WITH_OBJECT_HANDLE;
58      private static final MethodHandle UNSAFE_GET_OBJECT_WITH_OBJECT_HANDLE;
59      private static final MethodHandle UNSAFE_PUT_BYTE_WITH_LONG_HANDLE;
60      private static final MethodHandle UNSAFE_PUT_BYTE_WITH_OBJECT_HANDLE;
61      private static final MethodHandle UNSAFE_PUT_SHORT_WITH_LONG_HANDLE;
62      private static final MethodHandle UNSAFE_PUT_SHORT_WITH_OBJECT_HANDLE;
63      private static final MethodHandle UNSAFE_PUT_INT_WITH_LONG_HANDLE;
64      private static final MethodHandle UNSAFE_PUT_INT_WITH_OBJECT_HANDLE;
65      private static final MethodHandle UNSAFE_PUT_ORDERED_INT_WITH_OBJECT_HANDLE;
66      private static final MethodHandle UNSAFE_PUT_LONG_WITH_LONG_HANDLE;
67      private static final MethodHandle UNSAFE_PUT_LONG_WITH_OBJECT_HANDLE;
68      private static final MethodHandle UNSAFE_PUT_OBJECT_WITH_OBJECT_HANDLE;
69      private static final MethodHandle UNSAFE_ADDRESS_SIZE_HANDLE;
70      private static final MethodHandle UNSAFE_STORE_FENCE_HANDLE;
71      private static final MethodHandle UNSAFE_ARRAY_BASE_OFFSET_HANDLE;
72      private static final MethodHandle UNSAFE_ARRAY_INDEX_SCALE_HANDLE;
73      private static final MethodHandle UNSAFE_STATIC_FIELD_OFFSET_HANDLE;
74      private static final MethodHandle UNSAFE_STATIC_FIELD_BASE_HANDLE;
75      private static final MethodHandle UNSAFE_GET_BOOLEAN_WITH_OBJECT_HANDLE;
76  
77      // Store as Object so we can compile with --release
78      static final Object UNSAFE;
79  
80      static {
81          MethodHandles.Lookup lookup = MethodHandles.lookup();
82          Throwable unsafeUnavailabilityCause = null;
83          Object unsafe;
84          MethodHandle allocateMemoryHandle = null;
85          MethodHandle copyMemoryWithLongHandle = null;
86          MethodHandle copyMemoryWithObjectHandle = null;
87          MethodHandle objectFieldOffsetHandle = null;
88          MethodHandle staticFieldOffsetHandle = null;
89          MethodHandle staticFieldBaseHandle = null;
90          MethodHandle arrayBaseOffsetHandle = null;
91          MethodHandle arrayIndexScaleHandle = null;
92          MethodHandle reallocateMemoryHandle = null;
93          MethodHandle freeMemoryHandle = null;
94          MethodHandle setMemoryWithLongHandle = null;
95          MethodHandle setMemoryWithObjectHandle = null;
96          MethodHandle getBooleanWithObjectHandle = null;
97          MethodHandle getByteWithLongHandle = null;
98          MethodHandle getByteWithObjectHandle = null;
99          MethodHandle getCharWithObjectHandle = null;
100         MethodHandle getShortWithLongHandle = null;
101         MethodHandle getShortWithObjectHandle = null;
102         MethodHandle getIntWithLongHandle = null;
103         MethodHandle getIntWithObjectHandle = null;
104         MethodHandle getIntVolatileWithObjectHandle = null;
105         MethodHandle getLongWithLongHandle = null;
106         MethodHandle getLongWithObjectHandle = null;
107         MethodHandle getObjectWithObjectHandle = null;
108         MethodHandle putByteWithLongHandle = null;
109         MethodHandle putByteWithObjectHandle = null;
110         MethodHandle putShortWithLongHandle = null;
111         MethodHandle putShortWithObjectHandle = null;
112         MethodHandle putIntWithLongHandle = null;
113         MethodHandle putIntWithObjectHandle = null;
114         MethodHandle putOrderedIntWithObjectHandle = null;
115         MethodHandle putLongWithLongHandle = null;
116         MethodHandle putLongWithObjectHandle = null;
117         MethodHandle putObjectWithObjectHandle = null;
118         MethodHandle addressSizeHandle = null;
119         MethodHandle storeFenceHandle = null;
120 
121         if ((unsafeUnavailabilityCause = EXPLICIT_NO_UNSAFE_CAUSE) != null) {
122             unsafe = null;
123         } else {
124             // attempt to access field Unsafe#theUnsafe
125             final Object maybeUnsafe = AccessController.doPrivileged(new PrivilegedAction<Object>() {
126                 @Override
127                 public Object run() {
128                     try {
129                         Class<?> unsafeClass = Class.forName(SUN_MISC_UNSAFE_CLASS_NAME);
130                         final Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
131                         // We always want to try using Unsafe as the access still works on java9 as well and
132                         // we need it for out native-transports and many optimizations.
133                         Throwable cause = ReflectionUtil.trySetAccessible(unsafeField, false);
134                         if (cause != null) {
135                             return cause;
136                         }
137                         // the unsafe instance
138                         return unsafeField.get(null);
139                     } catch (NoSuchFieldException | SecurityException |
140                              IllegalAccessException | ClassNotFoundException e) {
141                         return e;
142                     } catch (NoClassDefFoundError e) {
143                         // Also catch NoClassDefFoundError in case someone uses for example OSGI and it made
144                         // Unsafe unloadable.
145                         return e;
146                     }
147                 }
148             });
149 
150             // the conditional check here can not be replaced with checking that maybeUnsafe
151             // is an instanceof Unsafe and reversing the if and else blocks; this is because an
152             // instanceof check against Unsafe will trigger a class load and we might not have
153             // the runtime permission accessClassInPackage.sun.misc
154             if (maybeUnsafe instanceof Throwable) {
155                 unsafe = null;
156                 unsafeUnavailabilityCause = (Throwable) maybeUnsafe;
157                 if (logger.isTraceEnabled()) {
158                     logger.debug("{}: unavailable", SUN_MISC_UNSAFE_CLASS_NAME, maybeUnsafe);
159                 } else {
160                     logger.debug("{}.theUnsafe: unavailable: {}", SUN_MISC_UNSAFE_CLASS_NAME,
161                             unsafeUnavailabilityCause.getMessage());
162                 }
163             } else {
164                 unsafe = maybeUnsafe;
165                 logger.debug("sun.misc.Unsafe.theUnsafe: available");
166             }
167 
168             // ensure the unsafe supports all necessary methods to work around the mistake in the latest OpenJDK,
169             // or that they haven't been removed by JEP 471.
170             // https://github.com/netty/netty/issues/1061
171             // https://www.mail-archive.com/[email protected]/msg00698.html
172             // https://openjdk.org/jeps/471
173             if (unsafe != null) {
174                 final Object finalUnsafe = unsafe;
175                 final Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
176                     @Override
177                     public Object run() {
178                         try {
179                             // Other methods like invokeCleaner() are tested for elsewhere.
180                             Class<?> cls = finalUnsafe.getClass();
181                             List<MethodHandle> methodHandles = new ArrayList<>();
182                             MethodHandle allocateMemoryHandle = lookup.findVirtual(cls, "allocateMemory",
183                                     methodType(long.class, long.class)).bindTo(finalUnsafe);
184                             methodHandles.add(allocateMemoryHandle);
185                             methodHandles.add(
186                                     lookup.findVirtual(cls, "copyMemory",
187                                                     methodType(void.class, long.class, long.class, long.class))
188                                             .bindTo(finalUnsafe)
189                             );
190                             methodHandles.add(
191                                     lookup.findVirtual(cls, "copyMemory",
192                                                     methodType(void.class, Object.class, long.class, Object.class,
193                                                             long.class, long.class))
194                                             .bindTo(finalUnsafe)
195                                     );
196                             methodHandles.add(
197                                     lookup.findVirtual(cls, "objectFieldOffset",
198                                                     methodType(long.class, Field.class))
199                                             .bindTo(finalUnsafe)
200                             );
201                             methodHandles.add(
202                                     lookup.findVirtual(cls, "staticFieldOffset",
203                                                     methodType(long.class, Field.class))
204                                             .bindTo(finalUnsafe)
205                             );
206                             methodHandles.add(
207                                     lookup.findVirtual(cls, "staticFieldBase",
208                                                     methodType(Object.class, Field.class))
209                                             .bindTo(finalUnsafe)
210                             );
211                             methodHandles.add(
212                                     lookup.findVirtual(cls, "arrayBaseOffset",
213                                                     methodType(int.class, Class.class))
214                                             .bindTo(finalUnsafe)
215                             );
216                             methodHandles.add(
217                                     lookup.findVirtual(cls, "arrayIndexScale",
218                                                     methodType(int.class, Class.class))
219                                             .bindTo(finalUnsafe)
220                             );
221                             methodHandles.add(
222                                     lookup.findVirtual(cls, "reallocateMemory",
223                                                     methodType(long.class, long.class, long.class))
224                                             .bindTo(finalUnsafe)
225                             );
226                             MethodHandle freeMemoryHandle = lookup.findVirtual(cls, "freeMemory",
227                                     methodType(void.class, long.class)).bindTo(finalUnsafe);
228                             methodHandles.add(freeMemoryHandle);
229                             methodHandles.add(
230                                     lookup.findVirtual(cls, "setMemory",
231                                                     methodType(void.class, long.class, long.class, byte.class))
232                                             .bindTo(finalUnsafe)
233                             );
234                             methodHandles.add(
235                                     lookup.findVirtual(cls, "setMemory",
236                                                     methodType(void.class, Object.class, long.class,
237                                                             long.class, byte.class))
238                                             .bindTo(finalUnsafe)
239                             );
240                             methodHandles.add(
241                                     lookup.findVirtual(cls, "getBoolean",
242                                                     methodType(boolean.class, Object.class, long.class))
243                                             .bindTo(finalUnsafe)
244                             );
245                             methodHandles.add(
246                                     lookup.findVirtual(cls, "getByte",
247                                                     methodType(byte.class, long.class))
248                                             .bindTo(finalUnsafe)
249                             );
250                             methodHandles.add(
251                                     lookup.findVirtual(cls, "getByte",
252                                                     methodType(byte.class, Object.class, long.class))
253                                             .bindTo(finalUnsafe)
254                             );
255                             methodHandles.add(
256                                     lookup.findVirtual(cls, "getChar",
257                                                     methodType(char.class, Object.class, long.class))
258                                             .bindTo(finalUnsafe)
259                             );
260                             methodHandles.add(
261                                     lookup.findVirtual(cls, "getShort",
262                                                     methodType(short.class, long.class))
263                                             .bindTo(finalUnsafe)
264                             );
265                             methodHandles.add(
266                                     lookup.findVirtual(cls, "getShort",
267                                                     methodType(short.class, Object.class, long.class))
268                                             .bindTo(finalUnsafe)
269                             );
270                             methodHandles.add(
271                                     lookup.findVirtual(cls, "getInt",
272                                                     methodType(int.class, long.class))
273                                             .bindTo(finalUnsafe)
274                             );
275                             methodHandles.add(
276                                     lookup.findVirtual(cls, "getInt",
277                                                     methodType(int.class, Object.class, long.class))
278                                             .bindTo(finalUnsafe)
279                             );
280                             methodHandles.add(
281                                     lookup.findVirtual(cls, "getIntVolatile",
282                                                     methodType(int.class, Object.class, long.class))
283                                             .bindTo(finalUnsafe)
284                             );
285                             methodHandles.add(
286                                     lookup.findVirtual(cls, "getLong",
287                                                     methodType(long.class, long.class))
288                                             .bindTo(finalUnsafe)
289                             );
290                             methodHandles.add(
291                                     lookup.findVirtual(cls, "getLong",
292                                                     methodType(long.class, Object.class, long.class))
293                                             .bindTo(finalUnsafe)
294                             );
295                             methodHandles.add(
296                                     lookup.findVirtual(cls, "getObject",
297                                                     methodType(Object.class, Object.class, long.class))
298                                             .bindTo(finalUnsafe)
299                             );
300                             methodHandles.add(
301                                     lookup.findVirtual(cls, "putByte",
302                                                     methodType(void.class, long.class, byte.class))
303                                             .bindTo(finalUnsafe)
304                             );
305                             methodHandles.add(
306                                     lookup.findVirtual(cls, "putByte",
307                                                     methodType(void.class, Object.class, long.class, byte.class))
308                                             .bindTo(finalUnsafe)
309                             );
310                             methodHandles.add(
311                                     lookup.findVirtual(cls, "putShort",
312                                                     methodType(void.class, long.class, short.class))
313                                             .bindTo(finalUnsafe)
314                             );
315                             methodHandles.add(
316                                     lookup.findVirtual(cls, "putShort",
317                                                     methodType(void.class, Object.class, long.class, short.class))
318                                             .bindTo(finalUnsafe)
319                             );
320                             methodHandles.add(
321                                     lookup.findVirtual(cls, "putInt",
322                                             methodType(void.class, long.class, int.class))
323                                             .bindTo(finalUnsafe)
324                             );
325                             methodHandles.add(
326                                     lookup.findVirtual(cls, "putInt",
327                                                     methodType(void.class, Object.class, long.class, int.class))
328                                             .bindTo(finalUnsafe)
329                             );
330                             methodHandles.add(
331                                     lookup.findVirtual(cls, "putOrderedInt",
332                                                     methodType(void.class, Object.class, long.class, int.class))
333                                             .bindTo(finalUnsafe)
334                             );
335                             MethodHandle putLongHandle = lookup.findVirtual(cls, "putLong",
336                                     methodType(void.class, long.class, long.class)).bindTo(finalUnsafe);
337                             methodHandles.add(putLongHandle);
338                             methodHandles.add(
339                                     lookup.findVirtual(cls, "putLong",
340                                                     methodType(void.class, Object.class, long.class, long.class))
341                                             .bindTo(finalUnsafe)
342                             );
343                             methodHandles.add(
344                                     lookup.findVirtual(cls, "putObject",
345                                                     methodType(void.class, Object.class, long.class, Object.class))
346                                             .bindTo(finalUnsafe)
347                             );
348                             methodHandles.add(
349                                     lookup.findVirtual(cls, "addressSize",
350                                                     methodType(int.class))
351                                             .bindTo(finalUnsafe)
352                             );
353                             methodHandles.add(
354                                     lookup.findVirtual(cls, "storeFence",
355                                                     methodType(void.class))
356                                             .bindTo(finalUnsafe)
357                             );
358 
359                             // The following tests the methods are usable.
360                             // Will throw UnsupportedOperationException if unsafe memory access is denied:
361                             long address = (long) allocateMemoryHandle.invokeExact((long) 8);
362                             putLongHandle.invokeExact(address, (long) 42);
363                             freeMemoryHandle.invokeExact(address);
364                             return methodHandles;
365                         } catch (Throwable cause) {
366                             return cause;
367                         }
368                     }
369                 });
370 
371                 if (maybeException instanceof Throwable) {
372                     // Unsafe.copyMemory(Object, long, Object, long, long) unavailable.
373                     unsafe = null;
374                     unsafeUnavailabilityCause = (Throwable) maybeException;
375                     if (logger.isTraceEnabled()) {
376                         logger.debug("{} method unavailable:", SUN_MISC_UNSAFE_CLASS_NAME, unsafeUnavailabilityCause);
377                     } else {
378                         logger.debug("{} method unavailable: {}", SUN_MISC_UNSAFE_CLASS_NAME,
379                                 ((Throwable) maybeException).getMessage());
380                     }
381                 } else {
382                     logger.debug("{} base methods: all available", SUN_MISC_UNSAFE_CLASS_NAME);
383                     @SuppressWarnings("unchecked") List<MethodHandle> handles = (List<MethodHandle>) maybeException;
384 
385                     int idx = 0;
386                     allocateMemoryHandle = handles.get(idx++);
387                     copyMemoryWithLongHandle = handles.get(idx++);
388                     copyMemoryWithObjectHandle = handles.get(idx++);
389                     objectFieldOffsetHandle = handles.get(idx++);
390                     staticFieldOffsetHandle = handles.get(idx++);
391                     staticFieldBaseHandle = handles.get(idx++);
392                     arrayBaseOffsetHandle = handles.get(idx++);
393                     arrayIndexScaleHandle = handles.get(idx++);
394                     reallocateMemoryHandle = handles.get(idx++);
395                     freeMemoryHandle = handles.get(idx++);
396                     setMemoryWithLongHandle = handles.get(idx++);
397                     setMemoryWithObjectHandle = handles.get(idx++);
398                     getBooleanWithObjectHandle = handles.get(idx++);
399                     getByteWithLongHandle = handles.get(idx++);
400                     getByteWithObjectHandle = handles.get(idx++);
401                     getCharWithObjectHandle = handles.get(idx++);
402                     getShortWithLongHandle = handles.get(idx++);
403                     getShortWithObjectHandle = handles.get(idx++);
404                     getIntWithLongHandle = handles.get(idx++);
405                     getIntWithObjectHandle = handles.get(idx++);
406                     getIntVolatileWithObjectHandle = handles.get(idx++);
407                     getLongWithLongHandle = handles.get(idx++);
408                     getLongWithObjectHandle = handles.get(idx++);
409                     getObjectWithObjectHandle = handles.get(idx++);
410                     putByteWithLongHandle = handles.get(idx++);
411                     putByteWithObjectHandle = handles.get(idx++);
412                     putShortWithLongHandle = handles.get(idx++);
413                     putShortWithObjectHandle = handles.get(idx++);
414                     putIntWithLongHandle = handles.get(idx++);
415                     putIntWithObjectHandle = handles.get(idx++);
416                     putOrderedIntWithObjectHandle = handles.get(idx++);
417                     putLongWithLongHandle = handles.get(idx++);
418                     putLongWithObjectHandle = handles.get(idx++);
419                     putObjectWithObjectHandle = handles.get(idx++);
420                     addressSizeHandle = handles.get(idx++);
421                     storeFenceHandle = handles.get(idx);
422                 }
423             }
424         }
425 
426         UNSAFE_ALLOCATE_MEMORY_HANDLE = allocateMemoryHandle;
427         UNSAFE_COPY_MEMORY_WITH_LONG_HANDLE = copyMemoryWithLongHandle;
428         UNSAFE_COPY_MEMORY_WITH_OBJECT_HANDLE = copyMemoryWithObjectHandle;
429         UNSAFE_OBJECT_FIELD_OFFSET_HANDLE = objectFieldOffsetHandle;
430         UNSAFE_REALLOCATE_MEMORY_HANDLE = reallocateMemoryHandle;
431         UNSAFE_FREE_MEMORY_HANDLE = freeMemoryHandle;
432         UNSAFE_SET_MEMORY_WITH_LONG_HANDLE = setMemoryWithLongHandle;
433         UNSAFE_SET_MEMORY_WITH_OBJECT_HANDLE = setMemoryWithObjectHandle;
434         UNSAFE_GET_BYTE_WITH_LONG_HANDLE = getByteWithLongHandle;
435         UNSAFE_GET_BYTE_WITH_OBJECT_HANDLE = getByteWithObjectHandle;
436         UNSAFE_GET_CHAR_WITH_OBJECT_HANDLE = getCharWithObjectHandle;
437         UNSAFE_GET_SHORT_WITH_LONG_HANDLE = getShortWithLongHandle;
438         UNSAFE_GET_SHORT_WITH_OBJECT_HANDLE = getShortWithObjectHandle;
439         UNSAFE_GET_INT_WITH_LONG_HANDLE = getIntWithLongHandle;
440         UNSAFE_GET_INT_WITH_OBJECT_HANDLE = getIntWithObjectHandle;
441         UNSAFE_GET_INT_VOLATILE_WITH_OBJECT_HANDLE = getIntVolatileWithObjectHandle;
442         UNSAFE_GET_LONG_WITH_LONG_HANDLE = getLongWithLongHandle;
443         UNSAFE_GET_LONG_WITH_OBJECT_HANDLE = getLongWithObjectHandle;
444         UNSAFE_GET_OBJECT_WITH_OBJECT_HANDLE = getObjectWithObjectHandle;
445         UNSAFE_PUT_BYTE_WITH_LONG_HANDLE = putByteWithLongHandle;
446         UNSAFE_PUT_BYTE_WITH_OBJECT_HANDLE = putByteWithObjectHandle;
447         UNSAFE_PUT_SHORT_WITH_LONG_HANDLE = putShortWithLongHandle;
448         UNSAFE_PUT_SHORT_WITH_OBJECT_HANDLE = putShortWithObjectHandle;
449         UNSAFE_PUT_INT_WITH_LONG_HANDLE = putIntWithLongHandle;
450         UNSAFE_PUT_INT_WITH_OBJECT_HANDLE = putIntWithObjectHandle;
451         UNSAFE_PUT_ORDERED_INT_WITH_OBJECT_HANDLE = putOrderedIntWithObjectHandle;
452         UNSAFE_PUT_LONG_WITH_LONG_HANDLE = putLongWithLongHandle;
453         UNSAFE_PUT_LONG_WITH_OBJECT_HANDLE = putLongWithObjectHandle;
454         UNSAFE_PUT_OBJECT_WITH_OBJECT_HANDLE = putObjectWithObjectHandle;
455         UNSAFE_ADDRESS_SIZE_HANDLE = addressSizeHandle;
456         UNSAFE_STORE_FENCE_HANDLE = storeFenceHandle;
457         UNSAFE_ARRAY_BASE_OFFSET_HANDLE = arrayBaseOffsetHandle;
458         UNSAFE_ARRAY_INDEX_SCALE_HANDLE = arrayIndexScaleHandle;
459         UNSAFE_STATIC_FIELD_OFFSET_HANDLE = staticFieldOffsetHandle;
460         UNSAFE_STATIC_FIELD_BASE_HANDLE = staticFieldBaseHandle;
461         UNSAFE_GET_BOOLEAN_WITH_OBJECT_HANDLE = getBooleanWithObjectHandle;
462         UNSAFE_UNAVAILABILITY_CAUSE = unsafeUnavailabilityCause;
463         UNSAFE = unsafe;
464     }
465 
466     static boolean isExplicitNoUnsafe() {
467         return EXPLICIT_NO_UNSAFE_CAUSE != null;
468     }
469 
470     private static Throwable explicitNoUnsafeCause0() {
471         boolean noUnsafe = SystemPropertyUtil.getBoolean("io.netty.noUnsafe", false);
472         logger.debug("-Dio.netty.noUnsafe: {}", noUnsafe);
473 
474         // See JDK 23 JEP 471 https://openjdk.org/jeps/471 and sun.misc.Unsafe.beforeMemoryAccess() on JDK 23+.
475         String unsafeMemoryAccess = SystemPropertyUtil.get("sun.misc.unsafe.memory.access", "<unspecified>");
476         if (!("allow".equals(unsafeMemoryAccess) || "<unspecified>".equals(unsafeMemoryAccess))) {
477             logger.debug("--sun-misc-unsafe-memory-access={}", unsafeMemoryAccess);
478             noUnsafe = true;
479         }
480 
481         if (noUnsafe) {
482             logger.debug("{}: unavailable (io.netty.noUnsafe)", SUN_MISC_UNSAFE_CLASS_NAME);
483             return new UnsupportedOperationException("sun.misc.Unsafe: unavailable (io.netty.noUnsafe)");
484         }
485 
486         // Legacy properties
487         String unsafePropName;
488         if (SystemPropertyUtil.contains("io.netty.tryUnsafe")) {
489             unsafePropName = "io.netty.tryUnsafe";
490         } else {
491             unsafePropName = "org.jboss.netty.tryUnsafe";
492         }
493 
494         if (!SystemPropertyUtil.getBoolean(unsafePropName, true)) {
495             String msg = "{}: unavailable ({})";
496             logger.debug(msg, SUN_MISC_UNSAFE_CLASS_NAME, unsafePropName);
497             return new UnsupportedOperationException(msg);
498         }
499 
500         return null;
501     }
502 
503     static int arrayIndexScale(Class<?> arrayClass) {
504         try {
505             return (int) UNSAFE_ARRAY_INDEX_SCALE_HANDLE.invokeExact(arrayClass);
506         } catch (Throwable cause) {
507             rethrowIfPossible(cause);
508             throw new IllegalStateException(cause);
509         }
510     }
511 
512     static int arrayBaseOffset(Class<?> arrayClass) {
513         try {
514             return (int) UNSAFE_ARRAY_BASE_OFFSET_HANDLE.invokeExact(arrayClass);
515         } catch (Throwable cause) {
516             rethrowIfPossible(cause);
517             throw new IllegalStateException(cause);
518         }
519     }
520 
521     static void storeFence() {
522         try {
523             UNSAFE_STORE_FENCE_HANDLE.invokeExact();
524         } catch (Throwable cause) {
525             rethrowIfPossible(cause);
526             throw new IllegalStateException(cause);
527         }
528     }
529 
530     static long objectFieldOffset(Field field) {
531         try {
532             return (long) UNSAFE_OBJECT_FIELD_OFFSET_HANDLE.invokeExact(field);
533         } catch (Throwable cause) {
534             rethrowIfPossible(cause);
535             throw new IllegalStateException(cause);
536         }
537     }
538 
539     static long staticFieldOffset(Field f) {
540         try {
541             return (long) UNSAFE_STATIC_FIELD_OFFSET_HANDLE.invokeExact(f);
542         } catch (Throwable cause) {
543             rethrowIfPossible(cause);
544             throw new IllegalStateException(cause);
545         }
546     }
547 
548     static Object staticFieldBase(Field f) {
549         try {
550             return UNSAFE_STATIC_FIELD_BASE_HANDLE.invokeExact(f);
551         } catch (Throwable cause) {
552             rethrowIfPossible(cause);
553             throw new IllegalStateException(cause);
554         }
555     }
556 
557     static boolean getBoolean(Object o, long offset) {
558         try {
559             return (boolean) UNSAFE_GET_BOOLEAN_WITH_OBJECT_HANDLE.invokeExact(o, offset);
560         } catch (Throwable cause) {
561             rethrowIfPossible(cause);
562             throw new IllegalStateException(cause);
563         }
564     }
565 
566     static Object getObject(Object object, long fieldOffset) {
567         try {
568             return (Object) UNSAFE_GET_OBJECT_WITH_OBJECT_HANDLE.invokeExact(object, fieldOffset);
569         } catch (Throwable cause) {
570             rethrowIfPossible(cause);
571             throw new IllegalStateException(cause);
572         }
573     }
574 
575     static long getLong(Object object, long fieldOffset) {
576         try {
577             return (long) UNSAFE_GET_LONG_WITH_OBJECT_HANDLE.invokeExact(object, fieldOffset);
578         } catch (Throwable cause) {
579             rethrowIfPossible(cause);
580             throw new IllegalStateException(cause);
581         }
582     }
583 
584     static byte getByte(long address) {
585         try {
586             return (byte) UNSAFE_GET_BYTE_WITH_LONG_HANDLE.invokeExact(address);
587         } catch (Throwable cause) {
588             rethrowIfPossible(cause);
589             throw new IllegalStateException(cause);
590         }
591     }
592 
593     static short getShort(long address) {
594         try {
595             return (short) UNSAFE_GET_SHORT_WITH_LONG_HANDLE.invokeExact(address);
596         } catch (Throwable cause) {
597             rethrowIfPossible(cause);
598             throw new IllegalStateException(cause);
599         }
600     }
601 
602     static int getInt(long address) {
603         try {
604             return (int) UNSAFE_GET_INT_WITH_LONG_HANDLE.invokeExact(address);
605         } catch (Throwable cause) {
606             rethrowIfPossible(cause);
607             throw new IllegalStateException(cause);
608         }
609     }
610 
611     static long getLong(long address) {
612         try {
613             return (long) UNSAFE_GET_LONG_WITH_LONG_HANDLE.invokeExact(address);
614         } catch (Throwable cause) {
615             rethrowIfPossible(cause);
616             throw new IllegalStateException(cause);
617         }
618     }
619 
620     static byte getByte(Object o, long offset) {
621         try {
622             return (byte) UNSAFE_GET_BYTE_WITH_OBJECT_HANDLE.invokeExact(o, offset);
623         } catch (Throwable cause) {
624             rethrowIfPossible(cause);
625             throw new IllegalStateException(cause);
626         }
627     }
628 
629     static short getShort(Object o, long offset) {
630         try {
631             return (short) UNSAFE_GET_SHORT_WITH_OBJECT_HANDLE.invokeExact(o, offset);
632         } catch (Throwable cause) {
633             rethrowIfPossible(cause);
634             throw new IllegalStateException(cause);
635         }
636     }
637 
638     static char getChar(Object o, long offset) {
639         try {
640             return (char) UNSAFE_GET_CHAR_WITH_OBJECT_HANDLE.invokeExact(o, offset);
641         } catch (Throwable cause) {
642             rethrowIfPossible(cause);
643             throw new IllegalStateException(cause);
644         }
645     }
646 
647     static int getInt(Object o, long offset) {
648         try {
649             return (int) UNSAFE_GET_INT_WITH_OBJECT_HANDLE.invokeExact(o, offset);
650         } catch (Throwable cause) {
651             rethrowIfPossible(cause);
652             throw new IllegalStateException(cause);
653         }
654     }
655 
656     static int getIntVolatile(Object o, long address) {
657         try {
658             return (int) UNSAFE_GET_INT_VOLATILE_WITH_OBJECT_HANDLE.invokeExact(o, address);
659         } catch (Throwable cause) {
660             rethrowIfPossible(cause);
661             throw new IllegalStateException(cause);
662         }
663     }
664 
665     static void putIntOrdered(Object o, long address, int newValue) {
666         try {
667             UNSAFE_PUT_ORDERED_INT_WITH_OBJECT_HANDLE.invokeExact(o, address, newValue);
668         } catch (Throwable cause) {
669             rethrowIfPossible(cause);
670             throw new IllegalStateException(cause);
671         }
672     }
673 
674     static void putByte(long address, byte value) {
675         try {
676             UNSAFE_PUT_BYTE_WITH_LONG_HANDLE.invokeExact(address, value);
677         } catch (Throwable cause) {
678             rethrowIfPossible(cause);
679             throw new IllegalStateException(cause);
680         }
681     }
682 
683     static void putShort(long address, short value) {
684         try {
685             UNSAFE_PUT_SHORT_WITH_LONG_HANDLE.invokeExact(address, value);
686         } catch (Throwable cause) {
687             rethrowIfPossible(cause);
688             throw new IllegalStateException(cause);
689         }
690     }
691 
692     static void putInt(long address, int value) {
693         try {
694             UNSAFE_PUT_INT_WITH_LONG_HANDLE.invokeExact(address, value);
695         } catch (Throwable cause) {
696             rethrowIfPossible(cause);
697             throw new IllegalStateException(cause);
698         }
699     }
700 
701     static void putLong(long address, long value) {
702         try {
703             UNSAFE_PUT_LONG_WITH_LONG_HANDLE.invokeExact(address, value);
704         } catch (Throwable cause) {
705             rethrowIfPossible(cause);
706             throw new IllegalStateException(cause);
707         }
708     }
709 
710     static void putByte(Object o, long offset, byte value) {
711         try {
712             UNSAFE_PUT_BYTE_WITH_OBJECT_HANDLE.invokeExact(o, offset, value);
713         } catch (Throwable cause) {
714             rethrowIfPossible(cause);
715             throw new IllegalStateException(cause);
716         }
717     }
718 
719     static void putShort(Object o, long offset, short value) {
720         try {
721             UNSAFE_PUT_SHORT_WITH_OBJECT_HANDLE.invokeExact(o, offset, value);
722         } catch (Throwable cause) {
723             rethrowIfPossible(cause);
724             throw new IllegalStateException(cause);
725         }
726     }
727 
728     static void putInt(Object o, long offset, int value) {
729         try {
730             UNSAFE_PUT_INT_WITH_OBJECT_HANDLE.invokeExact(o, offset, value);
731         } catch (Throwable cause) {
732             rethrowIfPossible(cause);
733             throw new IllegalStateException(cause);
734         }
735     }
736 
737     static void putLong(Object o, long offset, long value) {
738         try {
739             UNSAFE_PUT_LONG_WITH_OBJECT_HANDLE.invokeExact(o, offset, value);
740         } catch (Throwable cause) {
741             rethrowIfPossible(cause);
742             throw new IllegalStateException(cause);
743         }
744     }
745 
746     static void putObject(Object o, long offset, Object value) {
747         try {
748             UNSAFE_PUT_OBJECT_WITH_OBJECT_HANDLE.invokeExact(o, offset, value);
749         } catch (Throwable cause) {
750             rethrowIfPossible(cause);
751             throw new IllegalStateException(cause);
752         }
753     }
754 
755     static void copyMemory(Object srcBase, long srcOffset,
756                            Object destBase, long destOffset,
757                            long bytes) {
758         try {
759             UNSAFE_COPY_MEMORY_WITH_OBJECT_HANDLE.invokeExact(srcBase, srcOffset, destBase, destOffset, bytes);
760         } catch (Throwable cause) {
761             rethrowIfPossible(cause);
762             throw new IllegalStateException(cause);
763         }
764     }
765 
766     static void copyMemory(long srcAddress, long destAddress, long bytes) {
767         try {
768             UNSAFE_COPY_MEMORY_WITH_LONG_HANDLE.invokeExact(srcAddress, destAddress, bytes);
769         } catch (Throwable cause) {
770             rethrowIfPossible(cause);
771             throw new IllegalStateException(cause);
772         }
773     }
774 
775     static void setMemory(long address, long bytes, byte value) {
776         try {
777             UNSAFE_SET_MEMORY_WITH_LONG_HANDLE.invokeExact(address, bytes, value);
778         } catch (Throwable cause) {
779             rethrowIfPossible(cause);
780             throw new IllegalStateException(cause);
781         }
782     }
783 
784     static void setMemory(Object o, long offset, long bytes, byte value) {
785         try {
786             UNSAFE_SET_MEMORY_WITH_OBJECT_HANDLE.invokeExact(o, offset, bytes, value);
787         } catch (Throwable cause) {
788             rethrowIfPossible(cause);
789             throw new IllegalStateException(cause);
790         }
791     }
792 
793     static int addressSize() {
794         try {
795             return (int) UNSAFE_ADDRESS_SIZE_HANDLE.invokeExact();
796         } catch (Throwable cause) {
797             rethrowIfPossible(cause);
798             throw new IllegalStateException(cause);
799         }
800     }
801 
802     static long allocateMemory(long size) {
803         try {
804             return (long) UNSAFE_ALLOCATE_MEMORY_HANDLE.invokeExact(size);
805         } catch (Throwable cause) {
806             rethrowIfPossible(cause);
807             throw new IllegalStateException(cause);
808         }
809     }
810 
811     static void freeMemory(long address) {
812         try {
813             UNSAFE_FREE_MEMORY_HANDLE.invokeExact(address);
814         } catch (Throwable cause) {
815             rethrowIfPossible(cause);
816             throw new IllegalStateException(cause);
817         }
818     }
819 
820     static long reallocateMemory(long address, long newSize) {
821         try {
822             return (long) UNSAFE_REALLOCATE_MEMORY_HANDLE.invokeExact(address, newSize);
823         } catch (Throwable cause) {
824             rethrowIfPossible(cause);
825             throw new IllegalStateException(cause);
826         }
827     }
828 
829     private static void rethrowIfPossible(Throwable cause) {
830         if (cause instanceof Error) {
831             throw (Error) cause;
832         }
833         if (cause instanceof RuntimeException) {
834             throw (RuntimeException) cause;
835         }
836     }
837 
838     static boolean isAvailabile() {
839         return UNSAFE_UNAVAILABILITY_CAUSE == null && UNSAFE != null;
840     }
841 
842     static Throwable unavailabilityCause() {
843         return UNSAFE_UNAVAILABILITY_CAUSE;
844     }
845 
846     private SunMiscUnsafeAccess() { }
847 }