Skip to content

Commit eb00590

Browse files
authored
Fix a bug where Netty fails to load a shaded native library (netty#12358)
Related: netty/netty-jni-util#13 Motivation: Netty can't load a shaded native library because `netty-jni-util` has a bug that appends an extra `/` when attmpting JNI `FindClass`. For example, `parsePackagePrefix()` returns `shaded//` instead of `shaded/`, leading to a bad attempt to load `shaded//io/netty/...`. Netty also doesn't handle the case where a shaded package name contains an underscore (`_`), such as `my_shaded_deps.io.netty.*`, because it simply replaces `.` with `_` and vice versa. JNI specification defines a mangling rule to avoid this issue: - https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names Modifications: - Replace `_` into `_1` so that `parsePackagePrefix()` in `netty-jni-utils.c` can get the correct package name later. - Update the `docker-compose.yaml` so that the integration tests in `testsuite-shading` are always run as a part of CI, so we don't have a regression in the future. Result: - A user can use a functionality that requires a native library even if they shaded Netty *and* the shaded package name contains an underscore (`_`).
1 parent 8e8e9cc commit eb00590

File tree

4 files changed

+35
-14
lines changed

4 files changed

+35
-14
lines changed

common/src/main/java/io/netty/util/internal/NativeLibraryLoader.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,25 @@ public static void loadFirstAvailable(ClassLoader loader, String... names) {
117117
}
118118

119119
/**
120-
* The shading prefix added to this class's full name.
120+
* Calculates the mangled shading prefix added to this class's full name.
121+
*
122+
* <p>This method mangles the package name as follows, so we can unmangle it back later:
123+
* <ul>
124+
* <li>{@code _} to {@code _1}</li>
125+
* <li>{@code .} to {@code _}</li>
126+
* </ul>
127+
*
128+
* <p>Note that we don't mangle non-ASCII characters here because it's extremely unlikely to have
129+
* a non-ASCII character in a package name. For more information, see:
130+
* <ul>
131+
* <li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html">JNI
132+
* specification</a></li>
133+
* <li>{@code parsePackagePrefix()} in {@code netty_jni_util.c}.</li>
134+
* </ul>
121135
*
122136
* @throws UnsatisfiedLinkError if the shader used something other than a prefix
123137
*/
124-
private static String calculatePackagePrefix() {
138+
private static String calculateMangledPackagePrefix() {
125139
String maybeShaded = NativeLibraryLoader.class.getName();
126140
// Use ! instead of . to avoid shading utilities from modifying the string
127141
String expected = "io!netty!util!internal!NativeLibraryLoader".replace('!', '.');
@@ -130,16 +144,17 @@ private static String calculatePackagePrefix() {
130144
"Could not find prefix added to %s to get %s. When shading, only adding a "
131145
+ "package prefix is supported", expected, maybeShaded));
132146
}
133-
return maybeShaded.substring(0, maybeShaded.length() - expected.length());
147+
return maybeShaded.substring(0, maybeShaded.length() - expected.length())
148+
.replace("_", "_1")
149+
.replace('.', '_');
134150
}
135151

136152
/**
137153
* Load the given library with the specified {@link ClassLoader}
138154
*/
139155
public static void load(String originalName, ClassLoader loader) {
140-
// Adjust expected name to support shading of native libraries.
141-
String packagePrefix = calculatePackagePrefix().replace('.', '_');
142-
String name = packagePrefix + originalName;
156+
String mangledPackagePrefix = calculateMangledPackagePrefix();
157+
String name = mangledPackagePrefix + originalName;
143158
List<Throwable> suppressed = new ArrayList<Throwable>();
144159
try {
145160
// first try to load from java.library.path
@@ -189,7 +204,7 @@ public static void load(String originalName, ClassLoader loader) {
189204
}
190205
out.flush();
191206

192-
if (shouldShadedLibraryIdBePatched(packagePrefix)) {
207+
if (shouldShadedLibraryIdBePatched(mangledPackagePrefix)) {
193208
// Let's try to patch the id and re-sign it. This is a best-effort and might fail if a
194209
// SecurityManager is setup or the right executables are not installed :/
195210
tryPatchShadedLibraryIdAndSign(tmpFile, originalName);

docker/docker-compose.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ services:
2929

3030
build:
3131
<<: *common
32-
command: /bin/bash -cl "./mvnw -B -ntp clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.classifier=linux-x86_64-fedora"
32+
command: '/bin/bash -cl "
33+
./mvnw -B -ntp clean install -Dio.netty.testsuite.badHost=netty.io -Dtcnative.classifier=linux-x86_64-fedora &&
34+
cd testsuite-shading &&
35+
../mvnw -B -ntp integration-test failsafe:verify -Dtcnative.classifier=linux-x86_64-fedora
36+
"'
3337

3438
deploy:
3539
<<: *common

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@
621621
<dependency>
622622
<groupId>io.netty</groupId>
623623
<artifactId>netty-jni-util</artifactId>
624-
<version>0.0.5.Final</version>
624+
<version>0.0.6.Final</version>
625625
<classifier>sources</classifier>
626626
<optional>true</optional>
627627
</dependency>

testsuite-shading/pom.xml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
<classesShadedDir>${project.build.directory}/classes-shaded</classesShadedDir>
3535
<classesShadedNativeDir>${classesShadedDir}/META-INF/native</classesShadedNativeDir>
3636
<shadingPrefix>shaded</shadingPrefix>
37-
<shadingPrefix2>shaded2</shadingPrefix2>
37+
<shadingPrefix2>shaded_2</shadingPrefix2>
38+
<mangledShadingPrefix>shaded</mangledShadingPrefix>
39+
<mangledShadingPrefix2>shaded_12</mangledShadingPrefix2>
3840
<skipShadingTestsuite>true</skipShadingTestsuite>
3941
<shadedPackagePrefix>io.netty.</shadedPackagePrefix>
4042
<japicmp.skip>true</japicmp.skip>
@@ -126,12 +128,12 @@
126128
<include name="shaded2.jar" />
127129
</fileset>
128130
</unzip>
129-
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTransportLib}" />
130-
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTransportLib}" />
131+
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${mangledShadingPrefix}_${nativeTransportLib}" />
132+
<copy file="${classesShadedNativeDir}/lib${nativeTransportLib}" tofile="${classesShadedNativeDir}/lib${mangledShadingPrefix2}_${nativeTransportLib}" />
131133
<delete file="${classesShadedNativeDir}/lib${nativeTransportLib}" />
132134

133-
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix}_${nativeTcnativeLib}" />
134-
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${shadingPrefix2}_${nativeTcnativeLib}" />
135+
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${mangledShadingPrefix}_${nativeTcnativeLib}" />
136+
<copy file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" tofile="${classesShadedNativeDir}/lib${mangledShadingPrefix2}_${nativeTcnativeLib}" />
135137
<delete file="${classesShadedNativeDir}/lib${nativeTcnativeLib}" />
136138

137139
<delete file="${project.build.directory}/shaded1.jar" />

0 commit comments

Comments
 (0)