Try to monkey-patch library id when shading is used and we are on Mac… (#8210)
* Try to monkey-patch library id when shading is used and we are on MacOS / OSX.
Motivation:
ea4c315b45
did ensure we support using multiple versions of the same shaded native library but the user still needed to run install_name_tool -id on MacOS to ensure the ID is unique.
This is kind of error prone and also means that the shading itself would need to be done on MacOS / OSX.
This is related to https://github.com/netty/netty/issues/7272.
Modifications:
- Monkey patch the shaded native lib on MacOS to ensure the id is unique while unpacking it to the tempory location.
Result:
Easier way of using shaded native libs in netty.
This commit is contained in:
parent
bbb6e126b1
commit
2bb9f64e16
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.util.internal;
|
package io.netty.util.internal;
|
||||||
|
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
@ -47,6 +48,11 @@ public final class NativeLibraryLoader {
|
|||||||
private static final String NATIVE_RESOURCE_HOME = "META-INF/native/";
|
private static final String NATIVE_RESOURCE_HOME = "META-INF/native/";
|
||||||
private static final File WORKDIR;
|
private static final File WORKDIR;
|
||||||
private static final boolean DELETE_NATIVE_LIB_AFTER_LOADING;
|
private static final boolean DELETE_NATIVE_LIB_AFTER_LOADING;
|
||||||
|
private static final boolean TRY_TO_PATCH_SHADED_ID;
|
||||||
|
|
||||||
|
// Just use a-Z and numbers as valid ID bytes.
|
||||||
|
private static final byte[] UNIQUE_ID_BYTES =
|
||||||
|
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(CharsetUtil.US_ASCII);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
String workdir = SystemPropertyUtil.get("io.netty.native.workdir");
|
String workdir = SystemPropertyUtil.get("io.netty.native.workdir");
|
||||||
@ -69,6 +75,11 @@ public final class NativeLibraryLoader {
|
|||||||
|
|
||||||
DELETE_NATIVE_LIB_AFTER_LOADING = SystemPropertyUtil.getBoolean(
|
DELETE_NATIVE_LIB_AFTER_LOADING = SystemPropertyUtil.getBoolean(
|
||||||
"io.netty.native.deleteLibAfterLoading", true);
|
"io.netty.native.deleteLibAfterLoading", true);
|
||||||
|
logger.debug("-Dio.netty.native.deleteLibAfterLoading: {}", DELETE_NATIVE_LIB_AFTER_LOADING);
|
||||||
|
|
||||||
|
TRY_TO_PATCH_SHADED_ID = SystemPropertyUtil.getBoolean(
|
||||||
|
"io.netty.native.tryPatchShadedId", true);
|
||||||
|
logger.debug("-Dio.netty.native.tryPatchShadedId: {}", TRY_TO_PATCH_SHADED_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,7 +128,8 @@ public final class NativeLibraryLoader {
|
|||||||
*/
|
*/
|
||||||
public static void load(String originalName, ClassLoader loader) {
|
public static void load(String originalName, ClassLoader loader) {
|
||||||
// Adjust expected name to support shading of native libraries.
|
// Adjust expected name to support shading of native libraries.
|
||||||
String name = calculatePackagePrefix().replace('.', '_') + originalName;
|
String packagePrefix = calculatePackagePrefix().replace('.', '_');
|
||||||
|
String name = packagePrefix + originalName;
|
||||||
List<Throwable> suppressed = new ArrayList<Throwable>();
|
List<Throwable> suppressed = new ArrayList<Throwable>();
|
||||||
try {
|
try {
|
||||||
// first try to load from java.library.path
|
// first try to load from java.library.path
|
||||||
@ -174,16 +186,32 @@ public final class NativeLibraryLoader {
|
|||||||
|
|
||||||
byte[] buffer = new byte[8192];
|
byte[] buffer = new byte[8192];
|
||||||
int length;
|
int length;
|
||||||
|
if (TRY_TO_PATCH_SHADED_ID && PlatformDependent.isOsx() && !packagePrefix.isEmpty()) {
|
||||||
|
// We read the whole native lib into memory to make it easier to monkey-patch the id.
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(in.available());
|
||||||
|
|
||||||
|
while ((length = in.read(buffer)) > 0) {
|
||||||
|
byteArrayOutputStream.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
byteArrayOutputStream.flush();
|
||||||
|
byte[] bytes = byteArrayOutputStream.toByteArray();
|
||||||
|
byteArrayOutputStream.close();
|
||||||
|
|
||||||
|
// Try to patch the library id.
|
||||||
|
patchShadedLibraryId(bytes, originalName, name);
|
||||||
|
|
||||||
|
out.write(bytes);
|
||||||
|
} else {
|
||||||
while ((length = in.read(buffer)) > 0) {
|
while ((length = in.read(buffer)) > 0) {
|
||||||
out.write(buffer, 0, length);
|
out.write(buffer, 0, length);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
out.flush();
|
out.flush();
|
||||||
|
|
||||||
// Close the output stream before loading the unpacked library,
|
// Close the output stream before loading the unpacked library,
|
||||||
// because otherwise Windows will refuse to load it when it's in use by other process.
|
// because otherwise Windows will refuse to load it when it's in use by other process.
|
||||||
closeQuietly(out);
|
closeQuietly(out);
|
||||||
out = null;
|
out = null;
|
||||||
|
|
||||||
loadLibrary(loader, tmpFile.getPath(), true);
|
loadLibrary(loader, tmpFile.getPath(), true);
|
||||||
} catch (UnsatisfiedLinkError e) {
|
} catch (UnsatisfiedLinkError e) {
|
||||||
try {
|
try {
|
||||||
@ -218,6 +246,51 @@ public final class NativeLibraryLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to patch shaded library to ensure it uses a unique ID.
|
||||||
|
*/
|
||||||
|
private static void patchShadedLibraryId(byte[] bytes, String originalName, String name) {
|
||||||
|
// Our native libs always have the name as part of their id so we can search for it and replace it
|
||||||
|
// to make the ID unique if shading is used.
|
||||||
|
byte[] nameBytes = originalName.getBytes(CharsetUtil.UTF_8);
|
||||||
|
int idIdx = -1;
|
||||||
|
|
||||||
|
// Be aware this is a really raw way of patching a dylib but it does all we need without implementing
|
||||||
|
// a full mach-o parser and writer. Basically we just replace the the original bytes with some
|
||||||
|
// random bytes as part of the ID regeneration. The important thing here is that we need to use the same
|
||||||
|
// length to not corrupt the mach-o header.
|
||||||
|
outerLoop: for (int i = 0; i < bytes.length && bytes.length - i >= nameBytes.length; i++) {
|
||||||
|
int idx = i;
|
||||||
|
for (int j = 0; j < nameBytes.length;) {
|
||||||
|
if (bytes[idx++] != nameBytes[j++]) {
|
||||||
|
// Did not match the name, increase the index and try again.
|
||||||
|
break;
|
||||||
|
} else if (j == nameBytes.length) {
|
||||||
|
// We found the index within the id.
|
||||||
|
idIdx = i;
|
||||||
|
break outerLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idIdx == -1) {
|
||||||
|
logger.debug("Was not able to find the ID of the shaded native library {}, can't adjust it.", name);
|
||||||
|
} else {
|
||||||
|
// We found our ID... now monkey-patch it!
|
||||||
|
for (int i = 0; i < nameBytes.length; i++) {
|
||||||
|
// We should only use bytes as replacement that are in our UNIQUE_ID_BYTES array.
|
||||||
|
bytes[idIdx + i] = UNIQUE_ID_BYTES[PlatformDependent.threadLocalRandom()
|
||||||
|
.nextInt(UNIQUE_ID_BYTES.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(
|
||||||
|
"Found the ID of the shaded native library {}. Replacing ID part {} with {}",
|
||||||
|
name, originalName, new String(bytes, idIdx, nameBytes.length, CharsetUtil.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading the native library into the specified {@link ClassLoader}.
|
* Loading the native library into the specified {@link ClassLoader}.
|
||||||
* @param loader - The {@link ClassLoader} where the native library will be loaded into
|
* @param loader - The {@link ClassLoader} where the native library will be loaded into
|
||||||
|
@ -166,32 +166,10 @@
|
|||||||
</unzip>
|
</unzip>
|
||||||
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTransportLib}" />
|
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTransportLib}" />
|
||||||
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTransportLib}" />
|
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTransportLib}" />
|
||||||
<exec executable="install_name_tool" failonerror="true" dir="${project.build.directory}/" resolveexecutable="true">
|
|
||||||
<arg value="-id" />
|
|
||||||
<arg value="lib${shadingPrefix}_${nativeTransportLib}" />
|
|
||||||
<arg value="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTransportLib}" />
|
|
||||||
</exec>
|
|
||||||
<!-- We need to adjust the ID used on MacOS so we are sure the correct lib is loaded later on -->
|
|
||||||
<exec executable="install_name_tool" failonerror="true" dir="${project.build.directory}/" resolveexecutable="true">
|
|
||||||
<arg value="-id" />
|
|
||||||
<arg value="lib${shadingPrefix2}_${nativeTransportLib}" />
|
|
||||||
<arg value="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTransportLib}" />
|
|
||||||
</exec>
|
|
||||||
<delete file="${classesShadedNativeDir}/lib${nativeTransportLib}" />
|
<delete file="${classesShadedNativeDir}/lib${nativeTransportLib}" />
|
||||||
|
|
||||||
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTcnativeLib}" />
|
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTcnativeLib}" />
|
||||||
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTcnativeLib}" />
|
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTcnativeLib}" />
|
||||||
<exec executable="install_name_tool" failonerror="true" dir="${project.build.directory}/" resolveexecutable="true">
|
|
||||||
<arg value="-id" />
|
|
||||||
<arg value="lib${shadingPrefix}_${nativeTcnativeLib}" />
|
|
||||||
<arg value="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTcnativeLib}" />
|
|
||||||
</exec>
|
|
||||||
<!-- We need to adjust the ID used on MacOS so we are sure the correct lib is loaded later on -->
|
|
||||||
<exec executable="install_name_tool" failonerror="true" dir="${project.build.directory}/" resolveexecutable="true">
|
|
||||||
<arg value="-id" />
|
|
||||||
<arg value="lib${shadingPrefix2}_${nativeTcnativeLib}" />
|
|
||||||
<arg value="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTcnativeLib}" />
|
|
||||||
</exec>
|
|
||||||
<delete file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" />
|
<delete file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" />
|
||||||
|
|
||||||
<jar destfile="${project.build.directory}/${jarName}" basedir="${classesShadedDir}" />
|
<jar destfile="${project.build.directory}/${jarName}" basedir="${classesShadedDir}" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user