Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
retrogradeorbit opened this issue Feb 13, 2020 · 11 comments
Assignees

Comments

@retrogradeorbit
Copy link

retrogradeorbit commented Feb 13, 2020

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 git@github.com: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
@dmlloyd
Copy link
Contributor

dmlloyd commented Feb 13, 2020

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.

@retrogradeorbit
Copy link
Author

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

@retrogradeorbit
Copy link
Author

@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.

@retrogradeorbit retrogradeorbit changed the title MacOS native image calling JNI code: write() to a unix domain socket always writes 0 bytes. MacOS native image calling JNI code: passed in argument to JNI call always passed as 0 Feb 13, 2020
@dmlloyd
Copy link
Contributor

dmlloyd commented Feb 13, 2020

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...?

@dmlloyd
Copy link
Contributor

dmlloyd commented Feb 13, 2020

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

@retrogradeorbit
Copy link
Author

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?

@retrogradeorbit
Copy link
Author

updated https://github.com/epiccastle/graal-jni-unix-socket-test with new information

@peter-hofer peter-hofer self-assigned this Feb 14, 2020
@peter-hofer
Copy link
Member

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.

@retrogradeorbit
Copy link
Author

@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.

@dmlloyd
Copy link
Contributor

dmlloyd commented Feb 14, 2020

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

@peter-hofer
Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants