Load bundled HDF core libraries before the JNI wrappers initialize - #480
Load bundled HDF core libraries before the JNI wrappers initialize#480mattjala wants to merge 4 commits into
Conversation
3a3312c to
04dc9a8
Compare
|
Before getting into some fairly crazy platform-specific workarounds here (to be honest, At least on Linux and MacOS, there shouldn't be an issue with allowing the JVM to try loading a shared HDF5 library outside the bundled ones in the first place, as .so/.dylib versioning should take care of the details and prevent loading an HDF5 library that is incompatible. In theory, we should be able to do the same thing on Windows as well, but I'm not familiar with the details of DLL versioning there. I verified the above by forcing HDFView to load HDF5 2.2.0 ( ldd libhdf5_java.so
ldd: warning: you do not have execution permission for `./libhdf5_java.so'
linux-vdso.so.1 (0x00007f0daa8bf000)
libhdf5.so.320 => ~/Downloads/HDFView/lib/app/libhdf5.so.320 (0x00007f0daa200000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0da9e00000)
libm.so.6 => /lib64/libm.so.6 (0x00007f0daa104000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0daa8c1000)
LD_LIBRARY_PATH=/opt/hdf5_2_2_0/lib64/ ./HDFView
lsof -p 23199
...
HDFView 23199 jhenderson mem REG 8,4 6021896 9961533 /opt/hdf5_2_2_0/lib64/libhdf5.so.320.2.0 |
HDFView sometimes fails to start when another HDF5 or HDF4 installation is visible to the operating system's dynamic loader. The workaround so far has been to require users to hand-edit their PATH to avoid detection of the pre-existing libraries. The cause is that two native libraries load by two different mechanisms. The JNI wrapper (hdf5_java) is loaded by the JVM and honors java.library.path, so our packaging already pins it correctly. But the wrapper lists the core library (libhdf5.so.320 on Linux, hdf5.dll on Windows) as a NEEDED dependency, and that one is resolved by the OS loader rather than the JVM. On Linux the loader checks LD_LIBRARY_PATH before the RUNPATH we ship, and on Windows the search order includes PATH. Either way a foreign copy can be detected first. This change loads the bundled core libraries by absolute path before anything touches the wrapper. Once a library is mapped into the process, the loader satisfies the wrapper's dependency from the already-mapped copy, matching on soname on Linux and on module base name on Windows, and never searches PATH or LD_LIBRARY_PATH at all. NativeLibraryLoader looks for each library in -Dhdfview.nativedir, then -Dhdfview.root (jpackage already sets this to $APPDIR), then the directory named by -Dhdf.hdf5lib.H5.hdf5lib, then each entry of java.library.path. The call sits at the top of FileFormat's static initializer, which is the earliest point the wrapper can initialize.
The loading logic is now addressed per platform where the bundle is assembled, and HDFView itself carries no library-loading logic. Linux: The new pin-rpath.py script refiles the HDF binaries' existing search path under DT_RPATH instead of DT_RUNPATH, so its consulted before LD_LIBRARY_PATH. Windows: The core DLLs move beside HDFView.exe, whose directory is searched ahead of both the system folder and PATH. macOS: No specific extra work needed, and the bundled libraries are already found first. PATH and LD_LIBRARY_PATH should no longer redirect a packaged HDFView to a different HDF build.
88b1fd9 to
4120171
Compare
| <!-- target only separates them out when this profile is also passing them --> | ||
| <!-- to jpackage. --> | ||
| <jpackage.corelib.include>*.dll</jpackage.corelib.include> | ||
| <jpackage.corelib.exclude>*_java.dll</jpackage.corelib.exclude> |
There was a problem hiding this comment.
I don't think there's anything particularly wrong with this approach, but it would probably be simpler for this workflow's logic to just put everything next to the .exe and adjust java.library.path
HDFView sometimes fails to start when another HDF5 or HDF4 installation is visible to the operating system's dynamic loader. The workaround so far has been to require users to hand-edit their PATH to avoid detection of the pre-existing libraries.
The cause is that two native libraries load by two different mechanisms. The JNI wrapper (hdf5_java) is loaded by the JVM and honors java.library.path, so our packaging already pins it correctly. But the wrapper lists the core library (libhdf5.so.320 on Linux, hdf5.dll on Windows) as a NEEDED dependency, and that one is resolved by the OS loader rather than the JVM. On Linux the loader checks LD_LIBRARY_PATH before the RUNPATH we ship, and on Windows the search order includes PATH. Either way a foreign copy can be detected first.
This change loads the bundled core libraries by absolute path before anything touches the wrapper. Once a library is mapped into the process, the loader satisfies the wrapper's dependency from the already-mapped copy, matching on soname on Linux and on module base name on Windows, and never searches PATH or LD_LIBRARY_PATH at all.
NativeLibraryLoader looks for each library in -Dhdfview.nativedir, then -Dhdfview.root (jpackage already sets this to $APPDIR), then the directory named by -Dhdf.hdf5lib.H5.hdf5lib, then each entry of java.library.path. The call sits at the top of FileFormat's static initializer, which is the earliest point the wrapper can initialize.
Resolves #459