我一直在尝试使用 Java 代理通过 ASM 应用字节码转换。

我使用 premain 方法实现了一个 Agent,向 Instrumentation 添加了一个转换器。
我在 .jar list 中添加了“Premain-Class”行

Premain-Class: <MyAgentPath>

然后我尝试使用代理运行该应用程序。

我有一个问题:我的转换器修改了一些方法调用,所以如果不是所有涉及的类都被修改了,它就无法工作。还有一些没有被修改的类,比如“org.apache.commons.math3.util.FastMath”。
当然,我得到了错误:
java.lang.NoSuchMethodError: org.apache.commons.math3.util.FastMath.floor<new_descriptor>

我检查了很多帖子,说它可能是引导加载程序,它不知道此类的路径,因此我尝试使用不同的方式添加它:
  • 将“Boot-Class-Path”行添加到 list 中:
    Boot-Class-Path: <...>/commons-math3<...>.jar
    
  • 使用方法“appendToBootstrapClassLoaderSearch(JarFile)”
    inst.appendToBootstrapClassLoaderSearch("<...>/commons-math3<...>.jar");
    
  • 使用 JVM 参数“-Xbootclasspath/a:”
    -Xbootclasspath/a:<...>/commons-math3<...>.jar
    

  • 这一切都没有改变任何事情。

    我还使用了 Instrumentation 类方法 getAllLoadedClasses() 来查看加载了哪些类,以及加载了应用程序进程中涉及的所有类,包括 FastMath。
    for(Class<?> clazz : MyAgent.getInstInstance().getAllLoadedClasses()){
        buffWrite.write(clazz.getName());
    

    由于“FastMath”类给出了一个错误,并且 Bootstrap 加载器应该有它的路径,我尝试向同一包中其他类的方法添加一些方法调用。似乎问题并未出现在包的每个类中。

    例如: MathUtils 被转换并调用修改后的方法 checkFinite(D)V -> checkFinite<new_descriptor>

    所以我猜这个问题与提供给引导加载程序的路径无关。

    如果您对正在发生的事情有一些想法,我会很高兴听到的!

    最佳答案

    NoSuchMethodError 很可能不是由未向引导类加载器添加内容引起的。这可能是一个问题的唯一机会是,如果突然有两个这样的 jar 可用,一个装有仪器,另一个没有。

    如果您将方法 checkFinite(D)V 调用更改为另一个方法 checkFinite<new_descriptor> ,那么您需要确保使用 FastMath.floor 的任何类都将描述符更新为该方法。这意味着您需要遍历每个类的每个方法以查找 ASM 的 visitMethodIns 调用。好像你错过了一些。由于您正在更改 FastMath 类的布局,因此 必须在第一次加载时对其进行 检测,并且 不能重新定义它

    Java 内部类不知道 FastMath,因为这是第三方依赖项。因此,应该可以检测来自您的代理的任何调用。在我看来,您正在过早地加载 FastMath

    关于Java 代理 : transform() not applied on all classes,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34285862/

    10-10 16:05