I’ve been trying to build some Java projects on CentOS 6.9 (Red Hat kernel version 2.6.32-696) using both Maven and Gradle, with both Oracle JDK 8 and OpenJDK 8, and all combinations fail with issues which I suspect are related to trying to use a newer glibc on such an old kernel.
The following program comprises a minimal test case which fails with both JDK versions:
import java.io.IOException;
import java.net.ServerSocket;
public class Main {
public static void main(String[] args){
try {
ServerSocket socket = new ServerSocket(32751);
socket.accept();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
With OracleJDK, I get the following error message:
library initialization failed - unable to allocate file descriptor table - out of memoryAborted (core dumped)
With OpenJDK, I get the following error:
java.net.SocketException: Socket closed
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at Main.main(Main.java:8)
Running strace against each of these shows a call to “prlimit64” which fails with ENOSYS. Using the system-wide JDK (OracleJDK 1.8.0_111), which runs the program successfully, I see a call to “getrlimit”, which succeeds.
How should I proceed from here?
I’m using nix 2.0 (nixUnstable from channel 17.093238.e984f9e48e1), with each Java version from channel 18.03.132275.2352d46904d. Please let me know if any other information is required.
Short answer: Yes, and we have machines running newer versions of CentOS, so this is the right long term solution to the problem.
Longer answer: I would like to explore introducing nix at work. A prerequisite to this is being able to use a nix-versioned JDK on our CI/CD machine, which, unfortunately, hosts far more than it should (CI/CD, artifact storage, and version control) and runs on Centos 6.9. I was hoping for a solution which would allow me to get nix working for our use case without having to bother anyone else, so I can convince people of its value by demonstration.
After posting this, I found that one can use the LD_PRELOAD environment variable to point to a shared library that can intercept system calls. Using that to point to a shared library compiled from the following worked for me:
Yes. I’m getting the outcome I hoped for (both JDK versions work as expected), but I’m still not entirely sure why - if LD_PRELOAD and strace work the way I think, I would expect either that the JDKs would work, and strace would show the syscalls supported by the kernel (getrlimit/setrlimit) being called, or that the JDKs would fail in the ways I saw previously, and strace would show the unsupported syscall (prlimit64). I don’t understand how the program is working while still showing the nonexistent syscall being called (and failing with ENOSYS).
Perhaps being impure and specifying the system glibc, or packaging the system glibc in nix (either from source or as a binary) is the better option. Who knows what other operations fail to work on a kernel this old? And the particular version of glibc I’m using explicitly states that it requires a Linux kernel of 3.2 or newer, so I’m not terribly surprised it doesn’t “just work” on CentOS 6.9.
RHEL / CentOS are special, because Red Hat backports lots of features to the old kernels. The link I’ve posted contains lots of details about what I’ve checked…