Adding new paths for native libraries at runtime in Java (part 2)
Part 1 of this article is available here.
Since Java 14, ClassLoader was changed a little bit and usr_paths it not available as field anymore. It's still possible to change the java.library.path at runtime, but it's still tricky and dirty:
Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
getDeclaredFields0.setAccessible(true);
Field[] fieldsClassLoader =
(Field[])getDeclaredFields0.invoke(ClassLoader.class, Boolean.FALSE);
for (Field fldClassLoader : fieldsClassLoader) {
if ("libraries".equals(fldClassLoader.getName())) {
fldClassLoader.setAccessible(true);
Class<?>[] classesClassLoader =
fldClassLoader.getType().getDeclaredClasses();
for (Class<?> clLibraryPaths : classesClassLoader) {
if ("jdk.internal.loader.NativeLibraries$LibraryPaths".equals(
clLibraryPaths.getName())) {
Field[] fieldsLibraryPaths =
(Field[])getDeclaredFields0.invoke(clLibraryPaths, Boolean.FALSE);
for (Field fldLibPath : fieldsLibraryPaths) {
if ("USER_PATHS".equals(fldLibPath.getName())) {
final Field fldUsrPaths = fldLibPath;
fldUsrPaths.setAccessible(true);
// get array of paths
final String[] saPath = (String[])fldUsrPaths.get(null);
// check if the path to add is already present
for (String path : saPath) {
if (path.equals(pPath)) {
return;
}
}
// add the new path
String[] saNewPaths = Arrays.copyOf(saPath, saPath.length + 1);
saNewPaths[saNewPaths.length - 1] = pPath;
Object unsafe;
// Unsafe is a hack
Class<?> clsUnsafe = Class.forName("sun.misc.Unsafe");
final Field unsafeField = clsUnsafe.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = unsafeField.get(null);
Method m1 = clsUnsafe.getMethod("staticFieldBase", Field.class);
Method m2 = clsUnsafe.getMethod("staticFieldOffset", Field.class);
Object fieldBase = m1.invoke(unsafe, fldUsrPaths);
Long fieldOffset = (Long)m2.invoke(unsafe, fldUsrPaths);
Method m3 = clsUnsafe.getMethod("putObject", Object.class,
long.class, Object.class);
m3.invoke(unsafe, fieldBase, fieldOffset, saNewPaths);
}
}
}
}
return;
}
}
Here's the complete solution: toPDF project (search for addLibraryPath(String pPath))
Above code requires:
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
for JDK > 14.