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();
|
||||
out = new FileOutputStream(tmpFile);
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
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);
|
||||
if (shouldShadedLibraryIdBePatched(packagePrefix)) {
|
||||
patchShadedLibraryId(in, out, originalName, name);
|
||||
} else {
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
while ((length = in.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, length);
|
||||
}
|
||||
}
|
||||
|
||||
out.flush();
|
||||
|
||||
// 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.
|
||||
*/
|
||||
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
|
||||
// to make the ID unique if shading is used.
|
||||
byte[] nameBytes = originalName.getBytes(CharsetUtil.UTF_8);
|
||||
@ -279,6 +307,7 @@ public final class NativeLibraryLoader {
|
||||
|
||||
if (idIdx == -1) {
|
||||
logger.debug("Was not able to find the ID of the shaded native library {}, can't adjust it.", name);
|
||||
return false;
|
||||
} else {
|
||||
// We found our ID... now monkey-patch it!
|
||||
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 {}",
|
||||
name, originalName, new String(bytes, idIdx, nameBytes.length, CharsetUtil.UTF_8));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,11 +15,19 @@
|
||||
*/
|
||||
package io.netty.util.internal;
|
||||
|
||||
import io.netty.util.CharsetUtil;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
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.fail;
|
||||
|
||||
@ -61,4 +69,70 @@ public class NativeLibraryLoaderTest {
|
||||
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