diff --git a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java b/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java index bc55533ac3..7d381e1835 100644 --- a/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java +++ b/common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java @@ -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; } } diff --git a/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java b/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java index 592b4f1723..17381985dc 100644 --- a/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java +++ b/common/src/test/java/io/netty/util/internal/NativeLibraryLoaderTest.java @@ -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(); + } + } }