-
-
Notifications
You must be signed in to change notification settings - Fork 808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proper way to repackage ByteBuddy into the library #670
Comments
Well, this one is a real PITA. Technically, this has nothing to do with Byte Buddy, but with binding native libraries that do not respect Java's class loader concept but rather load a VM global state. Tools.jar is one of those. Byte Buddy cannot know if it is already loaded on another class loader. This class loader might be isolated and to discover it, it would already need an agent present. Thus, Byte Buddy has a mechanism to inject a holder class into a well-know class loader (the system loader) once attached to avoid this conflict. Using this class, previous attachments can be discovered. But if Byte Buddy is shaded, this won't work out as the well-known name is not known anymore. The general advice is therefore not to repackage. I try to keep Byte Buddy API compatible for not only that reason. But of course, the version conflict remains. Maybe I should make sure Byte Buddy always puts the holder class into the net.bytebuddy namespace by obfuscating its name to shading tools. I will explore that option. |
Can you provide us with instructions for shading, maybe, as to what classes shall not be shaded for this "sharing" to work? |
It's classes in the net.bytebuddy.agent package, I think (without subpackages). Those are fairly stable, too. I'd have to experiment myself, to be honest before I can promise that this will work but it should be a big step in the right direction. I am however confident that I can fix this in the library eventually by obfuscating the net.bytebuddy namespace eventually when those dynamic injections are made. ASM just does a startswith check in their remapper of the constant pool that every shading tool uses, it's not hard to fool. |
If it helps: sigar for intance uses a system property to say the native is already loaded and skip it for later calls, can be a trivial way to share it accross relocations for BB too. |
Unfortunately, Byte Buddy cannot control the native code bits of tools.jar. |
@raphw it can suround its usage, will not prevent another lib to use it and fall into that bucket but enables to solve that ticket issue ;). |
Yes, but to use the attach API, Byte Buddy needs to load tools.jar which automatically triggers loading the native library. I think using a fixed name that survives autmatic repackaging. I'm on vacation atm, but I'm sure I'll find a solution. |
FYI: I extended Byte Buddy agent such that it is now possible to attach to a VM without using the JDK-specific tools.jar. I simply reimplemented the attach API for OpenJ9/HotSpot on Solaris/Windows/POSIX which is now a part of Byte Buddy. All you need to do is to include JNA for this. Note however that JNA is not shadable due to its use of JNI. However, this mechanism is not vulnerable to being loaded by multiple class loaders. Maybe instead of shading you can isolate Byte Buddy agent and JNA in a seperate class loader and work around that way? Due to the possibility of using Byte Buddy agent in a premain state, it is not trivial to work around the shading, unfortunately but I will investigate further. As a bonus, with the JNA-based solution, you can self-attach on non-JDK VMs if this is relevant to you. |
Thanks. JNA use is indeed safer. If we include it as a "plain" dependency, then there's still a risk of a version conflict on JNA. However, if you use "separate classloader" trick, then it indeed works -- JNA can be safely loaded from multiple classloaders 🎉 (I have not explicitly checked if it holds for the case of pre-main, but I don't see how it should be different). Interestly, though, behind the scenes two JNA instances share one copy of the loaded native library, but create different proxies to it. |
Minor current issue: There seems to be a bug in the attach emulation for MacOS where the temporary directory is user-dependent. I do not own a Mac but someone else is looking into it. |
Did you look into using JNA and class loader isolation? |
Yeah, the issue with non-standard tmpdir on MacOS is still present. A simple project that shows this: https://github.com/dkhalanskyjb/byteBuddyMacBug Are there any new details? |
We are using |
I'd still like to fix the repackaging for older JVMs where JNA is not an option but yes, the JNA approach does not suffer these problems. |
Hi there! The problem that I'm facing is that byte-buddy inside kotlinx-coroutines-debug is v1.10.9. My instrumentation agent and application itself compiled with java 17 but
My suspicion is that ByteBuddy v1.10.9 doesn't know anything about java 17 so noting could be injected. On local environment without |
If you are running an agent, it is a good strategy to use a trampoline agent. The trampoline agent basically just creates a class loader which has the boot loader as its parent and adds the actual jar to it. It then invokes the actual agent's premain method using reflection. |
ATM I was using dynamic loading to inject agent in runtime without intervention into classpath on a startup. Is this approach is pretty limited and only can get this far? |
Not sure I understand the question. What do you mean by "dynamic loading"? |
Attaching agent to a running JVM
|
Some context:
We at kotlinx.coroutines have a special "debug" library that uses ByteByddy to redefine some classes for the sake of better user experience.
Our API shape has two modes:
So to make it as simple as
java -jar myapp.jar -javaagent:kotlinx-coroutines-debug.jar
we'll have to shade ByteBuddy into our library. But then we can mess up with other libraries that use ByteBuddy, especially in case of incompatible versions. To avoid that, we not only shade but also repackage ByteBuddy.It works fine most of the time but fails with
java.lang.UnsatisfiedLinkError: Native Library /.../jre/lib/amd64/libattach.so already loaded in another classloader
when the target application uses libraries that also use ByteBuddy (e.g. Spring or Mockito).So the question is, what is the best way to have a self-contained JAR suitable for attach and that also works with other libraries that use ByteBuddy?
The text was updated successfully, but these errors were encountered: