Repository: netty/netty
Issue: Failure to run a service that uses a Netty library having built it with Graal native-image

### Expected behavior
Ability to build and run a service using Netty with GraalVM [native-image](https://www.graalvm.org/docs/reference-manual/native-image/).

### Actual behavior
It seems like Netty already has some support for GraalVM, but there still some issues. I can't find any reference to this error below, so maybe I'm doing something wrong! Any ideas would be greatly appreciated.

Failure during runtime:
```
com.oracle.svm.core.jdk.UnsupportedFeatureError: Unsupported method of Unsafe
	at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:101)
	at jdk.internal.misc.Unsafe.staticFieldOffset(Unsafe.java:230)
	at sun.misc.Unsafe.staticFieldOffset(Unsafe.java:662)
	at io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0$5.run(PlatformDependent0.java:281)
	at java.security.AccessController.doPrivileged(AccessController.java:81)
	at io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0.<clinit>(PlatformDependent0.java:266)
	at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350)
	at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)
	at java.lang.Class.ensureInitialized(DynamicHub.java:496)
	at io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent.isAndroid(PlatformDependent.java:272)
	at io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent.<clinit>(PlatformDependent.java:92)
	at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350)
	at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)
	at java.lang.Class.ensureInitialized(DynamicHub.java:496)
	at io.grpc.netty.shaded.io.netty.util.AsciiString.<init>(AsciiString.java:222)
	at io.grpc.netty.shaded.io.netty.util.AsciiString.<init>(AsciiString.java:209)
	at io.grpc.netty.shaded.io.netty.util.AsciiString.cached(AsciiString.java:1406)
	at io.grpc.netty.shaded.io.netty.util.AsciiString.<clinit>(AsciiString.java:47)
	at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350)
	at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)
	at java.lang.Class.ensureInitialized(DynamicHub.java:496)
	at io.grpc.netty.shaded.io.grpc.netty.Utils.<clinit>(Utils.java:72)
	at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350)
	at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)
	at java.lang.Class.ensureInitialized(DynamicHub.java:496)
	at io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder.<clinit>(NettyChannelBuilder.java:74)
	at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350)
	at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)
	at java.lang.Class.ensureInitialized(DynamicHub.java:496)
	at io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider.builderForAddress(NettyChannelProvider.java:37)
	at io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider.builderForAddress(NettyChannelProvider.java:23)
	at io.grpc.ManagedChannelBuilder.forAddress(ManagedChannelBuilder.java:39)
	at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.createSingleChannel(InstantiatingGrpcChannelProvider.java:269)
	at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.access$1500(InstantiatingGrpcChannelProvider.java:71)
	at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider$1.createSingleChannel(InstantiatingGrpcChannelProvider.java:202)
	at com.google.api.gax.grpc.ChannelPool.create(ChannelPool.java:72)
	at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.createChannel(InstantiatingGrpcChannelProvider.java:209)
	at com.google.api.gax.grpc.InstantiatingGrpcChannelProvider.getTransportChannel(InstantiatingGrpcChannelProvider.java:192)
	at com.google.api.gax.rpc.ClientContext.create(ClientContext.java:155)
	at com.google.cloud.bigtable.data.v2.stub.EnhancedBigtableStub.create(EnhancedBigtableStub.java:163)
	at com.google.cloud.bigtable.data.v2.BigtableDataClient.create(BigtableDataClient.java:152)
```

### Steps to reproduce
Try building a service that uses a library that uses Netty under the hood (in my particular example, `google-cloud-bigtable`).

### Minimal yet complete reproducer code (or URL to code)

### Netty version
The library uses a shaded version of Netty `grpc-netty-shaded`, but using the latest version `1.27.1`, which I'm pretty sure it's Netty 4.1.44+ (but I don't know how to verify).

### JVM version (e.g. `java -version`)
N/A

### OS version (e.g. `uname -a`)
macOS Catalina


Issue comments:
please ensure you use the latest netty release and re-open if you still see issues. 
Encountered the same error, also trying to use `grpc-netty-shaded`.
As a work-around: `-Dio.netty.noUnsafe=true` added at run time (that is, if your built application is called `app`, it is `./app -Dio.netty.noUnsafe=true`) seems to solve the problem.

Please note that it compleletely disables the usage of `Unsafe` by Netty. I'm not sure what other consequences it has (probably, some performance penalty).

Kudos to @johnou 

Pull request review comments:
@rpuch can we merge this directly to `PlatformDependent0` ? Seems overkill to add this class just for exposing one method etc 
Use `SystemPropertyUtils.`
I've moved the code to `PlatformDependent0` and removed `GraalDetector` class. Please let me know if it is ok now.
Done

Patch that resolved the issue:
diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java
index 44976f18cb8..d95aaa8cdb8 100644
--- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java
+++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java
@@ -53,6 +53,11 @@ final class PlatformDependent0 {
     private static final Object INTERNAL_UNSAFE;
     private static final boolean IS_EXPLICIT_TRY_REFLECTION_SET_ACCESSIBLE = explicitTryReflectionSetAccessible0();
 
+    // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/
+    // ImageInfo.java
+    private static final boolean RUNNING_IN_NATIVE_IMAGE = SystemPropertyUtil.contains(
+            "org.graalvm.nativeimage.imagecode");
+
     static final Unsafe UNSAFE;
 
     // constants borrowed from murmur3
@@ -283,7 +288,7 @@ public Object run() {
                         Class<?> bitsClass =
                                 Class.forName("java.nio.Bits", false, getSystemClassLoader());
                         int version = javaVersion();
-                        if (version >= 9) {
+                        if (unsafeStaticFieldOffsetSupported() && version >= 9) {
                             // Java9/10 use all lowercase and later versions all uppercase.
                             String fieldName = version >= 11 ? "UNALIGNED" : "unaligned";
                             // On Java9 and later we try to directly access the field as we can do this without
@@ -399,6 +404,10 @@ public Object run() {
                 DIRECT_BUFFER_CONSTRUCTOR != null ? "available" : "unavailable");
     }
 
+    private static boolean unsafeStaticFieldOffsetSupported() {
+        return !RUNNING_IN_NATIVE_IMAGE;
+    }
+
     static boolean isExplicitNoUnsafe() {
         return EXPLICIT_NO_UNSAFE_CAUSE != null;
     }


Test patch: