jpype icon indicating copy to clipboard operation
jpype copied to clipboard

JVM DLL not found. Apple m1

Open orakzai opened this issue 3 years ago • 26 comments

I'm using Jaydebeapi to connect to Hive but it throws the below error. I tried with both JDK 8 and 15.

OSError: [Errno 0] JVM DLL not found: /Library/Java/JavaVirtualMachines/zulu-15.jdk/Contents/Home/lib/libjli.dylib

I've added the JAVA_HOME in .zshrc file at the end:

#export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-8.jdk/Contents/Home
export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-15.jdk/Contents/Home
export PATH=${PATH}:$JAVA_HOME/bin

orakzai avatar Aug 06 '21 08:08 orakzai

Is there a lib directory under Home?

Thrameos avatar Aug 09 '21 20:08 Thrameos

yes there is a lib directory. Btw I installed oracle JDK 8, and the library is able to run without problem. I guess the issue is with the zulu JDK

orakzai avatar Aug 10 '21 07:08 orakzai

I had the same mistake. using mac m1 python3.9 JPype==1.3.0

Traceback (most recent call last): File "/Users/xxx/miniforge3/lib/python3.9/site-packages/jpype/_core.py", line 226, in startJVM _jpype.startup(jvmpath, tuple(args), OSError: [Errno 0] JVM DLL not found: /Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/jli/libjli.dylib

but i have libjli.dylib in this path.

vipzgy avatar Aug 11 '21 09:08 vipzgy

I also ran into this issue. Brand new Mac Mini with BigSur and M1 CPU.

Python version: Python 3.9.6 (default, Jun 28 2021, 19:24:41) [Clang 12.0.5 (clang-1205.0.22.9)] on darwin Type "help", "copyright", "credits" or "license" for more information

Java Version openjdk version "1.8.0_292" OpenJDK Runtime Environment (Zulu 8.54.0.21-CA-macosx) (build 1.8.0_292-b10) OpenJDK 64-Bit Server VM (Zulu 8.54.0.21-CA-macosx) (build 25.292-b10, mixed mode)

nils-tekampe avatar Aug 28 '21 18:08 nils-tekampe

What is the setting of JAVA_HOME and what is the contents of the JAVA_HOME directory?

Thrameos avatar Aug 30 '21 16:08 Thrameos

Here is some more information.

  1. I set the JAVA_HOME explicitely as follows export JAVA_HOME=/Users/nils/.sdkman/candidates/java/8.0.292-zulu
  2. this folder contains
➜  ~ ls /Users/nils/.sdkman/candidates/java/8.0.292-zulu
ASSEMBLY_EXCEPTION       THIRD_PARTY_README       include                  readme.txt               zulu-8.jdk
CLASSPATH_EXCEPTION_NOTE Welcome.html             jre                      release
DISCLAIMER               bin                      lib                      sample
LICENSE                  demo                     man                      src.zip
  1. I am using the following test.py for testing
from jpype import *
startJVM ("/Users/nils/.sdkman/candidates/java/8.0.292-zulu/jre/lib/server/libjvm.dylib")

Result as mentioned before:

Traceback (most recent call last):
  File "/Users/nils/test.py", line 2, in <module>
    startJVM ("/Users/nils/.sdkman/candidates/java/8.0.292-zulu/jre/lib/server/libjvm.dylib")
  File "/opt/homebrew/lib/python3.9/site-packages/jpype/_core.py", line 226, in startJVM
    _jpype.startup(jvmpath, tuple(args),
OSError: [Errno 0] JVM DLL not found: /Users/nils/.sdkman/candidates/java/8.0.292-zulu/jre/lib/server/libjvm.dylib

And finally:

➜  ~ ls /Users/nils/.sdkman/candidates/java/8.0.292-zulu/jre/lib/server/libjvm.dylib
/Users/nils/.sdkman/candidates/java/8.0.292-zulu/jre/lib/server/libjvm.dylib

nils-tekampe avatar Aug 30 '21 20:08 nils-tekampe

It seems like the error message is deceiving in this case as the file is clearly present. The issue is how to figure out what the real error is.

My first thought would be making sure that the library actually exists and is a valid binary. Next I would check to see if the file is tagged with the executable flag. If it is not then it likely would fail during the load. From there is gets a bit harder as we would have to alter the code and recompile.

The error message was issued from the code in native/common/jp_exception.cpp

                else if (m_Type == JPError::_os_error_unix)
                {
                        std::stringstream ss;
                        ss << "JVM DLL not found: " << mesg;
                        PyObject* val = Py_BuildValue("(iz)", m_Error.i,
                                        ss.str().c_str());
                        if (val != NULL)
                        {
                                PyObject* exc = PyObject_Call(PyExc_OSError, val, NULL);
                                Py_DECREF(val);
                                if (exc != NULL)
                                {
                                        PyErr_SetObject(PyExc_OSError, exc);
                                        Py_DECREF(exc);
                                }
                        }

But this is just the error handler. The actual source of the error is native/common/jp_platform.cpp


        virtual void loadLibrary(const char* path) override
        {
                JP_TRACE_IN("LinuxPlatformAdapter::loadLibrary");
#if defined(_HPUX) && !defined(_IA64)
                JP_TRACE("shl_load", path);
                jvmLibrary = shl_load(path, BIND_DEFERRED | BIND_VERBOSE, 0L);
#else
                JP_TRACE("dlopen", path);
                jvmLibrary = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
#endif // HPUX
                // GCOVR_EXCL_START
                if (jvmLibrary == NULL)
                {
                        JP_TRACE("null library");
                        JP_TRACE("errno", errno);
                        if (errno == ENOEXEC)
                        {
                                JP_TRACE("dignostics", dlerror());
                        }
                        JP_RAISE_OS_ERROR_UNIX( errno, path);
                }
                // GCOVR_EXCL_STOP
                JP_TRACE_OUT; // GCOVR_EXCL_LINE
        }

The key part is the error number. We are currently getting as 0, which is not a valid value as far as I am aware. So the reason for the fail is dlopen returned NULL but did not set an errno. So to make headway we would need to find some way to print additional diagnostics as to why dlopen did not work. I know of no reason for a NULL without an errno.

Thrameos avatar Aug 30 '21 21:08 Thrameos

An explicit chmod+x on the library did not change anything. Is there anything else that I can do to support debugging this?

nils-tekampe avatar Aug 31 '21 07:08 nils-tekampe

My best guess is that it is related to this ticket. Unfortunately I don't have access to such a system so I can't really test to see if this is a solution.

https://support.azul.com/hc/en-us/articles/360039650212-On-MacOS-10-15-3-and-later-Azul-Zulu-Builds-of-OpenJDK-Java-doesn-t-load-the-JNI-Libraries-my-Application-needs

Thrameos avatar Aug 31 '21 16:08 Thrameos

Reading some of the documentation on "notarized" shared libraries it seems very likely this is the issue. I can't find any technical documentation on how the model operates that would give me a way to catch and resolve this error from within JPype.

The apparent issue is that Python and JPype are installed at one protection level and Java was installed at another and thus calls to dlopen will fail. The solution is to have all at the same privilege level (likely by using a copy of Java outside of /Library). Why the error is silent preventing us from issuing a proper error is baffling. Without some error message that I can propagate to user there is no way this will not create endless headaches and support tickets. At best I could check if the file exists and is executable and if it still fails change the error message to something that indicates an unspecified permission error.

There is nothing about this in the online man pages that I can access. https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlopen.3.html

Other python libraries have similar issues in that if one piece is not notarized then the process fails.
https://stackoverflow.com/questions/67834070/error-loading-python-lib-after-notarizing-macos-application

Thrameos avatar Aug 31 '21 16:08 Thrameos

@Thrameos I am not sure that this is the case here. In my Installation the Java is installed into the home directory. The “system Java” that resides in Library should not be involved at all. Or am I mistaken?

nils-tekampe avatar Aug 31 '21 18:08 nils-tekampe

I think it has something to do with which applications on the system are signed and located in trusted paths. If Python and JPype were signed but Java was not then it would fail. The same is true in the reverse case. Unfortunately I don't know enough about the process to determine which is signed and which are not, nor do I have technical specs on how to make sure this is even the right solution. I am afraid we would need an apple expert to weigh in.

Here is the only example I found thus far.

https://docs.azul.com/prime/Native-Application

Is there a libjvm.so file as well as the dylib file? Maybe we are trying to load the wrong file? If so manually give a path to startJVM with the so file and see if that fixes it.

The next most likely problem is that the LD_LIBRARY_PATH is missing something the shared library requires. I remember a case in which I had to add the $JAVA_HOME/bin to LD_LIBRARY_PATH because some shared library was in bin for unknown reason. There should be a utility that scans a shared library and tells if all the shared library dependencies can be satisfied.

Also from that doc it appears there is a function called dlerror() perhaps printing that value (you would need to recompile JPype) to see if it can tell us what is going on. I would just add printf("%s\n", dlerror()); after JP_TRACE("errno", errno); to see if we can get some more information.

Thrameos avatar Aug 31 '21 20:08 Thrameos

@Thrameos I made added the printf statements and compiled jpype locally. The printed messages are:

dlopen(/Users/nils/.sdkman/candidates/java/8.0.292-zulu/jre/lib/server/libjvm.dylib, 9): no suitable image found.  Did find:
	/Users/nils/.sdkman/candidates/java/8.0.292-zulu/jre/lib/server/libjvm.dylib: mach-o, but wrong architecture
	/Users/nils/.sdkman/candidates/java/8.0.292-zulu/zulu-8.jdk/Contents/Home/jre/lib/server/libjvm.dylib: mach-o, but wrong architecture

I guess that the problem is that the JDK that is used here, is not native for M1. When the java excutable is called directly, I guess that the Rosetta framework kicks in but this does not seem to be the case here. I will try to find a JDK that has been compiled for M1 natively next.

nils-tekampe avatar Sep 06 '21 10:09 nils-tekampe

Okay it sounds like the issue is identified. Python and the JVM must be the same architecture. I can try to improve the error message though it won't fix the underlaying issue.

Thrameos avatar Sep 07 '21 16:09 Thrameos

Making the error message more precise may help others. I guess that it would have saved me 3 hours of debugging.

nils-tekampe avatar Sep 07 '21 16:09 nils-tekampe

I ran into this problem and I managed to fix it by installing an M1-native JVM (azul OpenJDK, .dmg version), following the instructions from this thread: https://stackoverflow.com/questions/64788005/java-jdk-for-apple-m1-chip/64881417#64881417

capelastegui avatar Sep 27 '21 14:09 capelastegui

Hi, i tried to download all versions of the jdk (the arm ones), it does not seem to work... But i tried to download the x86 one and it magically did work. Maybe you should try both the arm and the x86 versions

TudorEsan avatar Mar 21 '22 06:03 TudorEsan

The architecture of python and the jdk must match. So if python is x86 then the jdk must be as well.

Thrameos avatar Mar 21 '22 16:03 Thrameos

The architecture of python and the jdk must match. So if python is x86 then the jdk must be as well.

That makes perfect sense! Thank you

TudorEsan avatar Mar 21 '22 18:03 TudorEsan

I got the same error. I changed the java that is used in /usr/bin to my newly-installed Zulu11 java: /Applications/zulu11.56.19-ca-jdk11.0.15-macosx_aarch64/bin/java Then it works for me. Basically, in the ~/.bash_profile or ~/.zshrc, you add export PATH="/Applications/zulu11.56.19-ca-jdk11.0.15-macosx_aarch64/bin:$PATH" I think this is caused by Jaydebeapi uses some particular java version (zulu proves java11) while the /usr/bin uses different java version

yanxg avatar May 11 '22 00:05 yanxg

The following solution worked for me: (based on: https://stackoverflow.com/questions/64788005/java-jdk-for-the-apple-m1-chip/64881417#64881417)

Use sdkmanager. vi ~/.sdkman/etc/config
sdkman_rosetta2_compatible=false should be already present in the file, make sure it is set to false. List available jdks: sdk list java Install the desired JDK, i.e: sdk install java 8.0.292-zulu Make sure your $JAVA_HOME is updated.

arikg10 avatar Jun 07 '22 20:06 arikg10

I solved this problem by

  1. Download the ARM64 jdk https://www.azul.com/downloads/?version=java-11-lts&os=macos&architecture=arm-64-bit&package=jdk
  2. untar it to /Applications/
  3. Make sure libel.dylib exists in /Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home/lib/jli/libjli.dylib (note that it in lib/jli/)
  4. As pointed out by @Thrameos that you need to make sure the Python is ARM version instead of Intel version

yanxg avatar Jul 13 '22 03:07 yanxg

None of the above solutions worked for me, but this did:

https://pyspi-toolkit.readthedocs.io/en/latest/faq.html

The AdoptOpenJDK appears to be the ticket for the M1 and M2 chips, for a Python-compatible library.

Ackbach avatar Jan 26 '23 23:01 Ackbach

What worked for me recently is using the ARM64 JDK as suggested in https://stackoverflow.com/a/74398849 I.e. install https://adoptium.net/temurin/releases/?version=11 for MacOS Arm64 and make sure JAVA_HOME is set properly. After then the JPype started successfully :v:

johnygomez avatar Feb 18 '23 14:02 johnygomez

就是版本的问题,之前安装的是官网下载的“jdk-8u371-macosx-x64.dmg”的x86版本,也有类似的问题, 在Azul 下载了arm版本的,就能正常使用了https://adoptium.net/zh-CN/temurin/releases/?version=11&os=mac&arch=arm

dulinanaaa avatar Oct 01 '23 04:10 dulinanaaa

I solved this problem by

  1. Download the ARM64 jdk https://www.azul.com/downloads/?version=java-11-lts&os=macos&architecture=arm-64-bit&package=jdk
  2. untar it to /Applications/
  3. Make sure libel.dylib exists in /Library/Java/JavaVirtualMachines/zulu-11.jdk/Contents/Home/lib/jli/libjli.dylib (note that it in lib/jli/)
  4. As pointed out by @Thrameos that you need to make sure the Python is ARM version instead of Intel version

This above solution worked for me.

Suleman-Tariq avatar Feb 26 '24 12:02 Suleman-Tariq