Correctly monkey-patch id also in whe os / arch is used within library name. (#8913)
Motivation:
2bb9f64e16
introduced a change which made it possible to use different shaded versions of netty-tcnative on the classpath. This only partly worked as we did not correctly handled the case when os / arch is part of the library name (which is the case when netty-tcnative-boringssl-static is used with the uber jar).
Modifications:
- If patching the ID failed we retry again with the os / arch stripped
- Add unit tests to verify that patching ID now works with and without os / arch as suffix.
Result:
Using multiple shaded version of netty-tcnative-boringssl-static on MacOS works.
This commit is contained in:
parent
c6b372f517
commit
6c4d6ae332
@ -185,28 +185,16 @@ public final class NativeLibraryLoader {
|
|||||||
in = url.openStream();
|
in = url.openStream();
|
||||||
out = new FileOutputStream(tmpFile);
|
out = new FileOutputStream(tmpFile);
|
||||||
|
|
||||||
|
if (shouldShadedLibraryIdBePatched(packagePrefix)) {
|
||||||
|
patchShadedLibraryId(in, out, originalName, name);
|
||||||
|
} else {
|
||||||
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,
|
||||||
@ -250,10 +238,50 @@ public final class NativeLibraryLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Package-private for testing.
|
||||||
|
static boolean patchShadedLibraryId(InputStream in, OutputStream out, String originalName, String name)
|
||||||
|
throws IOException {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int length;
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
final boolean patched;
|
||||||
|
// Try to patch the library id.
|
||||||
|
if (!patchShadedLibraryId(bytes, originalName, name)) {
|
||||||
|
// We did not find the Id, check if we used a originalName that has the os and arch as suffix.
|
||||||
|
// If this is the case we should also try to patch with the os and arch suffix removed.
|
||||||
|
String os = PlatformDependent.normalizedOs();
|
||||||
|
String arch = PlatformDependent.normalizedArch();
|
||||||
|
String osArch = "_" + os + "_" + arch;
|
||||||
|
if (originalName.endsWith(osArch)) {
|
||||||
|
patched = patchShadedLibraryId(bytes,
|
||||||
|
originalName.substring(0, originalName.length() - osArch.length()), name);
|
||||||
|
} else {
|
||||||
|
patched = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
patched = true;
|
||||||
|
}
|
||||||
|
out.write(bytes, 0, bytes.length);
|
||||||
|
return patched;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean shouldShadedLibraryIdBePatched(String packagePrefix) {
|
||||||
|
return TRY_TO_PATCH_SHADED_ID && PlatformDependent.isOsx() && !packagePrefix.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to patch shaded library to ensure it uses a unique ID.
|
* Try to patch shaded library to ensure it uses a unique ID.
|
||||||
*/
|
*/
|
||||||
private static void patchShadedLibraryId(byte[] bytes, String originalName, String name) {
|
private static boolean 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
|
// 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.
|
// to make the ID unique if shading is used.
|
||||||
byte[] nameBytes = originalName.getBytes(CharsetUtil.UTF_8);
|
byte[] nameBytes = originalName.getBytes(CharsetUtil.UTF_8);
|
||||||
@ -279,6 +307,7 @@ public final class NativeLibraryLoader {
|
|||||||
|
|
||||||
if (idIdx == -1) {
|
if (idIdx == -1) {
|
||||||
logger.debug("Was not able to find the ID of the shaded native library {}, can't adjust it.", name);
|
logger.debug("Was not able to find the ID of the shaded native library {}, can't adjust it.", name);
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// We found our ID... now monkey-patch it!
|
// We found our ID... now monkey-patch it!
|
||||||
for (int i = 0; i < nameBytes.length; i++) {
|
for (int i = 0; i < nameBytes.length; i++) {
|
||||||
@ -291,6 +320,7 @@ public final class NativeLibraryLoader {
|
|||||||
"Found the ID of the shaded native library {}. Replacing ID part {} with {}",
|
"Found the ID of the shaded native library {}. Replacing ID part {} with {}",
|
||||||
name, originalName, new String(bytes, idIdx, nameBytes.length, CharsetUtil.UTF_8));
|
name, originalName, new String(bytes, idIdx, nameBytes.length, CharsetUtil.UTF_8));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,11 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.util.internal;
|
package io.netty.util.internal;
|
||||||
|
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@ -61,4 +69,70 @@ public class NativeLibraryLoaderTest {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchingId() throws IOException {
|
||||||
|
testPatchingId0(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchingIdWithOsArch() throws IOException {
|
||||||
|
testPatchingId0(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchingIdNotMatch() throws IOException {
|
||||||
|
testPatchingId0(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchingIdWithOsArchNotMatch() throws IOException {
|
||||||
|
testPatchingId0(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testPatchingId0(boolean match, boolean withOsArch) throws IOException {
|
||||||
|
byte[] bytes = new byte[1024];
|
||||||
|
PlatformDependent.threadLocalRandom().nextBytes(bytes);
|
||||||
|
byte[] idBytes = ("/workspace/netty-tcnative/boringssl-static/target/" +
|
||||||
|
"native-build/target/lib/libnetty_tcnative-2.0.20.Final.jnilib").getBytes(CharsetUtil.UTF_8);
|
||||||
|
|
||||||
|
String originalName;
|
||||||
|
if (match) {
|
||||||
|
originalName = "netty-tcnative";
|
||||||
|
} else {
|
||||||
|
originalName = "nonexist_tcnative";
|
||||||
|
}
|
||||||
|
String name = "shaded_" + originalName;
|
||||||
|
if (withOsArch) {
|
||||||
|
name += "_osx_x86_64";
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(bytes, 0, bytes.length);
|
||||||
|
out.write(idBytes, 0, idBytes.length);
|
||||||
|
out.write(bytes, 0 , bytes.length);
|
||||||
|
|
||||||
|
out.flush();
|
||||||
|
byte[] inBytes = out.toByteArray();
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
InputStream inputStream = new ByteArrayInputStream(inBytes);
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
assertEquals(match,
|
||||||
|
NativeLibraryLoader.patchShadedLibraryId(inputStream, outputStream, originalName, name));
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
byte[] outputBytes = outputStream.toByteArray();
|
||||||
|
assertArrayEquals(bytes, Arrays.copyOfRange(outputBytes, 0, bytes.length));
|
||||||
|
byte[] patchedId = Arrays.copyOfRange(outputBytes, bytes.length, bytes.length + idBytes.length);
|
||||||
|
assertEquals(!match, Arrays.equals(idBytes, patchedId));
|
||||||
|
assertArrayEquals(bytes,
|
||||||
|
Arrays.copyOfRange(outputBytes, bytes.length + idBytes.length, outputBytes.length));
|
||||||
|
assertEquals(inBytes.length, outputBytes.length);
|
||||||
|
} finally {
|
||||||
|
inputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user