Graal: MacOS native image calling JNI code: passed in argument to JNI call always passed as 0

Created on 13 Feb 2020  路  11Comments  路  Source: oracle/graal

Describe GraalVM and your environment :

  • GraalVM version or commit id if built from source: 19.3.1, 20.1.0-dev
  • CE or EE: CE
  • Build Time or run time failure: run-time
  • JDK version: JDK8 and JDK11
  • Native compiler information:
    Run the following to capture compiler version

    • In windows: cl.exe

    • In macOS : cc -v

    • In Linux: gcc --version


Apple LLVM version 10.0.0 (clang-1000.11.45.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
InstalledDir: /Applications/Xcode-10.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
  • Native linker information:
    Run the following to capture linker version

    • In windows: cl.exe

    • In macOS : cc -Wl,-v

    • In Linux: gcc -Wl,--version


@(#)PROGRAM:ld  PROJECT:ld64-302.3
configured to support archs: armv6 armv7 armv7s arm64 i386 x86_64 x86_64h armv6m armv7k armv7m armv7em (tvOS)
Library search paths:
    /usr/lib
    /usr/local/lib
Framework search paths:
    /Library/Frameworks/
    /System/Library/Frameworks/
Undefined symbols for architecture x86_64:
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
  • OS and OS Version: Mac OS X 10.13.6 17G65 (Circle CI mac build node)
  • Architecture: AMD64
  • The output of java -Xinternalversion:
Java HotSpot(TM) 64-Bit Server VM (25.181-b13) for bsd-amd64 JRE (1.8.0_181-b13), built on Jul  7 2018 01:02:31 by "java_re" with gcc 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)

Have you verified this issue still happens when using the latest snapshot?
Yes. Happens on all MacOS native-images up to and including 20.1.0-dev

Describe the issue
JNI code writing to Unix domain sockets, when called from a graal native-image on MacOS, always writes 0 bytes.
JNI call on native-image on MacOS gets argument passed always as 0 when it shouldn't be:

JNIEXPORT jint JNICALL Java_SocketTest_unix_1socket_1write
  (JNIEnv *env, jclass this, jint fd, jbyteArray buf, jint count)

The final jint argument count is always passed a 0. (note: the first jint arg fd is passed correctly)

On MacOS the code works when running on the JVM, but fails when run as a native-image.
The code works on Linux, on both the JVM and as a native image.

Describe the full native-image command

Capture full native-image command by running with the --verbose flag e.g.:

bin/native-image \
        -jar SocketTest.jar \
        -H:Name=sockettest \
        -H:+ReportExceptionStackTraces \
        -H:ConfigurationFileDirectories=config-dir \
        --initialize-at-build-time \
        --verbose \
        --no-fallback \
        --no-server \
        "-J-Xmx1g"

javac src/SocketTest.java
cd src && jar cfm ../SocketTest.jar manifest.txt SocketTest.class
cd src && javah -o SocketTest.h -cp ./ SocketTest
cc -I/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/include -I/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/include/darwin -I. -dynamiclib -undefined suppress -flat_namespace src/SocketTest.c -o libSocketTest.dylib -fPIC
/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/bin/native-image \
        -jar SocketTest.jar \
        -H:Name=sockettest \
        -H:+ReportExceptionStackTraces \
        -H:ConfigurationFileDirectories=config-dir \
        --initialize-at-build-time \
        --verbose \
        --no-fallback \
        --no-server \
        "-J-Xmx1g"
Executing [
/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/bin/java \
-XX:+UnlockExperimentalVMOptions \
-XX:+EnableJVMCI \
-Dtruffle.TrustAllTruffleRuntimeProviders=true \
-Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime \
-Dgraalvm.ForcePolyglotInvalid=true \
-Dgraalvm.locatorDisabled=true \
-d64 \
-XX:-UseJVMCIClassLoader \
-XX:+UseJVMCINativeLibrary \
-Xss10m \
-Xms1g \
-Xmx6871947672 \
-Duser.country=US \
-Duser.language=en \
-Dorg.graalvm.version=19.3.1 \
-Dorg.graalvm.config=CE \
-Dcom.oracle.graalvm.isaot=true \
-Djvmci.class.path.append=/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/graal.jar \
-javaagent:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/svm.jar \
-Djdk.internal.lambda.disableEagerInitialization=true \
-Djdk.internal.lambda.eagerlyInitialize=false \
-Djava.lang.invoke.InnerClassLambdaMetafactory.initializeLambdas=false \
-Xmx1g \
-Xbootclasspath/a:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/boot/graal-sdk.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/boot/graaljs-scriptengine.jar \
-cp \
/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/graal-llvm.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/javacpp-shadowed.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/llvm-platform-specific-shadowed.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/llvm-wrapper-shadowed.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/objectfile.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/pointsto.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/svm-llvm.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/svm.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/graal-management.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/graal.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/jvmci-api.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/jvmci-hotspot.jar \
com.oracle.svm.hosted.NativeImageGeneratorRunner \
-imagecp \
/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/boot/graal-sdk.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/boot/graaljs-scriptengine.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/graal-llvm.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/javacpp-shadowed.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/llvm-platform-specific-shadowed.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/llvm-wrapper-shadowed.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/objectfile.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/pointsto.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/svm-llvm.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/builder/svm.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/graal-management.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/graal.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/jvmci-api.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/jvmci/jvmci-hotspot.jar:/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/library-support.jar:/Users/distiller/project/SocketTest.jar \
-H:Path=/Users/distiller/project \
-H:Class=SocketTest \
-H:+ReportExceptionStackTraces \
-H:ConfigurationFileDirectories=config-dir \
-H:ClassInitialization=:build_time \
-H:FallbackThreshold=0 \
-H:CLibraryPath=/Users/distiller/graalvm-ce-java8-19.3.1/Contents/Home/jre/lib/svm/clibraries/darwin-amd64 \
-H:Name=sockettest
]
[sockettest:658]    classlist:   2,609.97 ms
[sockettest:658]        (cap):   2,352.17 ms
[sockettest:658]        setup:   4,091.46 ms
[sockettest:658]   (typeflow):   4,852.49 ms
[sockettest:658]    (objects):   4,295.98 ms
[sockettest:658]   (features):     218.70 ms
[sockettest:658]     analysis:   9,509.99 ms
[sockettest:658]     (clinit):     145.00 ms
[sockettest:658]     universe:     467.81 ms
[sockettest:658]      (parse):   1,066.00 ms
[sockettest:658]     (inline):   1,890.51 ms
[sockettest:658]    (compile):   6,216.90 ms
[sockettest:658]      compile:   9,636.61 ms
[sockettest:658]        image:     727.41 ms
[sockettest:658]        write:     244.79 ms
[sockettest:658]      [total]:  27,775.94 ms
rm -f socket; \
    nc -l -U socket & \
    sleep 5; \
    LD_LIBRARY_PATH=./ ./sockettest
Hello world; this is C talking!
opened fd: 3
writing to fd...
bytes written (should be 14): 0
closed fd: 3
Test FAILED!

Code snippet or code repository that reproduces the issue
I've setup a repo reducing the expression of the problem to the barest minimum.

https://github.com/epiccastle/graal-jni-unix-socket-test

Steps to reproduce the issue
Please include both build steps as well as run steps

  1. git clone [email protected]:epiccastle/graal-jni-unix-socket-test.git
  2. cd graal-jni-unix-socket-test
  3. make run-native-test GRAALVM=$GRAALVM_HOME

Expected behavior
The test should pass. 14 bytes should be written to the socket. Instead 0 bytes are written.

Additional context
Add any other context about the problem here. Specially important are stack traces or log output. Feel free to link to gists or to screenshots if necesary

Details

CI builds of this test code on both JVM and native image on all the graal versions and linux, too:
https://circleci.com/gh/epiccastle/graal-jni-unix-socket-test/tree/master

bug native-image

All 11 comments

You should determine whether the native method gets the correct value for count. If write returns 0 then the problem isn't on the GraalVM side, it's something to do with the socket configuration on Mac OS X. If count is coming in as 0, then there may be some problem in the JNI code that only affects Mac OS X.

@dmlloyd It works correctly unaltered running on the JVM on MacOS.

@dmlloyd you got it! Added debug in JNI code to print out count:

rm -f socket; \
    nc -l -U socket & \
    sleep 5; \
    LD_LIBRARY_PATH=./ ./sockettest
Hello world; this is C talking!
opened fd: 3
writing to fd...
Java_SocketTest_unix_1socket_1write writing 0 bytes
bytes written (should be 14): 0
closed fd: 3
Test FAILED!

Will rename ticket.

I guess this parameter should be appearing in R8. But since (I think?) Darwin and Linux are using the same calling convention, if it worked on Linux, it should work on Darwin too...?

What happens if you put the count parameter before the buf parameter?

Putting the count parameter before the buf works: https://circleci.com/workflow-run/3a8309b3-6f59-4017-a4de-c979d595fc90
So perhaps something about the byte[]array preceding the jint on macos stuffs it up?

Thank you for your report @retrogradeorbit ! I have looked into this and I believe the problem is not with the calling convention. Instead, in GetByteArrayElements, we treat the jboolean *isCopy parameter as a jint*. The compiler only allocates a single byte for your jboolean copy local variable on the stack in my tests, so that will overwrite adjacent bytes of the last parameter, in your case count. On Linux, the copy variable is probably allocated more space or placed somewhere else so it doesn't cause a problem here. I'll fix this.

@peter-hofer Ah that is interesting. I was playing around with other ways of passing the parameters and experiencing all kinds of strangeness, including on linux. And in every case I was using GetByteArrayElements. I will test when your patch lands in the dev builds.

Great! I thought it might be something about parameter type handling. I'm glad that the solution was found!

Fixed in 4653525c0f1b135c51aef01c03080dfb961aba91, thanks @retrogradeorbit , @dmlloyd and @olpaw !

Was this page helpful?
0 / 5 - 0 ratings